From 72ae4a5471fcad689ab657645eacdfab2a7d27ea Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Fri, 21 Mar 2014 11:23:27 +0100 Subject: [PATCH 01/18] MT#4369 optimize mysql performance on callflow page distinct is faster than group by here drawback: we cannot select min(timestamp) anymore query is still very slow, when filter is used --- lib/NGCP/Panel/Controller/Callflow.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/NGCP/Panel/Controller/Callflow.pm b/lib/NGCP/Panel/Controller/Callflow.pm index f9981f815c..3ffdddbf38 100644 --- a/lib/NGCP/Panel/Controller/Callflow.pm +++ b/lib/NGCP/Panel/Controller/Callflow.pm @@ -21,7 +21,7 @@ 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 => "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') }, @@ -46,7 +46,8 @@ 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' + select => [\'distinct(call_id)', 'caller_uuid', 'callee_uuid', 'cseq_method', 'timestamp'], + as => ['call_id', 'caller_uuid', 'callee_uuid', 'cseq_method', 'timestamp'], }); NGCP::Panel::Utils::Datatables::process( $c, From 720e70b9397857565dc78766810c385164a5b813 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Fri, 21 Mar 2014 15:46:35 +0100 Subject: [PATCH 02/18] MT#4369 optimize callflow using a custom query we need to disable the sorting option on the columns though --- lib/NGCP/Panel/Controller/Callflow.pm | 25 ++++-- lib/NGCP/Panel/Utils/Datatables.pm | 116 ++++++++++++++++++-------- 2 files changed, 96 insertions(+), 45 deletions(-) diff --git a/lib/NGCP/Panel/Controller/Callflow.pm b/lib/NGCP/Panel/Controller/Callflow.pm index 3ffdddbf38..c96a799282 100644 --- a/lib/NGCP/Panel/Controller/Callflow.pm +++ b/lib/NGCP/Panel/Controller/Callflow.pm @@ -28,10 +28,6 @@ sub root :Chained('/') :PathPart('callflow') :CaptureArgs(0) { { 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,13 +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,{ - select => [\'distinct(call_id)', 'caller_uuid', 'callee_uuid', 'cseq_method', 'timestamp'], - as => ['call_id', 'caller_uuid', 'callee_uuid', 'cseq_method', 'timestamp'], - }); + + my $calls_rs_cb = sub { + my %params = @_; + my $total_count = $c->model('DB')->resultset('messages')->search(undef,{select => \'distinct(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/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: From a6ae6b1a84c8beab9e3e19b43f3762d860c3a09c Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Mon, 24 Mar 2014 09:04:09 +0100 Subject: [PATCH 03/18] MT#4369 disable sorting on callflow page as it has no effect, because of our optimized query --- share/templates/callflow/list.tt | 1 + share/templates/helpers/datatables.tt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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, From 83fa4552f264235a86cd6aa5769fc38f746ee163 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Mon, 24 Mar 2014 09:18:25 +0100 Subject: [PATCH 04/18] MT#4369 use min_timestamp to really display the time, the call started --- lib/NGCP/Panel/Controller/Callflow.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/NGCP/Panel/Controller/Callflow.pm b/lib/NGCP/Panel/Controller/Callflow.pm index c96a799282..d483f8db49 100644 --- a/lib/NGCP/Panel/Controller/Callflow.pm +++ b/lib/NGCP/Panel/Controller/Callflow.pm @@ -21,7 +21,7 @@ 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') }, + { 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') }, From 11e421dc08027214f0895d7fd77ac03edc713054 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Thu, 13 Mar 2014 08:26:36 +0100 Subject: [PATCH 05/18] MT#6195 API: rename rewrite -> rewriterulesets --- lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm | 8 ++++---- lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm | 12 ++++++------ lib/NGCP/Panel/Role/API/RewriteRuleSets.pm | 6 ------ lib/NGCP/Panel/Role/API/RewriteRules.pm | 10 ++-------- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm index 6e41c83c61..aef3f4f19e 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm @@ -26,9 +26,9 @@ class_has 'api_description' => ( 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 => { @@ -65,7 +65,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), diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm index 635ac53d9a..b875b95ce5 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm @@ -19,9 +19,9 @@ 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 +52,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 @@ -118,7 +118,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 +158,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); diff --git a/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm index a576cc570d..a61ea52413 100644 --- a/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm +++ b/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm @@ -69,12 +69,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"; } 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 { From ede3d43a4280ac391d11351ad6ea0c07a2d73503 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Thu, 13 Mar 2014 09:38:10 +0100 Subject: [PATCH 06/18] MT#6195 API: rewriterule/rewriteruleset: POST --- .../Panel/Controller/API/RewriteRuleSets.pm | 57 ++++++++++++++++++ lib/NGCP/Panel/Controller/API/RewriteRules.pm | 58 +++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm index aef3f4f19e..1263e28209 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm @@ -122,6 +122,63 @@ 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 $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; + } + + $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/RewriteRules.pm b/lib/NGCP/Panel/Controller/API/RewriteRules.pm index d9ce48b0ca..ba472c216d 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRules.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRules.pm @@ -127,6 +127,64 @@ 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($resource->{direction} && ($resource->{direction} eq "in" || $resource->{direction} eq "out") ) { + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'direction', must be 'in' or 'out'."); + last; + } + unless($resource->{field} && ($resource->{field} eq "callee" || $resource->{field} eq "caller") ) { + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'field', must be 'callee' or 'caller'."); + last; + } + 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) = @_; From c7c1e012a9f53c72c5a76b51754cfed94ddaea07 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Mon, 17 Mar 2014 13:03:08 +0100 Subject: [PATCH 07/18] MT#6195 API: adapt RewriteRules to search framework see MT#6283 --- .../Panel/Controller/API/RewriteRuleSets.pm | 41 ++++++++++++++++++- .../Controller/API/RewriteRuleSetsItem.pm | 1 - lib/NGCP/Panel/Controller/API/RewriteRules.pm | 36 ++++++++++++---- .../Panel/Controller/API/RewriteRulesItem.pm | 1 - 4 files changed, 69 insertions(+), 10 deletions(-) diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm index 1263e28209..92fbae2bd4 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm @@ -23,7 +23,46 @@ 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 => 'rewriterulesets'); diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm index b875b95ce5..607723e2f4 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.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::RewriteRuleSets'; class_has('resource_name', is => 'ro', default => 'rewriterulesets'); diff --git a/lib/NGCP/Panel/Controller/API/RewriteRules.pm b/lib/NGCP/Panel/Controller/API/RewriteRules.pm index ba472c216d..1eb812862d 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, diff --git a/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm index a61830bc77..f200a991ef 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'); From 63aa4e35f762df28fb5a31db2036962bbc8c1be7 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Thu, 13 Mar 2014 09:57:26 +0100 Subject: [PATCH 08/18] MT#6195 API: rewriterule/rewriteruleset basic tests --- t/api-rewriterulesets.t | 398 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 t/api-rewriterulesets.t diff --git a/t/api-rewriterulesets.t b/t/api-rewriterulesets.t new file mode 100644 index 0000000000..19b6532e1d --- /dev/null +++ b/t/api-rewriterulesets.t @@ -0,0 +1,398 @@ +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"); + ok($res->header('Accept-Post') eq "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}; + ok($selfuri eq $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 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"); + + # 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: From edd904ce45f1f1da187171b0da7b956e067b4c1a Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Fri, 14 Mar 2014 12:43:04 +0100 Subject: [PATCH 09/18] MT#6295 API: fix tests Prefer header no longer required --- lib/NGCP/Panel/Role/API.pm | 7 ++++--- t/api-billingfees.t | 8 +------- t/api-billingprofiles.t | 5 ----- t/api-contracts.t | 6 ------ t/api-customercontacts.t | 5 ----- t/api-customers.t | 6 ------ t/api-resellers.t | 7 +------ t/api-rewriterulesets.t | 6 ------ t/api-systemcontacts.t | 8 +------- t/api-valid-patch.t | 21 ++++++++------------- 10 files changed, 15 insertions(+), 64 deletions(-) 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/t/api-billingfees.t b/t/api-billingfees.t index 3bf43ed25f..a58f844cc6 100644 --- a/t/api-billingfees.t +++ b/t/api-billingfees.t @@ -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"); diff --git a/t/api-billingprofiles.t b/t/api-billingprofiles.t index d45699e696..a3a8b6de02 100644 --- a/t/api-billingprofiles.t +++ b/t/api-billingprofiles.t @@ -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); diff --git a/t/api-contracts.t b/t/api-contracts.t index cf10628259..93d7dba08b 100644 --- a/t/api-contracts.t +++ b/t/api-contracts.t @@ -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"); diff --git a/t/api-customercontacts.t b/t/api-customercontacts.t index 08f18cb74a..eaf0fd3f53 100644 --- a/t/api-customercontacts.t +++ b/t/api-customercontacts.t @@ -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); diff --git a/t/api-customers.t b/t/api-customers.t index 9ce0aee712..24bfadff7c 100644 --- a/t/api-customers.t +++ b/t/api-customers.t @@ -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"); diff --git a/t/api-resellers.t b/t/api-resellers.t index 25aafbc9d5..717a200889 100644 --- a/t/api-resellers.t +++ b/t/api-resellers.t @@ -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); diff --git a/t/api-rewriterulesets.t b/t/api-rewriterulesets.t index 19b6532e1d..7147ad68a7 100644 --- a/t/api-rewriterulesets.t +++ b/t/api-rewriterulesets.t @@ -304,17 +304,11 @@ my @allrules = (); $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"); diff --git a/t/api-systemcontacts.t b/t/api-systemcontacts.t index b944c14e49..f8e960e9af 100644 --- a/t/api-systemcontacts.t +++ b/t/api-systemcontacts.t @@ -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"); 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; From 31e6c5fa715f37347799bba2cd09d827d7503c82 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Fri, 14 Mar 2014 14:18:00 +0100 Subject: [PATCH 10/18] MT#6195 API: ability to handle rules directly in rewriteruleset --- .../Panel/Controller/API/RewriteRuleSets.pm | 28 ++++++++++++++++++ .../Controller/API/RewriteRuleSetsItem.pm | 2 +- lib/NGCP/Panel/Role/API/RewriteRuleSets.pm | 29 ++++++++++++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm index 92fbae2bd4..ed1278114d 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSets.pm @@ -184,6 +184,8 @@ sub POST :Allow { last; } + my $rewriterules = $resource->{rewriterules}; + my $form = $self->get_form($c); last unless $self->validate_form( c => $c, @@ -209,6 +211,32 @@ sub POST :Allow { 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); diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm index 607723e2f4..9a5d7bc5ed 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm @@ -102,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; diff --git a/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm b/lib/NGCP/Panel/Role/API/RewriteRuleSets.pm index a61ea52413..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; } @@ -96,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, From 6f368e820fb5af7af367c9a0f419609461b9bf0b Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Tue, 18 Mar 2014 09:12:48 +0100 Subject: [PATCH 11/18] MT#6195 API: rewriterule/rewriteruleset: DELETE and remove some redundant checks (done by formhandler) --- .../Controller/API/RewriteRuleSetsItem.pm | 22 +++++++++++++++++++ lib/NGCP/Panel/Controller/API/RewriteRules.pm | 8 ------- .../Panel/Controller/API/RewriteRulesItem.pm | 21 ++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm index 9a5d7bc5ed..9f8d0aebfc 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRuleSetsItem.pm @@ -169,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 1eb812862d..8b6b1a584d 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRules.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRules.pm @@ -161,14 +161,6 @@ sub POST :Allow { ); last unless $resource; - unless($resource->{direction} && ($resource->{direction} eq "in" || $resource->{direction} eq "out") ) { - $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'direction', must be 'in' or 'out'."); - last; - } - unless($resource->{field} && ($resource->{field} eq "callee" || $resource->{field} eq "caller") ) { - $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'field', must be 'callee' or 'caller'."); - last; - } my $set_id = delete $resource->{set_id}; # keep this, cause formhandler doesn't know it my $form = $self->get_form($c); diff --git a/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm b/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm index f200a991ef..1992450cf0 100644 --- a/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm +++ b/lib/NGCP/Panel/Controller/API/RewriteRulesItem.pm @@ -169,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) = @_; From 46e5f4431c95159c8fb43cf5b03a50c170a86682 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Wed, 19 Mar 2014 18:10:19 +0100 Subject: [PATCH 12/18] MT#6195 api tests adjustments - use is --- t/api-billingfees.t | 18 +++++++++--------- t/api-billingprofiles.t | 16 ++++++++-------- t/api-contracts.t | 20 ++++++++++---------- t/api-customercontacts.t | 12 ++++++------ t/api-customers.t | 22 +++++++++++----------- t/api-resellers.t | 24 ++++++++++++------------ t/api-rewriterulesets.t | 4 ++-- t/api-systemcontacts.t | 8 ++++---- 8 files changed, 62 insertions(+), 62 deletions(-) diff --git a/t/api-billingfees.t b/t/api-billingfees.t index a58f844cc6..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"); @@ -399,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 a3a8b6de02..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"); @@ -240,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 93d7dba08b..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"); @@ -306,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( @@ -366,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 eaf0fd3f53..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"); @@ -238,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 24bfadff7c..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"); @@ -340,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( @@ -406,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 717a200889..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"); @@ -328,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 index 7147ad68a7..c0059d65e4 100644 --- a/t/api-rewriterulesets.t +++ b/t/api-rewriterulesets.t @@ -26,7 +26,7 @@ $ua->ssl_opts( $req = HTTP::Request->new('OPTIONS', $uri.'/api/rewriterulesets/'); $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-rewriterulesets", "check Accept-Post header in options response"); + 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"); @@ -204,7 +204,7 @@ my @allrules = (); is($res->code, 200, "fetch rules 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"); diff --git a/t/api-systemcontacts.t b/t/api-systemcontacts.t index f8e960e9af..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"); @@ -207,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 } ] From a1309af52a6792fd2248ef2532e2cd6f70bc2ca1 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Wed, 19 Mar 2014 18:39:42 +0100 Subject: [PATCH 13/18] MT#6195 api tests: provide ./Build test_api and the option --no-junit --- Build.PL | 1 + inc/Local/Module/Build.pm | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) 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/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(); From 9274c0a82d7edda7be34ef1dcf6576b669e30292 Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Thu, 27 Mar 2014 13:23:11 +0100 Subject: [PATCH 14/18] MT#5837 Fix syntax error in ru translation. --- lib/NGCP/Panel/I18N/ru.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 консоли." From dde8616821b9492a3378b0857631b77d38628720 Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Thu, 27 Mar 2014 13:28:56 +0100 Subject: [PATCH 15/18] MT#6425 Improve localization script. - Remove separate config file (only causes overhead in packaging) - Don't generate fuzzy translations during msgmerge --- etc/i18n.inc | 13 ------------- script/extract_i18n.sh | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 etc/i18n.inc 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/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 From 2a850873703a5d0ac16b3f11b325210ba76ab376 Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Thu, 27 Mar 2014 13:34:01 +0100 Subject: [PATCH 16/18] MT#6425 Package localization tools. --- debian/control | 1 + debian/ngcp-panel.install | 1 + 2 files changed, 2 insertions(+) diff --git a/debian/control b/debian/control index cceb13fb4f..e708a31317 100644 --- a/debian/control +++ b/debian/control @@ -112,6 +112,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 From 451b6a89e634678faccea3e75875fb3f10ef3097 Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Thu, 27 Mar 2014 13:35:07 +0100 Subject: [PATCH 17/18] MT#6425 Install gettext dependency. --- debian/control | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index e708a31317..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, From d313bc75596639e49a02552bb0f3a38f1c397c32 Mon Sep 17 00:00:00 2001 From: Gerhard Jungwirth Date: Tue, 25 Mar 2014 09:54:14 +0100 Subject: [PATCH 18/18] MT#4369 callflow: count total correctly this group_by is ok, because it is optimized by mysql and the index on call_id is still used --- lib/NGCP/Panel/Controller/Callflow.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/NGCP/Panel/Controller/Callflow.pm b/lib/NGCP/Panel/Controller/Callflow.pm index d483f8db49..32a550be16 100644 --- a/lib/NGCP/Panel/Controller/Callflow.pm +++ b/lib/NGCP/Panel/Controller/Callflow.pm @@ -44,7 +44,7 @@ sub ajax :Chained('root') :PathPart('ajax') :Args(0) { my $calls_rs_cb = sub { my %params = @_; - my $total_count = $c->model('DB')->resultset('messages')->search(undef,{select => \'distinct(call_id)'})->count; + 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}.'%' : '';