Merge branch 'master' of git.mgm.sipwise.com:ngcp-panel

agranig/subprof
Irina Peshinskaya 11 years ago
commit bd7d18c49b

@ -157,6 +157,7 @@ Build.PL - NGCP-Panel build system including test fixtures
--schema-base-dir directory of NGCP::Schema if its not yet installed
--mysqld-port port where the mysqld should be started
--mysql-dump one or more mysql dumps to be imported to our mysqld
--no-junit don't output junit but normal TAP, for manual testing
--help brief help message
--man full documentation

4
debian/control vendored

@ -72,7 +72,8 @@ Replaces: ngcp-panel-common (<= 1.0.15),
ngcp-panel-nginx (<= 1.0.15)
Breaks: ngcp-panel-common (<= 1.0.15),
ngcp-panel-nginx (<= 1.0.15)
Depends: gnutls-bin,
Depends: gettext,
gnutls-bin,
libautodie-perl (>= 2.21~),
libcatalyst-actionrole-acl-perl,
libcatalyst-actionrole-checktrailingslash-perl,
@ -112,6 +113,7 @@ Depends: gnutls-bin,
libjson-multivalueordered-perl,
libjson-pointer-perl,
libjson-types-perl,
liblocale-maketext-lexicon-perl,
liblog-log4perl-perl,
libmodule-runtime-perl,
libmoose-perl,

@ -3,3 +3,4 @@ script/ngcp_panel_fastcgi.pl usr/share/ngcp-panel/
ngcp_panel.conf etc/ngcp-panel/
share/* usr/share/ngcp-panel/
lib/NGCP/Panel/I18N/* usr/share/perl5/NGCP/Panel/I18N
script/* usr/share/ngcp-panel/script

@ -1,13 +0,0 @@
lib/NGCP/Panel/Role
lib/NGCP/Panel/Field
lib/NGCP/Panel/AuthenticationStore
lib/NGCP/Panel/Form
lib/NGCP/Panel/Render
lib/NGCP/Panel/Controller
lib/NGCP/Panel/Model
lib/NGCP/Panel/Utils
lib/NGCP/Panel/Widget
lib/NGCP/Panel/View
lib/NGCP/Panel/Cache
share/templates
share/layout

@ -5,6 +5,7 @@ use Child qw(child);
use Capture::Tiny qw(capture);
use TryCatch;
use MooseX::Method::Signatures;
use LWP::UserAgent;
extends 'Module::Build';
our ($plackup, $webdriver, @cover_opt, $mysqld);
@ -40,7 +41,7 @@ sub _test_preconditions {
require Getopt::Long;
my %opt = (server => 'http://localhost:5000');
Getopt::Long::GetOptions(\%opt, 'webdriver=s', 'server:s', 'help|?', 'man', 'wd-server=s', 'schema-base-dir=s', 'mysqld-port=s', 'mysql-dump=s@')
Getopt::Long::GetOptions(\%opt, 'webdriver=s', 'server:s', 'help|?', 'man', 'wd-server=s', 'schema-base-dir=s', 'mysqld-port=s', 'mysql-dump=s@', 'no-junit')
or die 'could not process command-line options';
require Pod::Usage;
@ -48,6 +49,11 @@ sub _test_preconditions {
Pod::Usage::pod2usage(-exitval => 0, -input => 'Build.PL', -verbose => 2) if $opt{man};
Pod::Usage::pod2usage("$0: --webdriver option required.\nRun `perldoc Build.PL`") unless $opt{webdriver};
if ($opt{'no-junit'}) {
delete $self->tap_harness_args->{formatter_class};
$self->tap_harness_args->{verbosity} = 1;
}
if ($opt{'wd-server'}) {
my ($wd_host, $wd_port) = $opt{'wd-server'} =~ m{([^/:]+):([0-9]+)};
$ENV{TWD_HOST} = $wd_host;
@ -105,6 +111,26 @@ sub _test_preconditions {
$ENV{CATALYST_SERVER} = $opt{server};
}
sub _download_certs {
my ($self) = @_;
my $uri = $ENV{CATALYST_SERVER};
use File::Temp qw/tempfile/;
my ($ua, $req, $res);
$ua = LWP::UserAgent->new(cookie_jar => {}, ssl_opts => {verify_hostname => 0});
$res = $ua->post($uri.'/login/admin', {username => 'administrator', password => 'administrator'}, 'Referer' => $uri.'/login/admin');
$res = $ua->get($uri.'/dashboard/');
$res = $ua->get($uri.'/administrator/1/api_key');
if ($res->decoded_content =~ m/gen\.generate/) { # key need to be generated first
$res = $ua->post($uri.'/administrator/1/api_key', {'gen.generate' => 'foo'}, 'Referer' => $uri.'/dashboard');
}
my (undef, $tmp_apiclient_filename) = tempfile;
my (undef, $tmp_apica_filename) = tempfile;
$res = $ua->post($uri.'/administrator/1/api_key', {'pem.download' => 'foo'}, 'Referer' => $uri.'/dashboard', ':content_file' => $tmp_apiclient_filename);
$res = $ua->post($uri.'/administrator/1/api_key', {'ca.download' => 'foo'}, 'Referer' => $uri.'/dashboard', ':content_file' => $tmp_apica_filename);
$ENV{API_SSL_CLIENT_CERT} = $tmp_apiclient_filename;
$ENV{API_SSL_CA_CERT} = $tmp_apica_filename;
}
around('ACTION_test', sub {
my $super = shift;
my $self = shift;
@ -163,6 +189,15 @@ method ACTION_test_selenium {
$self->generic_test(type => 'default');
}
method ACTION_test_api {
$self->depends_on('code');
$self->_test_preconditions;
$self->_download_certs;
$self->test_files('t/api-*.t');
$self->generic_test(type => 'default');
unlink ($ENV{API_SSL_CLIENT_CERT}, $ENV{API_SSL_CA_CERT}); # created by _download_certs()
}
method ACTION_readme {
require Pod::Readme;
my $parser = Pod::Readme->new();

@ -23,12 +23,51 @@ class_has 'api_description' => (
'Defines a collection of <a href="#rewriterules">Rewrite Rules</a>.',
);
with 'NGCP::Panel::Role::API';
class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
{
param => 'reseller_id',
description => 'Filter for rewriterulesets belonging to a specific reseller',
query => {
first => sub {
my $q = shift;
{ reseller_id => $q };
},
second => sub {},
},
},
{
param => 'description',
description => 'Filter rulesets for a certain description (wildcards possible).',
query => {
first => sub {
my $q = shift;
return { description => { like => $q } };
},
second => sub {},
},
},
{
param => 'name',
description => 'Filter rulesets for a certain name (wildcards possible).',
query => {
first => sub {
my $q = shift;
return { name => { like => $q } };
},
second => sub {},
},
},
]},
);
with 'NGCP::Panel::Role::API::RewriteRuleSets';
class_has('resource_name', is => 'ro', default => 'rewrite');
class_has('dispatch_path', is => 'ro', default => '/api/rewrite/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-rewrite');
class_has('resource_name', is => 'ro', default => 'rewriterulesets');
class_has('dispatch_path', is => 'ro', default => '/api/rewriterulesets/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-rewriterulesets');
__PACKAGE__->config(
action => {
@ -65,7 +104,7 @@ sub GET :Allow {
});
my (@embedded, @links);
for my $set ($rwr_set->search({}, {order_by => {-asc => 'me.id'}})->all) {
push @embedded, $self->hal_from_item($c, $set, "rewrite");
push @embedded, $self->hal_from_item($c, $set, "rewriterulesets");
push @links, Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
href => sprintf('%s%d', $self->dispatch_path, $set->id),
@ -122,6 +161,91 @@ sub OPTIONS :Allow {
return;
}
sub POST :Allow {
my ($self, $c) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $schema = $c->model('DB');
my $resource = $self->get_valid_post_data(
c => $c,
media_type => 'application/json',
);
last unless $resource;
unless(defined $resource->{reseller_id}) {
try {
$resource->{reseller_id} = $c->user->contract->contact->reseller_id;
}
}
my $reseller = $c->model('DB')->resultset('resellers')->find($resource->{reseller_id});
unless($reseller) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'reseller_id', doesn't exist.");
last;
}
my $rewriterules = $resource->{rewriterules};
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
);
my $ruleset_test = $schema->resultset('voip_rewrite_rule_sets')->search_rs({
name => $resource->{name}
})->first;
if ($ruleset_test) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Ruleset with this 'name' already exists.");
last;
}
my $ruleset;
try {
$ruleset = $schema->resultset('voip_rewrite_rule_sets')->create($resource);
} catch($e) {
$c->log->error("failed to create rewriteruleset: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create rewriteruleset.");
last;
}
if ($rewriterules) {
my $i = 30;
if (ref($rewriterules) ne "ARRAY") {
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "rewriterules must be an array.");
}
for my $rule (@{ $rewriterules }) {
use DDP; p $rule;
my $rule_form = $self->get_form($c, "rules");
last unless $self->validate_form(
c => $c,
resource => $rule,
form => $rule_form,
);
try {
$ruleset->voip_rewrite_rules->create({
%{ $rule },
priority => $i++,
});
} catch($e) {
$c->log->error("failed to create rewriterules: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create rewrite rules.");
last;
}
}
}
$guard->commit;
$c->response->status(HTTP_CREATED);
$c->response->header(Location => sprintf('/%s%d', $c->request->path, $ruleset->id));
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;

@ -16,12 +16,11 @@ require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::RewriteRuleSets';
class_has('resource_name', is => 'ro', default => 'rewrite');
class_has('dispatch_path', is => 'ro', default => '/api/rewrite/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-rewrite');
class_has('resource_name', is => 'ro', default => 'rewriterulesets');
class_has('dispatch_path', is => 'ro', default => '/api/rewriterulesets/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-rewriterulesets');
__PACKAGE__->config(
action => {
@ -52,7 +51,7 @@ sub GET :Allow {
my $ruleset = $self->item_by_id($c, $id, "rulesets");
last unless $self->resource_exists($c, ruleset => $ruleset);
my $hal = $self->hal_from_item($c, $ruleset, "rewrite");
my $hal = $self->hal_from_item($c, $ruleset, "rewriterulesets");
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
(map { # XXX Data::HAL must be able to generate links with multiple relations
@ -103,7 +102,7 @@ sub PATCH :Allow {
my $ruleset = $self->item_by_id($c, $id, "rulesets");
last unless $self->resource_exists($c, ruleset => $ruleset);
my $old_resource = { $ruleset->get_inflated_columns };
my $old_resource = $self->hal_from_item($c, $ruleset, "rewriterulesets")->resource;
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
@ -118,7 +117,7 @@ sub PATCH :Allow {
$c->response->header(Preference_Applied => 'return=minimal');
$c->response->body(q());
} else {
my $hal = $self->hal_from_item($c, $ruleset, "rulesets");
my $hal = $self->hal_from_item($c, $ruleset, "rewriterulesets");
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
@ -158,7 +157,7 @@ sub PUT :Allow {
$c->response->header(Preference_Applied => 'return=minimal');
$c->response->body(q());
} else {
my $hal = $self->hal_from_item($c, $ruleset, "rulesets");
my $hal = $self->hal_from_item($c, $ruleset, "rewriterulesets");
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
@ -170,6 +169,28 @@ sub PUT :Allow {
return;
}
sub DELETE :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $ruleset = $self->item_by_id($c, $id, "rulesets");
last unless $self->resource_exists($c, ruleset => $ruleset);
try {
$ruleset->voip_rewrite_rules->delete;
$ruleset->delete;
} catch($e) {
$c->log->error("Failed to delete rewriteruleset with id '$id': $e");
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error");
last;
}
$guard->commit;
$c->response->status(HTTP_NO_CONTENT);
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;

@ -21,13 +21,41 @@ class_has 'api_description' => (
'Defines a set of Rewrite Rules which are grouped in <a href="#rewriterulesets">Rewrite Rule Sets</a>. They can be used to alter incoming and outgoing numbers.',
);
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::RewriteRules';
class_has('resource_name', is => 'ro', default => 'rewriterules');
class_has('dispatch_path', is => 'ro', default => '/api/rewriterules/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-rewriterules');
class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
{
param => 'description',
description => 'Filter rules for a certain description (wildcards possible).',
query => {
first => sub {
my $q = shift;
return { description => { like => $q } };
},
second => sub {},
},
},
{
param => 'set_id',
description => 'Filter for rules belonging to a specific rewriteruleset.',
query => {
first => sub {
my $q = shift;
return { set_id => $q };
},
second => sub {},
},
},
]},
);
__PACKAGE__->config(
action => {
map { $_ => {
@ -57,12 +85,6 @@ sub GET :Allow {
{
my $rules = $self->item_rs($c, "rules");
if($c->request->query_parameters->{set_id}) { #TODO: naming? document?
$rules = $rules->search({
set_id => $c->request->query_parameters->{set_id},
});
}
my $total_count = int($rules->count);
$rules = $rules->search(undef, {
page => $page,
@ -127,6 +149,56 @@ sub OPTIONS :Allow {
return;
}
sub POST :Allow {
my ($self, $c) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $schema = $c->model('DB');
my $resource = $self->get_valid_post_data(
c => $c,
media_type => 'application/json',
);
last unless $resource;
my $set_id = delete $resource->{set_id}; # keep this, cause formhandler doesn't know it
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
);
my $rule;
unless(defined $set_id) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Required: 'set_id'");
last;
}
my $ruleset = $schema->resultset('voip_rewrite_rule_sets')->find($set_id);
unless($ruleset) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'set_id'.");
last;
}
$resource->{set_id} = $ruleset->id;
try {
$rule = $schema->resultset('voip_rewrite_rules')->create($resource);
} catch($e) {
$c->log->error("failed to create rewriterule: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create rewriterule.");
last;
}
$guard->commit;
$c->response->status(HTTP_CREATED);
$c->response->header(Location => sprintf('/%s%d', $c->request->path, $rule->id));
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;

@ -16,7 +16,6 @@ require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
with 'NGCP::Panel::Role::API';
with 'NGCP::Panel::Role::API::RewriteRules';
class_has('resource_name', is => 'ro', default => 'rewriterules');
@ -170,6 +169,27 @@ sub PUT :Allow {
return;
}
sub DELETE :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $rule = $self->item_by_id($c, $id, "rules");
last unless $self->resource_exists($c, rule => $rule);
try {
$rule->delete;
} catch($e) {
$c->log->error("Failed to delete rewriterule with id '$id': $e");
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error");
last;
}
$guard->commit;
$c->response->status(HTTP_NO_CONTENT);
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;

@ -21,17 +21,13 @@ sub root :Chained('/') :PathPart('callflow') :CaptureArgs(0) {
my ( $self, $c ) = @_;
$c->stash->{capture_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
{ name => "timestamp", search => 0, title => $c->loc('Timestamp'), literal_sql => "min(timestamp)" },
{ name => "min_timestamp", search => 0, title => $c->loc('Timestamp') },
{ name => "call_id", search => 1, title => $c->loc('Call-ID') },
{ name => "caller_uuid", search => 1, title => $c->loc('Caller UUID') },
{ name => "callee_uuid", search => 1, title => $c->loc('Callee UUID') },
{ name => "cseq_method", search => 1, title => $c->loc('Method') },
]);
$c->stash->{calls_rs} = $c->model('DB')->resultset('messages')->search(undef, {
order_by => { -asc => 'timestamp' },
});
}
sub index :Chained('root') :PathPart('') :Args(0) {
@ -45,12 +41,24 @@ sub index :Chained('root') :PathPart('') :Args(0) {
sub ajax :Chained('root') :PathPart('ajax') :Args(0) {
my ( $self, $c ) = @_;
my $calls_rs = $c->stash->{calls_rs}->search_rs(undef,{
group_by => 'call_id'
my $calls_rs_cb = sub {
my %params = @_;
my $total_count = $c->model('DB')->resultset('messages')->search(undef,{group_by => 'call_id'})->count;
my $base_rs = $c->model('DB')->resultset('messages_custom');
my $searchstring = $params{searchstring} ? $params{searchstring}.'%' : '';
my @bind_vals = (($searchstring) x 3, $params{offset}, $params{rows});
my $new_rs = $base_rs->search(undef,{
bind => \@bind_vals,
});
return ($new_rs, $total_count, $total_count);
};
NGCP::Panel::Utils::Datatables::process(
$c,
$calls_rs,
$calls_rs_cb,
$c->stash->{capture_dt_columns},
);
$c->detach( $c->view("JSON") );

@ -972,7 +972,7 @@ msgid ""
"patterns."
msgstr ""
"Содержит маски SIP имен пользователей (локальная часть SIP URI, например "
""\"user\" из SIP URI \"user@example.com\") с которых (не) разрешено принимать "
"\"user\" из SIP URI \"user@example.com\") с которых (не) разрешено принимать "
"звонки данному абоненту. Поддерживаются \"*\", \"?\" и \"[x-y]\", где \"x\" и \"y\" "
"соответствуют числам от 0 до 9, аналогично шаблону в Unix консоли."
@ -984,7 +984,7 @@ msgid ""
"numbers from 0 to 9 may be used as wildcards like in shell patterns."
msgstr ""
"Содержит маски SIP имен пользователей (локальная часть SIP URI, например "
""\"user\" из SIP URI \"user@example.com\") которым (не) разрешено звонить "
"\"user\" из SIP URI \"user@example.com\") которым (не) разрешено звонить "
"данному абоненту. Поддерживаются \"*\", \"?\" и \"[x-y]\", где \"x\" и \"y\" "
"соответствуют числам от 0 до 9, аналогично шаблону в Unix консоли."

@ -100,10 +100,10 @@ sub validate_form {
if $resource->{$k}->$_isa('DateTime');
$resource->{$k} = $resource->{$k} + 0
if(defined $resource->{$k} && (
$resource->{$k}->is_int || $resource->{$k}->is_decimal) && (
$form->field($k)->$_isa('HTML::FormHandler::Field::Integer') ||
$form->field($k)->$_isa('HTML::FormHandler::Field::Money') ||
$form->field($k)->$_isa('HTML::FormHandler::Field::Float')));
$form->field($k)->$_isa('HTML::FormHandler::Field::Float')) &&
($resource->{$k}->is_int || $resource->{$k}->is_decimal));
# only do this for converting back from obj to hal
# otherwise it breaks db fields with the \0 and \1 notation
@ -192,10 +192,11 @@ sub valid_precondition {
sub require_preference {
my ($self, $c) = @_;
return 'minimal' unless $c->request->header('Prefer');
my @preference = grep { 'return' eq $_->[0] } split_header_words($c->request->header('Prefer'));
return $preference[0][1]
if 1 == @preference && ('minimal' eq $preference[0][1] || 'representation' eq $preference[0][1]);
return 'minimal';
$self->error($c, HTTP_BAD_REQUEST, "Header 'Prefer' must be either 'return=minimal' or 'return=representation'.");
}
sub require_wellformed_json {

@ -17,8 +17,11 @@ use NGCP::Panel::Form::RewriteRule::ResellerSet;
use NGCP::Panel::Form::RewriteRule::Rule;
sub get_form {
my ($self, $c) = @_;
my ($self, $c, $type) = @_;
if ($type && $type eq "rules") {
return NGCP::Panel::Form::RewriteRule::Rule->new;
}
if($c->user->roles eq "admin") {
return NGCP::Panel::Form::RewriteRule::AdminSet->new;
} else {
@ -29,8 +32,20 @@ sub get_form {
sub hal_from_item {
my ($self, $c, $item, $type) = @_;
my $form;
my $rwr_form = $self->get_form($c, "rules");
my %resource = $item->get_inflated_columns;
my @rewriterules;
for my $rule ( $item->voip_rewrite_rules->all ) {
my $rule_resource = { $rule->get_inflated_columns };
return unless $self->validate_form(
c => $c,
form => $rwr_form,
resource => $rule_resource,
run => 0,
);
push @rewriterules, $rule_resource;
}
my $hal = Data::HAL->new(
links => [
@ -55,6 +70,7 @@ sub hal_from_item {
resource => \%resource,
run => 0,
);
$resource{rewriterules} = \@rewriterules;
$hal->resource(\%resource);
return $hal;
}
@ -69,12 +85,6 @@ sub item_rs {
} else {
return;
}
} elsif($type eq "rules") {
if($c->user->roles eq "admin") {
$item_rs = $c->model('DB')->resultset('voip_rewrite_rules');
} else {
return;
}
} else {
die "You should not reach this";
}
@ -102,6 +112,17 @@ sub update_item {
}
}
if ($resource->{rewriterules}) {
$item->voip_rewrite_rules->delete;
my $i = 30;
for my $rule (@{ $resource->{rewriterules} }) {
$item->voip_rewrite_rules->create({
%{ $rule },
priority => $i++,
});
}
}
return unless $self->validate_form(
c => $c,
form => $form,

@ -39,7 +39,7 @@ sub hal_from_item {
Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
Data::HAL::Link->new(relation => 'self', href => sprintf("%s%d", $self->dispatch_path, $item->id)),
Data::HAL::Link->new(relation => "ngcp:$type", href => sprintf("/api/%s/%d", $type, $item->id)),
Data::HAL::Link->new(relation => "ngcp:rewrite", href => sprintf("/api/rewrite/%d", $item->set_id)), #TODO: naming?
Data::HAL::Link->new(relation => "ngcp:rewriterulesets", href => sprintf("/api/rewriterulesets/%d", $item->set_id)),
],
relation => 'ngcp:'.$self->resource_name,
);
@ -59,13 +59,7 @@ sub item_rs {
my ($self, $c, $type) = @_;
my $item_rs;
if($type eq "rulesets") {
if($c->user->roles eq "admin") {
$item_rs = $c->model('DB')->resultset('voip_rewrite_rule_sets');
} else {
return;
}
} elsif($type eq "rules") {
if($type eq "rules") {
if($c->user->roles eq "admin") {
$item_rs = $c->model('DB')->resultset('voip_rewrite_rules');
} else {

@ -10,12 +10,15 @@ use DateTime::Format::Strptime;
sub process {
my ($c, $rs, $cols, $row_func) = @_;
my $use_rs_cb = ('CODE' eq (ref $rs));
my $aaData = [];
my $totalRecords = $rs->count;
my $displayRecords = 0;
my $totalRecords = $use_rs_cb ? 0 : $rs->count;
# check if we need to join more tables
# TODO: can we nest it deeper than once level?
unless ($use_rs_cb) {
for my $c(@{ $cols }) {
my @parts = split /\./, $c->{name};
if($c->{literal_sql}) {
@ -45,6 +48,7 @@ sub process {
# TODO throw an error for now as we only support up to 3 levels
}
}
}
# generic searching
my @searchColumns = ();
@ -57,7 +61,7 @@ sub process {
if $col->{literal_sql};
push @searchColumns, $stmt if $col->{search};
}
if($searchString) {
if($searchString && ! $use_rs_cb) {
$rs = $rs->search([@searchColumns]);
}
@ -91,7 +95,7 @@ sub process {
}
}
$displayRecords = $rs->count;
$displayRecords = $use_rs_cb ? 0 : $rs->count;
# show specific row on top (e.g. if we come back from a newly created entry)
my $topId = $c->request->params->{iIdOnTop};
@ -108,7 +112,7 @@ sub process {
# sorting
my $sortColumn = $c->request->params->{iSortCol_0};
my $sortDirection = $c->request->params->{sSortDir_0} || 'asc';
if(defined $sortColumn && defined $sortDirection) {
if(defined $sortColumn && defined $sortDirection && ! $use_rs_cb) {
if('desc' eq lc $sortDirection) {
$sortDirection = 'desc';
} else {
@ -135,12 +139,20 @@ sub process {
# pagination
my $pageStart = $c->request->params->{iDisplayStart};
my $pageSize = $c->request->params->{iDisplayLength};
if ($use_rs_cb) {
($rs, $totalRecords, $displayRecords) = $rs->(
offset => $pageStart || 0,
rows => $pageSize || 5,
searchstring => $searchString,
);
} else {
if(defined $pageStart && defined $pageSize && $pageSize > 0) {
$rs = $rs->search(undef, {
offset => $pageStart,
rows => $pageSize,
});
}
}
for my $row ($rs->all) {
push @{ $aaData }, _prune_row($cols, $row->get_inflated_columns);
@ -206,4 +218,36 @@ sub _get_joined_column_name {
1;
__END__
=encoding UTF-8
=head1 NAME
NGCP::Panel::Utils::Datatables
=head1 DESCRIPTION
=head1 METHODS
=head2 C<process>
Query DB on datatables ajax request.
Format of the resultset callback (if used):
Arguments as hash
ARGUMENTS: offset, rows, searchstring
RETURNS: ($rs, $totalcount, $displaycount)
=head1 AUTHOR
Gerhard Jungwirth C<< <gjungwirth@sipwise.com> >>
=head1 LICENSE
This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.
# vim: set tabstop=4 expandtab:

@ -3,7 +3,7 @@
POT="lib/NGCP/Panel/I18N/messages.pot"
DIRS=""
for d in $(cat etc/i18n.inc); do
for d in lib/NGCP/Panel/Role lib/NGCP/Panel/Field lib/NGCP/Panel/AuthenticationStore lib/NGCP/Panel/Form lib/NGCP/Panel/Render lib/NGCP/Panel/Controller lib/NGCP/Panel/Model lib/NGCP/Panel/Utils lib/NGCP/Panel/Widget lib/NGCP/Panel/View lib/NGCP/Panel/Cache share/templates share/layout; do
DIRS="$DIRS --directory $d";
done
@ -18,5 +18,5 @@ xgettext.pl \
for po in $(find lib/NGCP/Panel/I18N -name "*.po"); do
echo; echo "Merging $po"; echo
msgmerge --update $po $POT
msgmerge --no-fuzzy-matching --update $po $POT
done

@ -6,6 +6,7 @@
helper.messages = messages;
helper.dt_columns = capture_dt_columns;
helper.length_change = 1;
helper.no_sort = 1;
helper.close_target = close_target;
helper.create_flag = create_flag;

@ -52,7 +52,7 @@ $(document).ready(function() {
[% ELSE %]
"bLengthChange": false,
[% END %]
"bSort": true,
"bSort": [% IF helper.no_sort %] false [% ELSE %] true [% END %],
"bInfo": true,
"iDisplayLength": 5,
'iShowPages': 5,

@ -26,7 +26,7 @@ $ua->ssl_opts(
$req = HTTP::Request->new('OPTIONS', $uri.'/api/billingfees/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
ok($res->header('Accept-Post') eq "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-billingfees", "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-billingfees", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
@ -119,7 +119,7 @@ my @allfees = ();
$res = $ua->request($req);
is($res->code, 422, "create profile without billing_profile_id");
my $err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_profile_id'/, "check error message in body");
# try to create fee with invalid billing_profile_id
@ -142,7 +142,7 @@ my @allfees = ();
$res = $ua->request($req);
is($res->code, 422, "create profile with invalid billing_profile_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_profile_id'/, "check error message in body");
# try to create fee with missing billing_zone_id
@ -165,7 +165,7 @@ my @allfees = ();
$res = $ua->request($req);
is($res->code, 422, "create profile without billing_zone_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='billing_zone_id'/, "check error message in body");
# try to create fee with invalid billing_zone_id
@ -188,7 +188,7 @@ my @allfees = ();
$res = $ua->request($req);
is($res->code, 422, "create profile without billing_profile_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_zone_id'/, "check error message in body");
# TODO: check for wrong values in rates, prepaid etc
@ -200,7 +200,7 @@ my @allfees = ();
is($res->code, 200, "fetch fees page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
ok($selfuri eq $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
@ -366,17 +366,11 @@ my @allfees = ();
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with missing Prefer
$req->remove_header('Prefer');
$res = $ua->request($req);
is($res->code, 400, "check put missing prefer");
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
is($res->code, 400, "check put invalid prefer");
$req->remove_header('Prefer');
$req->header('Prefer' => "return=representation");
@ -405,9 +399,9 @@ my @allfees = ();
$res = $ua->request($req);
is($res->code, 200, "check patched fee item");
my $mod_fee = JSON::from_json($res->decoded_content);
ok($mod_fee->{direction} eq "in", "check patched replace op");
ok($mod_fee->{_links}->{self}->{href} eq $firstfee, "check patched self link");
ok($mod_fee->{_links}->{collection}->{href} eq '/api/billingfees/', "check patched collection link");
is($mod_fee->{direction}, "in", "check patched replace op");
is($mod_fee->{_links}->{self}->{href}, $firstfee, "check patched self link");
is($mod_fee->{_links}->{collection}->{href}, '/api/billingfees/', "check patched collection link");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/billing_profile_id', value => undef } ]

@ -26,7 +26,7 @@ $ua->ssl_opts(
$req = HTTP::Request->new('OPTIONS', $uri.'/api/billingprofiles/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
ok($res->header('Accept-Post') eq "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-billingprofiles", "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-billingprofiles", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
@ -69,7 +69,7 @@ my @allprofiles = ();
$res = $ua->request($req);
is($res->code, 422, "create profile without reseller_id");
my $err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='reseller_id'/, "check error message in body");
# try to create profile with empty reseller_id
@ -81,7 +81,7 @@ my @allprofiles = ();
$res = $ua->request($req);
is($res->code, 422, "create profile with empty reseller_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='reseller_id'/, "check error message in body");
# try to create profile with invalid reseller_id
@ -93,7 +93,7 @@ my @allprofiles = ();
$res = $ua->request($req);
is($res->code, 422, "create profile with invalid reseller_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'reseller_id'/, "check error message in body");
# TODO: check for wrong values in prepaid, fees etc
@ -105,7 +105,7 @@ my @allprofiles = ();
is($res->code, 200, "fetch profile page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
ok($selfuri eq $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
@ -205,11 +205,6 @@ my @allprofiles = ();
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with missing Prefer
$req->remove_header('Prefer');
$res = $ua->request($req);
is($res->code, 400, "check put missing prefer");
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
@ -245,9 +240,9 @@ my @allprofiles = ();
$res = $ua->request($req);
is($res->code, 200, "check patched profile item");
my $mod_profile = JSON::from_json($res->decoded_content);
ok($mod_profile->{name} eq "patched name $t", "check patched replace op");
ok($mod_profile->{_links}->{self}->{href} eq $firstprofile, "check patched self link");
ok($mod_profile->{_links}->{collection}->{href} eq '/api/billingprofiles/', "check patched collection link");
is($mod_profile->{name}, "patched name $t", "check patched replace op");
is($mod_profile->{_links}->{self}->{href}, $firstprofile, "check patched self link");
is($mod_profile->{_links}->{collection}->{href}, '/api/billingprofiles/', "check patched collection link");
$req->content(JSON::to_json(

@ -28,7 +28,7 @@ $ua->ssl_opts(
$req = HTTP::Request->new('OPTIONS', $uri.'/api/contracts/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
ok($res->header('Accept-Post') eq "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-contracts", "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-contracts", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
@ -107,7 +107,7 @@ my @allcontracts = ();
$res = $ua->request($req);
is($res->code, 422, "create contract with invalid type");
my $err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'type'/, "check error message in body");
# try to create invalid contract with wrong billing profile
@ -120,7 +120,7 @@ my @allcontracts = ();
$res = $ua->request($req);
is($res->code, 422, "create contract with invalid billing profile");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_profile_id'/, "check error message in body");
# try to create invalid contract with customercontact
@ -133,7 +133,7 @@ my @allcontracts = ();
$res = $ua->request($req);
is($res->code, 422, "create contract with invalid contact");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /The contact_id is not a valid ngcp:systemcontacts item/, "check error message in body");
# try to create invalid contract without contact
@ -155,7 +155,7 @@ my @allcontracts = ();
$res = $ua->request($req);
is($res->code, 422, "create contract with invalid status");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='status'/, "check error message in body");
# iterate over contracts collection to check next/prev links and status
@ -165,7 +165,7 @@ my @allcontracts = ();
is($res->code, 200, "fetch contacts page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
ok($selfuri eq $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
@ -271,17 +271,11 @@ my @allcontracts = ();
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with missing Prefer
$req->remove_header('Prefer');
$res = $ua->request($req);
is($res->code, 400, "check put missing prefer");
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
is($res->code, 400, "check put invalid prefer");
$req->remove_header('Prefer');
$req->header('Prefer' => "return=representation");
@ -312,9 +306,9 @@ my @allcontracts = ();
$res = $ua->request($req);
is($res->code, 200, "check patched contract item");
my $mod_contact = JSON::from_json($res->decoded_content);
ok($mod_contact->{status} eq "pending", "check patched replace op");
ok($mod_contact->{_links}->{self}->{href} eq $firstcontract, "check patched self link");
ok($mod_contact->{_links}->{collection}->{href} eq '/api/contracts/', "check patched collection link");
is($mod_contact->{status}, "pending", "check patched replace op");
is($mod_contact->{_links}->{self}->{href}, $firstcontract, "check patched self link");
is($mod_contact->{_links}->{collection}->{href}, '/api/contracts/', "check patched collection link");
$req->content(JSON::to_json(
@ -372,7 +366,7 @@ my @allcontracts = ();
$res = $ua->request($req);
is($res->code, 200, "check termination of contract");
$pc = JSON::from_json($res->decoded_content);
ok($pc->{status} eq "terminated", "check termination status of contract");
is($pc->{status}, "terminated", "check termination status of contract");
}
# check if we can still get the terminated contract

@ -26,7 +26,7 @@ $ua->ssl_opts(
$req = HTTP::Request->new('OPTIONS', $uri.'/api/customercontacts/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
ok($res->header('Accept-Post') eq "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-customercontacts", "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-customercontacts", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
@ -82,7 +82,7 @@ my @allcontacts = ();
$res = $ua->request($req);
is($res->code, 422, "create invalid test contact with missing email");
my $email_err = JSON::from_json($res->decoded_content);
ok($email_err->{code} eq "422", "check error code in body");
is($email_err->{code}, "422", "check error code in body");
ok($email_err->{message} =~ /field=\'email\'/, "check error message in body");
# try to create invalid contact without reseller_id
$req->content(JSON::to_json({
@ -94,7 +94,7 @@ my @allcontacts = ();
$res = $ua->request($req);
is($res->code, 422, "create invalid test contact with missing reseller_id");
$email_err = JSON::from_json($res->decoded_content);
ok($email_err->{code} eq "422", "check error code in body");
is($email_err->{code}, "422", "check error code in body");
ok($email_err->{message} =~ /field=\'reseller_id\'/, "check error message in body");
# try to create invalid contact with invalid reseller_id
@ -107,7 +107,7 @@ my @allcontacts = ();
$res = $ua->request($req);
is($res->code, 422, "create invalid test contact with invalid reseller_id");
$email_err = JSON::from_json($res->decoded_content);
ok($email_err->{code} eq "422", "check error code in body");
is($email_err->{code}, "422", "check error code in body");
ok($email_err->{message} =~ /Invalid \'reseller_id\'/, "check error message in body");
# iterate over contacts collection to check next/prev links
@ -117,7 +117,7 @@ my @allcontacts = ();
is($res->code, 200, "fetch contacts page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
ok($selfuri eq $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
@ -207,11 +207,6 @@ my @allcontacts = ();
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with missing Prefer
$req->remove_header('Prefer');
$res = $ua->request($req);
is($res->code, 400, "check put missing prefer");
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
@ -243,7 +238,7 @@ my @allcontacts = ();
$res = $ua->request($req);
is($res->code, 200, "check patched contact item");
my $mod_contact = JSON::from_json($res->decoded_content);
ok($mod_contact->{firstname} eq "patchedfirst", "check patched replace op");
is($mod_contact->{firstname}, "patchedfirst", "check patched replace op");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/firstname', value => undef } ]

@ -28,7 +28,7 @@ $ua->ssl_opts(
$req = HTTP::Request->new('OPTIONS', $uri.'/api/customers/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
ok($res->header('Accept-Post') eq "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-customers", "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-customers", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
@ -117,7 +117,7 @@ my @allcustomers = ();
$res = $ua->request($req);
is($res->code, 422, "create customer with invalid type");
my $err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'type'/, "check error message in body");
# try to create invalid customer with wrong billing profile
@ -132,7 +132,7 @@ my @allcustomers = ();
$res = $ua->request($req);
is($res->code, 422, "create customer with invalid billing profile");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /Invalid 'billing_profile_id'/, "check error message in body");
# try to create invalid customer with systemcontact
@ -147,7 +147,7 @@ my @allcustomers = ();
$res = $ua->request($req);
is($res->code, 422, "create customer with invalid contact");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /The contact_id is not a valid ngcp:customercontacts item/, "check error message in body");
# try to create invalid customer without contact
@ -173,7 +173,7 @@ my @allcustomers = ();
$res = $ua->request($req);
is($res->code, 422, "create customer with invalid status");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='status'/, "check error message in body");
# try to create invalid customer with invalid max_subscribers
@ -188,7 +188,7 @@ my @allcustomers = ();
$res = $ua->request($req);
is($res->code, 422, "create customer with invalid max_subscribers");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='max_subscribers'/, "check error message in body");
# iterate over customers collection to check next/prev links and status
@ -198,7 +198,7 @@ my @allcustomers = ();
is($res->code, 200, "fetch contacts page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
ok($selfuri eq $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
@ -305,17 +305,11 @@ my @allcustomers = ();
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with missing Prefer
$req->remove_header('Prefer');
$res = $ua->request($req);
is($res->code, 400, "check put missing prefer");
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
is($res->code, 400, "check put invalid prefer");
$req->remove_header('Prefer');
$req->header('Prefer' => "return=representation");
@ -346,9 +340,9 @@ my @allcustomers = ();
$res = $ua->request($req);
is($res->code, 200, "check patched customer item");
my $mod_contact = JSON::from_json($res->decoded_content);
ok($mod_contact->{status} eq "pending", "check patched replace op");
ok($mod_contact->{_links}->{self}->{href} eq $firstcustomer, "check patched self link");
ok($mod_contact->{_links}->{collection}->{href} eq '/api/customers/', "check patched collection link");
is($mod_contact->{status}, "pending", "check patched replace op");
is($mod_contact->{_links}->{self}->{href}, $firstcustomer, "check patched self link");
is($mod_contact->{_links}->{collection}->{href}, '/api/customers/', "check patched collection link");
$req->content(JSON::to_json(
@ -412,7 +406,7 @@ my @allcustomers = ();
$res = $ua->request($req);
is($res->code, 200, "check termination of customer");
$pc = JSON::from_json($res->decoded_content);
ok($pc->{status} eq "terminated", "check termination status of customer");
is($pc->{status}, "terminated", "check termination status of customer");
}
# check if we can still get the terminated customer

@ -26,7 +26,7 @@ $ua->ssl_opts(
$req = HTTP::Request->new('OPTIONS', $uri.'/api/resellers/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
ok($res->header('Accept-Post') eq "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-resellers", "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-resellers", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
@ -116,7 +116,7 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 422, "create reseller without contract_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='contract_id'/, "check error message in body");
# try to create reseller with empty contract_id
@ -128,7 +128,7 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 422, "create reseller with empty contract_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='contract_id'/, "check error message in body");
# try to create reseller with existing contract_id
@ -140,7 +140,7 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 422, "create reseller with existing contract_id");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /reseller with this contract already exists/, "check error message in body");
# try to create reseller with existing name
@ -152,7 +152,7 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 422, "create reseller with existing name");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /reseller with this name already exists/, "check error message in body");
# try to create reseller with missing name
@ -163,7 +163,7 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 422, "create reseller with missing name");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='name'/, "check error message in body");
# try to create reseller with missing status
@ -174,7 +174,7 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 422, "create reseller with invalid status");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='status'/, "check error message in body");
# try to create reseller with invalid status
@ -186,7 +186,7 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 422, "create reseller with invalid status");
$err = JSON::from_json($res->decoded_content);
ok($err->{code} eq "422", "check error code in body");
is($err->{code}, "422", "check error code in body");
ok($err->{message} =~ /field='status'/, "check error message in body");
# iterate over collection to check next/prev links and status
@ -196,7 +196,7 @@ my @allresellers = ();
is($res->code, 200, "fetch reseller page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
ok($selfuri eq $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
@ -294,11 +294,6 @@ my @allresellers = ();
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with missing Prefer
$req->remove_header('Prefer');
$res = $ua->request($req);
is($res->code, 400, "check put missing prefer");
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
@ -333,9 +328,9 @@ my @allresellers = ();
$res = $ua->request($req);
is($res->code, 200, "check patched reseller item");
my $mod_reseller = JSON::from_json($res->decoded_content);
ok($mod_reseller->{name} eq "patched name $t", "check patched replace op");
ok($mod_reseller->{_links}->{self}->{href} eq $firstreseller, "check patched self link");
ok($mod_reseller->{_links}->{collection}->{href} eq '/api/resellers/', "check patched collection link");
is($mod_reseller->{name}, "patched name $t", "check patched replace op");
is($mod_reseller->{_links}->{self}->{href}, $firstreseller, "check patched self link");
is($mod_reseller->{_links}->{collection}->{href}, '/api/resellers/', "check patched collection link");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/contract_id', value => undef } ]

@ -0,0 +1,392 @@
use Sipwise::Base;
use Net::Domain qw(hostfqdn);
use LWP::UserAgent;
use JSON qw();
use Test::More;
my $uri = $ENV{CATALYST_SERVER} || ('https://'.hostfqdn.':4443');
my $valid_ssl_client_cert = $ENV{API_SSL_CLIENT_CERT} ||
"/etc/ssl/ngcp/api/NGCP-API-client-certificate.pem";
my $valid_ssl_client_key = $ENV{API_SSL_CLIENT_KEY} ||
$valid_ssl_client_cert;
my $ssl_ca_cert = $ENV{API_SSL_CA_CERT} || "/etc/ssl/ngcp/api/ca-cert.pem";
my ($ua, $req, $res);
$ua = LWP::UserAgent->new;
$ua->ssl_opts(
SSL_cert_file => $valid_ssl_client_cert,
SSL_key_file => $valid_ssl_client_key,
SSL_ca_file => $ssl_ca_cert,
);
# OPTIONS tests
{
$req = HTTP::Request->new('OPTIONS', $uri.'/api/rewriterulesets/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-rewriterulesets", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
foreach my $opt(qw( GET HEAD OPTIONS POST )) {
ok(grep(/^$opt$/, @hopts), "check for existence of '$opt' in Allow header");
ok(grep(/^$opt$/, @{ $opts->{methods} }), "check for existence of '$opt' in body");
}
}
my $reseller_id = 1;
# first, we create a rewriteruleset
$req = HTTP::Request->new('POST', $uri.'/api/rewriterulesets/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
my $t = time;
$req->content(JSON::to_json({
reseller_id => $reseller_id,
description => "testdescription $t",
name => "test rewriteruleset $t",
}));
$res = $ua->request($req);
is($res->code, 201, "create test rewriteruleset");
my $rewriteruleset_id = $res->header('Location');
# TODO: get it from body! -> we have no body ...
$rewriteruleset_id =~ s/^.+\/(\d+)$/$1/;
diag("set id is $rewriteruleset_id");
# then, we create a rewriterule
$req = HTTP::Request->new('POST', $uri.'/api/rewriterules/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
set_id => $rewriteruleset_id,
description => "test rule $t",
direction => "in",
field => "caller",
match_pattern => "test pattern $t",
replace_pattern => "test_replace_$t",
}));
$res = $ua->request($req);
is($res->code, 201, "create test rewriterule");
my $rule_id = $res->header('Location');
# TODO: get it from body! -> we have no body ...
$rule_id =~ s/^.+\/(\d+)$/$1/;
# collection test
my $firstrule = undef;
my @allrules = ();
{
# create 6 new rewriterules
my %rules = ();
for(my $i = 1; $i <= 6; ++$i) {
$req = HTTP::Request->new('POST', $uri.'/api/rewriterules/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
set_id => $rewriteruleset_id,
description => "test rule $t - $i",
direction => "out",
field => "callee",
match_pattern => "test pattern $t",
replace_pattern => "test_replace_$t",
}));
$res = $ua->request($req);
is($res->code, 201, "create test rewriterule $i");
$rules{$res->header('Location')} = 1;
push @allrules, $res->header('Location');
$firstrule = $res->header('Location') unless $firstrule;
}
# try to create ruleset without reseller_id
$req = HTTP::Request->new('POST', $uri.'/api/rewriterulesets/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
#reseller_id => $reseller_id,
description => "testdescription $t",
name => "test rewriteruleset $t",
}));
$res = $ua->request($req);
is($res->code, 422, "create ruleset without reseller_id");
my $err = JSON::from_json($res->decoded_content);
is($err->{code}, "422", "check error code in body");
like($err->{message}, qr/Invalid 'reseller_id'/, "check error message in body");
# try to create rule with invalid set_id
$req = HTTP::Request->new('POST', $uri.'/api/rewriterules/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
set_id => 999999,
description => "test rule $t",
direction => "in",
field => "caller",
match_pattern => "test pattern $t",
replace_pattern => "test_replace_$t",
}));
$res = $ua->request($req);
is($res->code, 422, "create rule with invalid set_id");
$err = JSON::from_json($res->decoded_content);
is($err->{code}, "422", "check error code in body");
like($err->{message}, qr/Invalid 'set_id'/, "check error message in body");
# try to create rule with negative set_id
$req = HTTP::Request->new('POST', $uri.'/api/rewriterules/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
set_id => -100,
description => "test rule $t",
direction => "in",
field => "caller",
match_pattern => "test pattern $t",
replace_pattern => "test_replace_$t",
}));
$res = $ua->request($req);
is($res->code, 422, "create rule with negative set_id");
$err = JSON::from_json($res->decoded_content);
is($err->{code}, "422", "check error code in body");
like($err->{message}, qr/Invalid 'set_id'/, "check error message in body");
# try to create rule with missing match_pattern
$req = HTTP::Request->new('POST', $uri.'/api/rewriterules/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
set_id => $rewriteruleset_id,
description => "test rule $t",
direction => "in",
field => "caller",
#match_pattern => "test pattern $t",
replace_pattern => "test_replace_$t",
}));
$res = $ua->request($req);
is($res->code, 422, "create rule with missing match_pattern");
$err = JSON::from_json($res->decoded_content);
is($err->{code}, "422", "check error code in body");
like($err->{message}, qr/field='match_pattern'/, "check error message in body");
# try to create rule with invalid direction and field
$req = HTTP::Request->new('POST', $uri.'/api/rewriterules/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
set_id => $rewriteruleset_id,
description => "test rule $t",
direction => "foo",
field => "bar",
match_pattern => "test pattern $t",
replace_pattern => "test_replace_$t",
}));
$res = $ua->request($req);
is($res->code, 422, "create rule with invalid direction and field");
$err = JSON::from_json($res->decoded_content);
is($err->{code}, "422", "check error code in body");
like($err->{message}, qr/field='direction'/, "check error message in body");
like($err->{message}, qr/field='field'/, "check error message in body");
# try to create rule without set_id
$req = HTTP::Request->new('POST', $uri.'/api/rewriterules/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
#set_id => $rewriteruleset_id,
description => "test rule $t",
direction => "in",
field => "caller",
match_pattern => "test pattern $t",
replace_pattern => "test_replace_$t",
}));
$res = $ua->request($req);
is($res->code, 422, "create rule without set_id");
$err = JSON::from_json($res->decoded_content);
is($err->{code}, "422", "check error code in body");
like($err->{message}, qr/Required: 'set_id'/, "check error message in body");
# iterate over rules collection to check next/prev links and status
my $nexturi = $uri.'/api/rewriterules/?page=1&rows=5';
do {
$res = $ua->get($nexturi);
is($res->code, 200, "fetch rules page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
my %q = $colluri->query_form;
ok(exists $q{page} && exists $q{rows}, "check existence of 'page' and 'row' in 'self'");
my $page = int($q{page});
my $rows = int($q{rows});
if($page == 1) {
ok(!exists $collection->{_links}->{prev}->{href}, "check absence of 'prev' on first page");
} else {
ok(exists $collection->{_links}->{prev}->{href}, "check existence of 'prev'");
}
if(($collection->{total_count} / $rows) <= $page) {
ok(!exists $collection->{_links}->{next}->{href}, "check absence of 'next' on last page");
} else {
ok(exists $collection->{_links}->{next}->{href}, "check existence of 'next'");
}
if($collection->{_links}->{next}->{href}) {
$nexturi = $uri . $collection->{_links}->{next}->{href};
} else {
$nexturi = undef;
}
ok((ref $collection->{_links}->{'ngcp:rewriterules'} eq "ARRAY" ||
ref $collection->{_links}->{'ngcp:rewriterules'} eq "HASH"),
"check if 'ngcp:rewriterules' is array/hash-ref");
# remove any entry we find in the collection for later check
if(ref $collection->{_links}->{'ngcp:rewriterules'} eq "HASH") {
ok(exists $collection->{_embedded}->{'ngcp:rewriterules'}->{_links}->{'ngcp:rewriterules'}, "check presence of ngcp:rewriterules relation");
ok(exists $collection->{_embedded}->{'ngcp:rewriterules'}->{_links}->{'ngcp:rewriterulesets'}, "check presence of ngcp:rewriterulesets relation");
delete $rules{$collection->{_links}->{'ngcp:rewriterules'}->{href}};
} else {
foreach my $c(@{ $collection->{_links}->{'ngcp:rewriterules'} }) {
delete $rules{$c->{href}};
}
foreach my $c(@{ $collection->{_embedded}->{'ngcp:rewriterules'} }) {
ok(exists $c->{_links}->{'ngcp:rewriterules'}, "check presence of ngcp:rewriterules (self) relation");
ok(exists $c->{_links}->{'ngcp:rewriterulesets'}, "check presence of ngcp:rewriterulesets relation");
delete $rules{$c->{_links}->{self}->{href}};
}
}
} while($nexturi);
is(scalar(keys %rules), 0, "check if all test rewriterules have been found");
}
# test rule item
{
$req = HTTP::Request->new('OPTIONS', $uri.'/'.$firstrule);
$res = $ua->request($req);
is($res->code, 200, "check options on item");
my @hopts = split /\s*,\s*/, $res->header('Allow');
my $opts = JSON::from_json($res->decoded_content);
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
foreach my $opt(qw( GET HEAD OPTIONS PUT PATCH DELETE )) {
ok(grep(/^$opt$/, @hopts), "check for existence of '$opt' in Allow header");
ok(grep(/^$opt$/, @{ $opts->{methods} }), "check for existence of '$opt' in body");
}
foreach my $opt(qw( POST )) {
ok(!grep(/^$opt$/, @hopts), "check for absence of '$opt' in Allow header");
ok(!grep(/^$opt$/, @{ $opts->{methods} }), "check for absence of '$opt' in body");
}
$req = HTTP::Request->new('GET', $uri.'/'.$firstrule);
$res = $ua->request($req);
is($res->code, 200, "fetch one rule item");
my $rule = JSON::from_json($res->decoded_content);
ok(exists $rule->{direction} && $rule->{direction} =~ /^(in|out)$/ , "check existence of direction");
ok(exists $rule->{field} && $rule->{field} =~ /^(caller|callee)$/, "check existence of field");
ok(exists $rule->{match_pattern} && length($rule->{match_pattern}) > 0, "check existence of match_pattern");
ok(exists $rule->{replace_pattern} && length($rule->{replace_pattern}) > 0, "check existence of replace_pattern");
ok(exists $rule->{description} && length($rule->{description}) > 0, "check existence of description");
# PUT same result again
my $old_rule = { %$rule };
delete $rule->{_links};
delete $rule->{_embedded};
$req = HTTP::Request->new('PUT', $uri.'/'.$firstrule);
# check if it fails without content type
$req->remove_header('Content-Type');
$req->header('Prefer' => "return=minimal");
$res = $ua->request($req);
is($res->code, 415, "check put missing content type");
# check if it fails with unsupported content type
$req->header('Content-Type' => 'application/xxx');
$res = $ua->request($req);
is($res->code, 415, "check put invalid content type");
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
is($res->code, 400, "check put invalid prefer");
$req->remove_header('Prefer');
$req->header('Prefer' => "return=representation");
# check if it fails with missing body
$res = $ua->request($req);
is($res->code, 400, "check put no body");
# check if put is ok
$req->content(JSON::to_json($rule));
$res = $ua->request($req);
is($res->code, 200, "check put successful");
my $new_rule = JSON::from_json($res->decoded_content);
is_deeply($old_rule, $new_rule, "check put if unmodified put returns the same");
# check if we have the proper links
ok(exists $new_rule->{_links}->{'ngcp:rewriterules'}, "check put presence of ngcp:rewriterules relation");
ok(exists $new_rule->{_links}->{'ngcp:rewriterulesets'}, "check put presence of ngcp:rewriterulesets relation");
$req = HTTP::Request->new('PATCH', $uri.'/'.$firstrule);
$req->header('Prefer' => 'return=representation');
$req->header('Content-Type' => 'application/json-patch+json');
$req->content(JSON::to_json(
[ { op => 'replace', path => '/description', value => 'iwasmodifiedbyreplace' } ]
));
$res = $ua->request($req);
is($res->code, 200, "check patched rule item");
my $mod_rule = JSON::from_json($res->decoded_content);
is($mod_rule->{description}, "iwasmodifiedbyreplace", "check patched replace op");
is($mod_rule->{_links}->{self}->{href}, $firstrule, "check patched self link");
is($mod_rule->{_links}->{collection}->{href}, '/api/rewriterules/', "check patched collection link");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/description', value => undef } ]
));
$res = $ua->request($req);
is($res->code, 422, "check patched undef description");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/direction', value => 99999 } ]
));
$res = $ua->request($req);
is($res->code, 422, "check patched invalid direction");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/match_pattern', value => undef } ]
));
$res = $ua->request($req);
is($res->code, 422, "check patched undef match_pattern");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/field', value => 'foobar' } ]
));
$res = $ua->request($req);
is($res->code, 422, "check patched invalid field");
}
{
my $firstr;
foreach my $r(@allrules) {
$req = HTTP::Request->new('DELETE', $uri.'/'.$r);
$res = $ua->request($req);
is($res->code, 204, "check delete of rule");
$firstr = $r unless $firstr;
}
$req = HTTP::Request->new('GET', $uri.'/'.$firstr);
$res = $ua->request($req);
is($res->code, 404, "check if deleted rule is really gone");
$req = HTTP::Request->new('DELETE', $uri.'/api/rewriterulesets/'.$rewriteruleset_id);
$res = $ua->request($req);
is($res->code, 204, "check delete of rewriteruleset");
$req = HTTP::Request->new('GET', $uri.'/api/rewriterulesets/'.$rewriteruleset_id);
$res = $ua->request($req);
is($res->code, 404, "check if deleted rewriteruleset is really gone");
}
done_testing;
# vim: set tabstop=4 expandtab:

@ -26,7 +26,7 @@ $ua->ssl_opts(
$req = HTTP::Request->new('OPTIONS', $uri.'/api/systemcontacts/');
$res = $ua->request($req);
is($res->code, 200, "check options request");
ok($res->header('Accept-Post') eq "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-systemcontacts", "check Accept-Post header in options response");
is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-systemcontacts", "check Accept-Post header in options response");
my $opts = JSON::from_json($res->decoded_content);
my @hopts = split /\s*,\s*/, $res->header('Allow');
ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
@ -67,7 +67,7 @@ my @allcontacts = ();
$res = $ua->request($req);
is($res->code, 422, "create invalid test contact with missing email");
my $email_err = JSON::from_json($res->decoded_content);
ok($email_err->{code} eq "422", "check error code in body");
is($email_err->{code}, "422", "check error code in body");
ok($email_err->{message} =~ /field=\'email\'/, "check error message in body");
# iterate over contacts collection to check next/prev links
@ -77,7 +77,7 @@ my @allcontacts = ();
is($res->code, 200, "fetch contacts page");
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
ok($selfuri eq $nexturi, "check _links.self.href of collection");
is($selfuri, $nexturi, "check _links.self.href of collection");
my $colluri = URI->new($selfuri);
ok($collection->{total_count} > 0, "check 'total_count' of collection");
@ -168,17 +168,11 @@ my @allcontacts = ();
$req->remove_header('Content-Type');
$req->header('Content-Type' => 'application/json');
# check if it fails with missing Prefer
$req->remove_header('Prefer');
$res = $ua->request($req);
is($res->code, 400, "check put missing prefer");
# check if it fails with invalid Prefer
$req->header('Prefer' => "return=invalid");
$res = $ua->request($req);
is($res->code, 400, "check put invalid prefer");
$req->remove_header('Prefer');
$req->header('Prefer' => "return=representation");
@ -213,7 +207,7 @@ my @allcontacts = ();
$res = $ua->request($req);
is($res->code, 200, "check patched contact item");
my $mod_contact = JSON::from_json($res->decoded_content);
ok($mod_contact->{firstname} eq "patchedfirst", "check patched replace op");
is($mod_contact->{firstname}, "patchedfirst", "check patched replace op");
$req->content(JSON::to_json(
[ { op => 'replace', path => '/firstname', value => undef } ]

@ -24,11 +24,6 @@ $ua->ssl_opts(
{
$req = HTTP::Request->new('PATCH', $uri.'/api/systemcontacts/1');
$res = $ua->request($req);
is($res->code, 400, "check patch missing Prefer code");
$body = JSON::from_json($res->decoded_content);
ok($body->{message} =~ /Use the 'Prefer' header/, "check patch missing Prefer response");
$req->header('Prefer' => 'return=minimal');
$res = $ua->request($req);
is($res->code, 415, "check patch missing media type");
@ -43,7 +38,7 @@ $ua->ssl_opts(
$res = $ua->request($req);
is($res->code, 400, "check patch missing body");
$body = JSON::from_json($res->decoded_content);
ok($body->{message} =~ /is missing a message body/, "check patch missing body response");
like($body->{message}, qr/is missing a message body/, "check patch missing body response");
$req->content(JSON::to_json(
{ foo => 'bar' },
@ -51,7 +46,7 @@ $ua->ssl_opts(
$res = $ua->request($req);
is($res->code, 400, "check patch no array body");
$body = JSON::from_json($res->decoded_content);
ok($body->{message} =~ /must be an array/, "check patch missing body response");
like($body->{message}, qr/must be an array/, "check patch missing body response");
$req->content(JSON::to_json(
[{ foo => 'bar' }],
@ -59,7 +54,7 @@ $ua->ssl_opts(
$res = $ua->request($req);
is($res->code, 400, "check patch no op in body");
$body = JSON::from_json($res->decoded_content);
ok($body->{message} =~ /must have an 'op' field/, "check patch no op in body response");
like($body->{message}, qr/must have an 'op' field/, "check patch no op in body response");
$req->content(JSON::to_json(
[{ op => 'bar' }],
@ -67,23 +62,23 @@ $ua->ssl_opts(
$res = $ua->request($req);
is($res->code, 400, "check patch invalid op in body");
$body = JSON::from_json($res->decoded_content);
ok($body->{message} =~ /Invalid PATCH op /, "check patch no op in body response");
like($body->{message}, qr/Invalid PATCH op /, "check patch no op in body response");
$req->content(JSON::to_json(
[{ op => 'test' }],
[{ op => 'replace' }],
));
$res = $ua->request($req);
is($res->code, 400, "check patch missing fields for op");
$body = JSON::from_json($res->decoded_content);
ok($body->{message} =~ /Missing PATCH keys /, "check patch missing fields for op response");
like($body->{message}, qr/Missing PATCH keys /, "check patch missing fields for op response");
$req->content(JSON::to_json(
[{ op => 'test', path => '/foo', value => 'bar', invalid => 'sna' }],
[{ op => 'replace', path => '/foo', value => 'bar', invalid => 'sna' }],
));
$res = $ua->request($req);
is($res->code, 400, "check patch extra fields for op");
$body = JSON::from_json($res->decoded_content);
ok($body->{message} =~ /Invalid PATCH key /, "check patch extra fields for op response");
like($body->{message}, qr/Invalid PATCH key /, "check patch extra fields for op response");
}
done_testing;

Loading…
Cancel
Save