diff --git a/lib/NGCP/Panel/Controller/API/ApplyRewrites.pm b/lib/NGCP/Panel/Controller/API/ApplyRewrites.pm new file mode 100644 index 0000000000..d7873861ab --- /dev/null +++ b/lib/NGCP/Panel/Controller/API/ApplyRewrites.pm @@ -0,0 +1,139 @@ +package NGCP::Panel::Controller::API::ApplyRewrites; +use Sipwise::Base; +use namespace::sweep; +use boolean qw(true); +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Headers qw(); +use HTTP::Status qw(:constants); +use MooseX::ClassAttribute qw(class_has); +use NGCP::Panel::Utils::DateTime; +use Path::Tiny qw(path); +use Safe::Isa qw($_isa); +BEGIN { extends 'Catalyst::Controller::ActionRole'; } +require Catalyst::ActionRole::ACL; +require Catalyst::ActionRole::CheckTrailingSlash; +require Catalyst::ActionRole::HTTPMethods; +require Catalyst::ActionRole::RequireSSL; + +with 'NGCP::Panel::Role::API::ApplyRewrites'; + +class_has 'api_description' => ( + is => 'ro', + isa => 'Str', + default => + 'Applies rewrite rules to a given number according to the given direction. It can for example be used to normalize user input to E164 using callee_in direction, or to denormalize E164 to user output using caller_out.', +); + +class_has 'query_params' => ( + is => 'ro', + isa => 'ArrayRef', + default => sub {[ + ]}, +); + +class_has('resource_name', is => 'ro', default => 'applyrewrites'); +class_has('dispatch_path', is => 'ro', default => '/api/applyrewrites/'); +class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-applyrewrites'); + +__PACKAGE__->config( + action => { + map { $_ => { + ACLDetachTo => '/api/root/invalid_user', + AllowedRole => [qw/admin reseller/], + Args => 0, + Does => [qw(ACL CheckTrailingSlash RequireSSL)], + Method => $_, + Path => __PACKAGE__->dispatch_path, + } } @{ __PACKAGE__->allowed_methods } + }, + action_roles => [qw(HTTPMethods)], +); + +sub auto :Private { + my ($self, $c) = @_; + + $self->set_body($c); + $self->log_request($c); +} + +sub OPTIONS :Allow { + my ($self, $c) = @_; + my $allowed_methods = $self->allowed_methods; + $c->response->headers(HTTP::Headers->new( + Allow => $allowed_methods->join(', '), + Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name, + )); + $c->response->content_type('application/json'); + $c->response->body(JSON::to_json({ methods => $allowed_methods })."\n"); + return; +} + +sub POST :Allow { + my ($self, $c) = @_; + my $guard = $c->model('DB')->txn_scope_guard; + { + my $resource = $self->get_valid_post_data( + c => $c, + media_type => 'application/json', + ); + last unless $resource; + + my $form = $self->get_form($c); + last unless $self->validate_form( + c => $c, + resource => $resource, + form => $form, + exceptions => [qw/subscriber_id/], + ); + + my $subscriber_rs = $c->model('DB')->resultset('voip_subscribers')->search({ + id => $resource->{subscriber_id}, + status => { '!=' => 'terminated' }, + }); + if($c->user->roles eq "admin") { + } elsif($c->user->roles eq "reseller") { + $subscriber_rs = $subscriber_rs->search({ + 'contact.reseller_id' => $c->user->reseller_id, + },{ + join => { contract => 'contact' }, + }); + } + my $subscriber = $subscriber_rs->first; + unless($subscriber) { + $c->log->error("invalid subscriber id $$resource{subscriber_id} for outbound call"); + $self->error($c, HTTP_NOT_FOUND, "Calling subscriber not found."); + last; + } + + my $normalized; + try { + + $normalized = NGCP::Panel::Utils::Subscriber::apply_rewrite( + c => $c, subscriber => $subscriber, + number => $resource->{number}, direction => $resource->{direction}, + ); + } catch($e) { + $c->log->error("failed to rewrite number: $e"); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to rewrite number."); + last; + } + + $guard->commit; + + my $res = '{ "result": "'.$normalized.'" }'."\n"; + + $c->response->status(HTTP_OK); + $c->response->body($res); + } + return; +} + + +sub end : Private { + my ($self, $c) = @_; + + $self->log_response($c); +} + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Controller/API/CallControls.pm b/lib/NGCP/Panel/Controller/API/CallControls.pm index 27b139718b..569278f2f9 100644 --- a/lib/NGCP/Panel/Controller/API/CallControls.pm +++ b/lib/NGCP/Panel/Controller/API/CallControls.pm @@ -90,8 +90,6 @@ sub POST :Allow { exceptions => [qw/subscriber_id/], ); - # TODO: fetch subscriber by id - my $subscriber_rs = $c->model('DB')->resultset('voip_subscribers')->search({ id => $resource->{subscriber_id}, status => { '!=' => 'terminated' }, diff --git a/lib/NGCP/Panel/Controller/API/Tests.pm b/lib/NGCP/Panel/Controller/API/Tests.pm deleted file mode 100644 index 6f93e46cd2..0000000000 --- a/lib/NGCP/Panel/Controller/API/Tests.pm +++ /dev/null @@ -1,85 +0,0 @@ -package NGCP::Panel::Controller::API::Tests; -use Sipwise::Base; -use namespace::sweep; -use boolean qw(true); -use Data::HAL qw(); -use Data::HAL::Link qw(); -use HTTP::Headers qw(); -use HTTP::Status qw(:constants); -use MooseX::ClassAttribute qw(class_has); -use NGCP::Panel::Utils::DateTime; -use Path::Tiny qw(path); -use Safe::Isa qw($_isa); -BEGIN { extends 'Catalyst::Controller::ActionRole'; } -require Catalyst::ActionRole::ACL; -require Catalyst::ActionRole::CheckTrailingSlash; -require Catalyst::ActionRole::HTTPMethods; -require Catalyst::ActionRole::RequireSSL; - -with 'NGCP::Panel::Role::API'; - -class_has 'api_description' => ( - is => 'ro', - isa => 'Str', - default => - 'Defines test (wake-up call) settings for subscribers.', -); - -class_has 'query_params' => ( - is => 'ro', - isa => 'ArrayRef', - default => sub {[ - ]}, -); - -class_has('resource_name', is => 'ro', default => 'tests'); -class_has('dispatch_path', is => 'ro', default => '/api/tests/'); -class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-tests'); - -__PACKAGE__->config( - action => { - map { $_ => { - ACLDetachTo => '/api/root/invalid_user', - AllowedRole => [qw/admin reseller/], - Args => 0, - Does => [qw(ACL CheckTrailingSlash RequireSSL)], - Method => $_, - Path => __PACKAGE__->dispatch_path, - } } @{ __PACKAGE__->allowed_methods } - }, - action_roles => [qw(HTTPMethods)], -); - -sub auto :Private { - my ($self, $c) = @_; - - $self->set_body($c); - $self->log_request($c); -} - -sub GET :Allow { - my ($self, $c) = @_; - { - my $res = ""; - my $subscriber = $c->model('DB')->resultset('voip_subscribers')->find(45); - use NGCP::Panel::Utils::Subscriber; - for my $n (qw/12345 012345 004312345/) { - my $nn = NGCP::Panel::Utils::Subscriber::normalize_callee( - c => $c, subscriber => $subscriber, number => $n, - ); - $res .= "$nn;"; - - } - $c->response->body($res); - return; - } - return; -} - -sub end : Private { - my ($self, $c) = @_; - - $self->log_response($c); -} - -# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/RewriteRule/ApplyAPI.pm b/lib/NGCP/Panel/Form/RewriteRule/ApplyAPI.pm new file mode 100644 index 0000000000..2bd2e3b3a0 --- /dev/null +++ b/lib/NGCP/Panel/Form/RewriteRule/ApplyAPI.pm @@ -0,0 +1,59 @@ +package NGCP::Panel::Form::RewriteRule::ApplyAPI; + +use HTML::FormHandler::Moose; +extends 'HTML::FormHandler'; +use Moose::Util::TypeConstraints; + +use HTML::FormHandler::Widget::Block::Bootstrap; + +has '+widget_wrapper' => ( default => 'Bootstrap' ); +has_field 'submitid' => ( type => 'Hidden' ); +sub build_render_list {[qw/submitid fields actions/]} +sub build_form_element_class {[qw(form-horizontal)]} + +has_field 'subscriber_id' => ( + type => 'PosInteger', + label => 'Subscriber #', + required => 1, + maxlength => 255, + element_attr => { + rel => ['tooltip'], + title => ['The ID of the subscriber to apply rules for'] + }, +); + +has_field 'number' => ( + type => 'Text', + label => 'User or number to rewrite', + required => 1, + element_attr => { + rel => ['tooltip'], + title => ['The username or number to rewrite'] + }, +); + +has_field 'direction' => ( + type => 'Select', + label => 'Direction', + required => 1, + options => [ + { label => 'caller_in', value => 'caller_in' }, + { label => 'callee_in', value => 'callee_in' }, + { label => 'caller_out', value => 'caller_out' }, + { label => 'callee_out', value => 'callee_out' }, + ], + element_attr => { + rel => ['tooltip'], + title => ['The direction rule set to apply, one of caller_in, callee_in, caller_out, callee_out'] + }, +); + +has_block 'fields' => ( + tag => 'div', + class => [qw/modal-body/], + render_list => [qw/subscriber_id number direction/], +); + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Role/API/ApplyRewrites.pm b/lib/NGCP/Panel/Role/API/ApplyRewrites.pm new file mode 100644 index 0000000000..33cecb6984 --- /dev/null +++ b/lib/NGCP/Panel/Role/API/ApplyRewrites.pm @@ -0,0 +1,26 @@ +package NGCP::Panel::Role::API::ApplyRewrites; +use Moose::Role; +use Sipwise::Base; +with 'NGCP::Panel::Role::API' => { + -alias =>{ item_rs => '_item_rs', }, + -excludes => [ 'item_rs' ], +}; + +use boolean qw(true); +use TryCatch; +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Status qw(:constants); + +use NGCP::Panel::Form::RewriteRule::ApplyAPI; + +sub item_rs { +} + +sub get_form { + my ($self, $c) = @_; + return NGCP::Panel::Form::RewriteRule::ApplyAPI->new; +} + +1; +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Utils/Subscriber.pm b/lib/NGCP/Panel/Utils/Subscriber.pm index c456affe96..2bcf2730f3 100644 --- a/lib/NGCP/Panel/Utils/Subscriber.pm +++ b/lib/NGCP/Panel/Utils/Subscriber.pm @@ -809,20 +809,25 @@ sub prepare_alias_select { $params->{alias_select} = encode_json(\@alias_options); } -sub normalize_callee { +sub apply_rewrite { my (%params) = @_; my $c = $params{c}; my $subscriber = $params{subscriber}; my $callee = $params{number}; + my $dir = $params{direction}; + return $callee unless $dir ~~ [qw/caller_in callee_in caller_out callee_out/]; + + my ($field, $direction) = split /_/, $dir; + $dir = "rewrite_".$dir."_dpid"; my $rwr_rs = NGCP::Panel::Utils::Preferences::get_usr_preference_rs( - c => $c, attribute => 'rewrite_callee_in_dpid', + c => $c, attribute => $dir, prov_subscriber => $subscriber->provisioning_voip_subscriber, ); unless($rwr_rs->count) { $rwr_rs = NGCP::Panel::Utils::Preferences::get_dom_preference_rs( - c => $c, attribute => 'rewrite_callee_in_dpid', + c => $c, attribute => $dir, prov_domain => $subscriber->provisioning_voip_subscriber->domain, ); } @@ -831,9 +836,9 @@ sub normalize_callee { } my $rule_rs = $c->model('DB')->resultset('voip_rewrite_rules')->search({ - 'ruleset.callee_in_dpid' => $rwr_rs->first->value, - direction => 'in', - field => 'callee', + 'ruleset.'.$field.'_'.$direction.'_dpid' => $rwr_rs->first->value, + direction => $direction, + field => $field, }, { join => 'ruleset', order_by => { -asc => 'priority' } @@ -843,14 +848,14 @@ sub normalize_callee { my $match = $r->match_pattern; my $replace = $r->replace_pattern; - print ">>>>>>>>>>> match=$match, replace=$replace\n"; + #print ">>>>>>>>>>> match=$match, replace=$replace\n"; for my $field($match, $replace) { - print ">>>>>>>>>>> normalizing $field\n"; + #print ">>>>>>>>>>> normalizing $field\n"; my @avps = (); @avps = ($field =~ /\$avp\(s:caller_([^\)]+)\)/g); use Data::Printer; p @avps; for my $avp(@avps) { - print ">>>>>>>>>> checking avp $avp\n"; + #print ">>>>>>>>>> checking avp $avp\n"; if(!exists $cache->{$avp}) { my $pref_rs = NGCP::Panel::Utils::Preferences::get_usr_preference_rs( c => $c, attribute => $avp, @@ -867,14 +872,19 @@ sub normalize_callee { } my $val = $cache->{$avp}; $field =~ s/\$avp\(s:caller_$avp\)/$val/g; - print ">>>>>>>>>>> normalized $field\n"; + #print ">>>>>>>>>>> normalized $field\n"; } } $replace =~ s/\\(\d{1})/\$$1/g; + #print ">>>>>>>>>>> final match=$match, replace=$replace, applying to $callee\n"; - print ">>>>>>>>>>> final match=$match, replace=$replace, applying to $callee\n"; - $callee =~ s/$match/$replace/eeg; - print ">>>>>>>>>>> done, match=$match, replace=$replace, callee is $callee\n"; + $replace =~ s/\"/\\"/g; + $replace = qq{"$replace"}; + if($callee =~ s/$match/$replace/eeg) { + # we only process one match + last; + } + #print ">>>>>>>>>>> done, match=$match, replace=$replace, callee is $callee\n"; }