diff --git a/Build.PL b/Build.PL
index bfb5605075..fbbe35442b 100644
--- a/Build.PL
+++ b/Build.PL
@@ -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
diff --git a/debian/control b/debian/control
index cceb13fb4f..ebab45046a 100644
--- a/debian/control
+++ b/debian/control
@@ -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,
diff --git a/debian/ngcp-panel.install b/debian/ngcp-panel.install
index a28dedae4f..98f14603a1 100644
--- a/debian/ngcp-panel.install
+++ b/debian/ngcp-panel.install
@@ -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
diff --git a/etc/i18n.inc b/etc/i18n.inc
deleted file mode 100644
index bbca5b575b..0000000000
--- a/etc/i18n.inc
+++ /dev/null
@@ -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
diff --git a/inc/Local/Module/Build.pm b/inc/Local/Module/Build.pm
index c4dceacc02..85ed527d2a 100644
--- a/inc/Local/Module/Build.pm
+++ b/inc/Local/Module/Build.pm
@@ -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();
diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm
index 6e41c83c61..ed1278114d 100644
--- a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm
+++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm
@@ -23,12 +23,51 @@ class_has 'api_description' => (
'Defines a collection of Rewrite Rules.',
);
-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) = @_;
diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm
index 635ac53d9a..9f8d0aebfc 100644
--- a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm
+++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm
@@ -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) = @_;
diff --git a/lib/NGCP/Panel/Controller/API/RewriteRules.pm b/lib/NGCP/Panel/Controller/API/RewriteRules.pm
index d9ce48b0ca..8b6b1a584d 100644
--- a/lib/NGCP/Panel/Controller/API/RewriteRules.pm
+++ b/lib/NGCP/Panel/Controller/API/RewriteRules.pm
@@ -21,13 +21,41 @@ class_has 'api_description' => (
'Defines a set of Rewrite Rules which are grouped in Rewrite Rule Sets. 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) = @_;
diff --git a/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm
index a61830bc77..1992450cf0 100644
--- a/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm
+++ b/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm
@@ -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) = @_;
diff --git a/lib/NGCP/Panel/Controller/Callflow.pm b/lib/NGCP/Panel/Controller/Callflow.pm
index f9981f815c..32a550be16 100644
--- a/lib/NGCP/Panel/Controller/Callflow.pm
+++ b/lib/NGCP/Panel/Controller/Callflow.pm
@@ -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") );
diff --git a/lib/NGCP/Panel/I18N/ru.po b/lib/NGCP/Panel/I18N/ru.po
index 5d929e92a8..9eb368e0d1 100644
--- a/lib/NGCP/Panel/I18N/ru.po
+++ b/lib/NGCP/Panel/I18N/ru.po
@@ -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 консоли."
diff --git a/lib/NGCP/Panel/Role/API.pm b/lib/NGCP/Panel/Role/API.pm
index 41fb429b7d..fc1b09ddda 100644
--- a/lib/NGCP/Panel/Role/API.pm
+++ b/lib/NGCP/Panel/Role/API.pm
@@ -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 {
diff --git a/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm
index a576cc570d..751cd186c5 100644
--- a/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm
+++ b/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm
@@ -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,
diff --git a/lib/NGCP/Panel/Role/API/RewriteRules.pm b/lib/NGCP/Panel/Role/API/RewriteRules.pm
index 2d0c1a6603..ccf99b9fb1 100644
--- a/lib/NGCP/Panel/Role/API/RewriteRules.pm
+++ b/lib/NGCP/Panel/Role/API/RewriteRules.pm
@@ -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 {
diff --git a/lib/NGCP/Panel/Utils/Datatables.pm b/lib/NGCP/Panel/Utils/Datatables.pm
index 37cb3177e1..c6ce33f66a 100644
--- a/lib/NGCP/Panel/Utils/Datatables.pm
+++ b/lib/NGCP/Panel/Utils/Datatables.pm
@@ -10,39 +10,43 @@ 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?
- for my $c(@{ $cols }) {
- my @parts = split /\./, $c->{name};
- if($c->{literal_sql}) {
- $rs = $rs->search_rs(undef, {
- '+select' => [ \[$c->{literal_sql}] ],
- '+as' => [ $c->{accessor} ],
- });
- } elsif(@parts == 2) {
- $rs = $rs->search_rs(undef, {
- join => $parts[0],
- '+select' => [ $c->{name} ],
- '+as' => [ $c->{accessor} ],
- });
- } elsif(@parts == 3) {
- $rs = $rs->search_rs(undef, {
- join => { $parts[0] => $parts[1] },
- '+select' => [ $parts[1].'.'.$parts[2] ],
- '+as' => [ $c->{accessor} ],
- });
- } elsif(@parts == 4) {
- $rs = $rs->search_rs(undef, {
- join => { $parts[0] => { $parts[1] => $parts[2] } },
- '+select' => [ $parts[2].'.'.$parts[3] ],
- '+as' => [ $c->{accessor} ],
- });
- } elsif(@parts > 4) {
- # TODO throw an error for now as we only support up to 3 levels
+ unless ($use_rs_cb) {
+ for my $c(@{ $cols }) {
+ my @parts = split /\./, $c->{name};
+ if($c->{literal_sql}) {
+ $rs = $rs->search_rs(undef, {
+ '+select' => [ \[$c->{literal_sql}] ],
+ '+as' => [ $c->{accessor} ],
+ });
+ } elsif(@parts == 2) {
+ $rs = $rs->search_rs(undef, {
+ join => $parts[0],
+ '+select' => [ $c->{name} ],
+ '+as' => [ $c->{accessor} ],
+ });
+ } elsif(@parts == 3) {
+ $rs = $rs->search_rs(undef, {
+ join => { $parts[0] => $parts[1] },
+ '+select' => [ $parts[1].'.'.$parts[2] ],
+ '+as' => [ $c->{accessor} ],
+ });
+ } elsif(@parts == 4) {
+ $rs = $rs->search_rs(undef, {
+ join => { $parts[0] => { $parts[1] => $parts[2] } },
+ '+select' => [ $parts[2].'.'.$parts[3] ],
+ '+as' => [ $c->{accessor} ],
+ });
+ } elsif(@parts > 4) {
+ # TODO throw an error for now as we only support up to 3 levels
+ }
}
}
@@ -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,11 +139,19 @@ sub process {
# pagination
my $pageStart = $c->request->params->{iDisplayStart};
my $pageSize = $c->request->params->{iDisplayLength};
- if(defined $pageStart && defined $pageSize && $pageSize > 0) {
- $rs = $rs->search(undef, {
- offset => $pageStart,
- rows => $pageSize,
- });
+ 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) {
@@ -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
+
+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<< >>
+
+=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:
diff --git a/script/extract_i18n.sh b/script/extract_i18n.sh
index f512797f57..b55497f75f 100755
--- a/script/extract_i18n.sh
+++ b/script/extract_i18n.sh
@@ -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
diff --git a/share/templates/callflow/list.tt b/share/templates/callflow/list.tt
index 63450d0a02..764f7ea6a9 100644
--- a/share/templates/callflow/list.tt
+++ b/share/templates/callflow/list.tt
@@ -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;
diff --git a/share/templates/helpers/datatables.tt b/share/templates/helpers/datatables.tt
index b1da51715c..ecae33cdaa 100644
--- a/share/templates/helpers/datatables.tt
+++ b/share/templates/helpers/datatables.tt
@@ -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,
diff --git a/t/api-billingfees.t b/t/api-billingfees.t
index 3bf43ed25f..8af3d6c06d 100644
--- a/t/api-billingfees.t
+++ b/t/api-billingfees.t
@@ -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");
@@ -351,7 +351,7 @@ my @allfees = ();
delete $fee->{_links};
delete $fee->{_embedded};
$req = HTTP::Request->new('PUT', $uri.'/'.$firstfee);
-
+
# check if it fails without content type
$req->remove_header('Content-Type');
$req->header('Prefer' => "return=minimal");
@@ -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 } ]
diff --git a/t/api-billingprofiles.t b/t/api-billingprofiles.t
index d45699e696..7ac58c3e9c 100644
--- a/t/api-billingprofiles.t
+++ b/t/api-billingprofiles.t
@@ -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(
diff --git a/t/api-contracts.t b/t/api-contracts.t
index cf10628259..2fd7c34295 100644
--- a/t/api-contracts.t
+++ b/t/api-contracts.t
@@ -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
diff --git a/t/api-customercontacts.t b/t/api-customercontacts.t
index 08f18cb74a..c1b99aeeff 100644
--- a/t/api-customercontacts.t
+++ b/t/api-customercontacts.t
@@ -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 } ]
diff --git a/t/api-customers.t b/t/api-customers.t
index 9ce0aee712..152b493fe7 100644
--- a/t/api-customers.t
+++ b/t/api-customers.t
@@ -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
diff --git a/t/api-resellers.t b/t/api-resellers.t
index 25aafbc9d5..397246a810 100644
--- a/t/api-resellers.t
+++ b/t/api-resellers.t
@@ -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");
@@ -279,7 +279,7 @@ my @allresellers = ();
delete $reseller->{_links};
delete $reseller->{_embedded};
$req = HTTP::Request->new('PUT', $uri.'/'.$firstreseller);
-
+
# check if it fails without content type
$req->remove_header('Content-Type');
$req->header('Prefer' => "return=minimal");
@@ -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 } ]
diff --git a/t/api-rewriterulesets.t b/t/api-rewriterulesets.t
new file mode 100644
index 0000000000..c0059d65e4
--- /dev/null
+++ b/t/api-rewriterulesets.t
@@ -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:
diff --git a/t/api-systemcontacts.t b/t/api-systemcontacts.t
index b944c14e49..eb11e3abce 100644
--- a/t/api-systemcontacts.t
+++ b/t/api-systemcontacts.t
@@ -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");
@@ -154,7 +154,7 @@ my @allcontacts = ();
delete $contact->{_embedded};
$req = HTTP::Request->new('PUT', $uri.'/'.$firstcontact);
$req->header('Prefer' => 'return=minimal');
-
+
# check if it fails without content type
$req->remove_header('Content-Type');
$res = $ua->request($req);
@@ -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 } ]
diff --git a/t/api-valid-patch.t b/t/api-valid-patch.t
index f615a69466..e4ea4255d6 100644
--- a/t/api-valid-patch.t
+++ b/t/api-valid-patch.t
@@ -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;