and add test script Change-Id: I70407cec591b8d719582dd476e7586496a795d69changes/38/19338/16
parent
ab10122651
commit
469d481b9f
@ -1,161 +1,166 @@
|
|||||||
package NGCP::Panel::Controller::API::NumbersItem;
|
package NGCP::Panel::Controller::API::NumbersItem;
|
||||||
use NGCP::Panel::Utils::Generic qw(:all);
|
|
||||||
|
|
||||||
use Sipwise::Base;
|
use Sipwise::Base;
|
||||||
|
use NGCP::Panel::Utils::Generic qw(:all);
|
||||||
|
|
||||||
|
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::Numbers/;
|
||||||
|
|
||||||
use boolean qw(true);
|
|
||||||
use Data::HAL qw();
|
|
||||||
use Data::HAL::Link qw();
|
|
||||||
use HTTP::Headers qw();
|
|
||||||
use HTTP::Status qw(:constants);
|
use HTTP::Status qw(:constants);
|
||||||
|
use JSON::Types;
|
||||||
|
|
||||||
use NGCP::Panel::Utils::ValidateJSON qw();
|
__PACKAGE__->set_config({
|
||||||
use NGCP::Panel::Utils::DateTime;
|
allowed_roles => {
|
||||||
use Path::Tiny qw(path);
|
Default => [qw/admin reseller subscriberadmin/],
|
||||||
use Safe::Isa qw($_isa);
|
Journal => [qw/admin reseller/],
|
||||||
require Catalyst::ActionRole::ACL;
|
}
|
||||||
require NGCP::Panel::Role::HTTPMethods;
|
});
|
||||||
require Catalyst::ActionRole::RequireSSL;
|
|
||||||
|
|
||||||
sub allowed_methods{
|
sub allowed_methods{
|
||||||
return [qw/GET PUT PATCH OPTIONS HEAD/];
|
return [qw/GET PUT PATCH OPTIONS HEAD/];
|
||||||
}
|
}
|
||||||
|
|
||||||
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::Numbers/;
|
|
||||||
|
|
||||||
sub resource_name{
|
|
||||||
return 'numbers';
|
|
||||||
}
|
|
||||||
|
|
||||||
sub dispatch_path{
|
|
||||||
return '/api/numbers/';
|
|
||||||
}
|
|
||||||
|
|
||||||
sub relation{
|
|
||||||
return 'http://purl.org/sipwise/ngcp-api/#rel-numbers';
|
|
||||||
}
|
|
||||||
|
|
||||||
sub journal_query_params {
|
sub journal_query_params {
|
||||||
my($self,$query_params) = @_;
|
my($self,$query_params) = @_;
|
||||||
return $self->get_journal_query_params($query_params);
|
return $self->get_journal_query_params($query_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
__PACKAGE__->set_config({
|
sub get_journal_methods{
|
||||||
allowed_roles => {
|
return [qw/handle_item_base_journal handle_journals_get handle_journalsitem_get handle_journals_options handle_journalsitem_options handle_journals_head handle_journalsitem_head handle_journalsitem_put handle_journalsitem_patch/];
|
||||||
Default => [qw/admin reseller subscriberadmin/],
|
}
|
||||||
Journal => [qw/admin reseller/],
|
|
||||||
}
|
sub update_item_model {
|
||||||
});
|
my ($self, $c, $item, $old_resource, $resource, $form) = @_;
|
||||||
|
my $schema = $c->model('DB');
|
||||||
|
|
||||||
sub GET :Allow {
|
# we maybe want to remove such checks to compare readonly fields:
|
||||||
my ($self, $c, $id) = @_;
|
foreach my $field(qw/cc ac sn is_primary/) {
|
||||||
{
|
unless(($old_resource->{$field} // '') eq ($resource->{$field} // '')
|
||||||
last unless $self->valid_id($c, $id);
|
or ('JSON::false' eq ref $resource->{$field} and $old_resource->{$field} == 0)
|
||||||
my $item = $self->item_by_id($c, $id);
|
or ('JSON::true' eq ref $resource->{$field} and $old_resource->{$field} == 1)) {
|
||||||
last unless $self->resource_exists($c, number => $item);
|
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Field '$field' is not allowed to be updated via this API endpoint, use /api/subscriber/\$id instead.");
|
||||||
|
|
||||||
my $hal = $self->hal_from_item($c, $item);
|
|
||||||
|
|
||||||
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
|
|
||||||
(map { # XXX Data::HAL must be able to generate links with multiple relations
|
|
||||||
s|rel="(http://purl.org/sipwise/ngcp-api/#rel-resellers)"|rel="item $1"|r =~
|
|
||||||
s/rel=self/rel="item self"/r;
|
|
||||||
} $hal->http_headers),
|
|
||||||
), $hal->as_json);
|
|
||||||
$c->response->headers($response->headers);
|
|
||||||
$c->response->body($response->content);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sub PUT :Allow {
|
|
||||||
my ($self, $c, $id) = @_;
|
|
||||||
my $guard = $c->model('DB')->txn_scope_guard;
|
|
||||||
{
|
|
||||||
my $preference = $self->require_preference($c);
|
|
||||||
last unless $preference;
|
|
||||||
|
|
||||||
my $item = $self->item_by_id($c, $id);
|
$form //= $self->get_form($c);
|
||||||
last unless $self->resource_exists($c, number => $item);
|
return unless $self->validate_form(
|
||||||
my $resource = $self->get_valid_put_data(
|
|
||||||
c => $c,
|
c => $c,
|
||||||
id => $id,
|
form => $form,
|
||||||
media_type => 'application/json',
|
resource => $resource,
|
||||||
);
|
);
|
||||||
last unless $resource;
|
|
||||||
my $old_resource = { $item->get_inflated_columns };
|
|
||||||
|
|
||||||
my $form = $self->get_form($c);
|
|
||||||
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
|
|
||||||
last unless $item;
|
|
||||||
|
|
||||||
$guard->commit;
|
my $sub = $schema->resultset('voip_subscribers')
|
||||||
|
->find($resource->{subscriber_id});
|
||||||
if ('minimal' eq $preference) {
|
unless($sub) {
|
||||||
$c->response->status(HTTP_NO_CONTENT);
|
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'subscriber_id', does not exist.");
|
||||||
$c->response->header(Preference_Applied => 'return=minimal');
|
return;
|
||||||
$c->response->body(q());
|
|
||||||
} else {
|
|
||||||
my $hal = $self->hal_from_item($c, $item, $form);
|
|
||||||
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
|
|
||||||
$hal->http_headers,
|
|
||||||
), $hal->as_json);
|
|
||||||
$c->response->headers($response->headers);
|
|
||||||
$c->response->header(Preference_Applied => 'return=representation');
|
|
||||||
$c->response->body($response->content);
|
|
||||||
}
|
}
|
||||||
|
if($c->user->roles eq "subscriberadmin" && $sub->contract_id != $c->user->account_id) {
|
||||||
|
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'subscriber_id', does not exist.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
my $old_sub = $schema->resultset('voip_subscribers')->find($old_resource->{subscriber_id});
|
||||||
|
if($old_sub->primary_number_id == $old_resource->{id}) {
|
||||||
|
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Cannot reassign primary number, already at subscriber ".$old_sub->id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub PATCH :Allow {
|
try {
|
||||||
my ($self, $c, $id) = @_;
|
|
||||||
my $guard = $c->model('DB')->txn_scope_guard;
|
|
||||||
{
|
|
||||||
my $preference = $self->require_preference($c);
|
|
||||||
last unless $preference;
|
|
||||||
|
|
||||||
my $json = $self->get_valid_patch_data(
|
# capture old subscriber's aliases
|
||||||
|
my $old_aliases_before = NGCP::Panel::Utils::Events::get_aliases_snapshot(
|
||||||
c => $c,
|
c => $c,
|
||||||
id => $id,
|
schema => $schema,
|
||||||
media_type => 'application/json-patch+json',
|
subscriber => $old_sub,
|
||||||
);
|
);
|
||||||
last unless $json;
|
|
||||||
|
|
||||||
my $item = $self->item_by_id($c, $id);
|
# capture new subscriber's aliases
|
||||||
last unless $self->resource_exists($c, number => $item);
|
my $aliases_before = NGCP::Panel::Utils::Events::get_aliases_snapshot(
|
||||||
my $old_resource = { $item->get_inflated_columns };
|
c => $c,
|
||||||
my $resource = $self->apply_patch($c, $old_resource, $json);
|
schema => $schema,
|
||||||
last unless $resource;
|
subscriber => $sub,
|
||||||
|
);
|
||||||
|
|
||||||
my $form = $self->get_form($c);
|
my $oldalias = [$old_sub->voip_numbers->all];
|
||||||
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
|
$oldalias = [ map {
|
||||||
last unless $item;
|
if($_->id == $old_sub->primary_number_id) {
|
||||||
|
# filter primary number
|
||||||
|
();
|
||||||
|
} else {
|
||||||
|
if($_->id == $item->id) {
|
||||||
|
# filter number we're about to remove
|
||||||
|
();
|
||||||
|
} else {
|
||||||
|
# otherwise keep number
|
||||||
|
{ e164 => { cc => $_->cc, ac => $_->ac, sn => $_->sn } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} @{ $oldalias } ];
|
||||||
|
|
||||||
$guard->commit;
|
NGCP::Panel::Utils::Subscriber::update_subscriber_numbers(
|
||||||
|
c => $c,
|
||||||
|
schema => $schema,
|
||||||
|
alias_numbers => $oldalias,
|
||||||
|
reseller_id => $old_sub->contract->contact->reseller_id,
|
||||||
|
subscriber_id => $old_sub->id,
|
||||||
|
);
|
||||||
|
|
||||||
if ('minimal' eq $preference) {
|
my $newalias = [ $sub->voip_numbers->all ];
|
||||||
$c->response->status(HTTP_NO_CONTENT);
|
$newalias = [ map {
|
||||||
$c->response->header(Preference_Applied => 'return=minimal');
|
if($_->id == $sub->primary_number_id) {
|
||||||
$c->response->body(q());
|
# filter primary number
|
||||||
|
();
|
||||||
} else {
|
} else {
|
||||||
my $hal = $self->hal_from_item($c, $item, $form);
|
if($_->id == $item->id) {
|
||||||
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
|
# filter number we're about to remove
|
||||||
$hal->http_headers,
|
();
|
||||||
), $hal->as_json);
|
} else {
|
||||||
$c->response->headers($response->headers);
|
# otherwise keep number
|
||||||
$c->response->header(Preference_Applied => 'return=representation');
|
{ e164 => { cc => $_->cc, ac => $_->ac, sn => $_->sn } };
|
||||||
$c->response->body($response->content);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} @{ $newalias } ];
|
||||||
|
push @{ $newalias }, { e164 => { cc => $item->cc, ac => $item->ac, sn => $item->sn } };
|
||||||
|
|
||||||
|
NGCP::Panel::Utils::Subscriber::update_subscriber_numbers(
|
||||||
|
c => $c,
|
||||||
|
schema => $schema,
|
||||||
|
alias_numbers => $newalias,
|
||||||
|
reseller_id => $sub->contract->contact->reseller_id,
|
||||||
|
subscriber_id => $sub->id,
|
||||||
|
);
|
||||||
|
|
||||||
|
# edr events for old sub
|
||||||
|
my $old_sub_profile = $old_sub->provisioning_voip_subscriber->profile_id;
|
||||||
|
NGCP::Panel::Utils::Events::insert_profile_events(
|
||||||
|
c => $c, schema => $schema, subscriber_id => $old_sub->id,
|
||||||
|
old => $old_sub_profile, new => $old_sub_profile,
|
||||||
|
%$old_aliases_before,
|
||||||
|
);
|
||||||
|
|
||||||
|
# edr events for new sub
|
||||||
|
my $new_sub_profile = $sub->provisioning_voip_subscriber->profile_id;
|
||||||
|
NGCP::Panel::Utils::Events::insert_profile_events(
|
||||||
|
c => $c, schema => $schema, subscriber_id => $sub->id,
|
||||||
|
old => $new_sub_profile, new => $new_sub_profile,
|
||||||
|
%$aliases_before,
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch($e) {
|
||||||
|
$c->log->error("failed to update number: $e");
|
||||||
|
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update number.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_journal_methods{
|
# reload item, in case the id changed (which shouldn't happen)
|
||||||
return [qw/handle_item_base_journal handle_journals_get handle_journalsitem_get handle_journals_options handle_journalsitem_options handle_journals_head handle_journalsitem_head handle_journalsitem_put handle_journalsitem_patch/];
|
$item = $self->_item_rs($c)->find({
|
||||||
|
cc => $item->cc, ac => $item->ac, sn => $item->sn
|
||||||
|
});
|
||||||
|
|
||||||
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
# vim: set tabstop=4 expandtab:
|
# vim: set tabstop=4 expandtab:
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Test::Collection;
|
||||||
|
use Test::FakeData;
|
||||||
|
use Test::More;
|
||||||
|
use Data::Dumper;
|
||||||
|
use Clone qw/clone/;
|
||||||
|
|
||||||
|
#use NGCP::Panel::Utils::Subscriber;
|
||||||
|
|
||||||
|
my $test_machine = Test::Collection->new(
|
||||||
|
name => 'numbers',
|
||||||
|
);
|
||||||
|
my $fake_data = Test::FakeData->new;
|
||||||
|
|
||||||
|
$fake_data->set_data_from_script({
|
||||||
|
'numbers' => {
|
||||||
|
'data' => {
|
||||||
|
subscriber_id => sub { return shift->get_id('subscribers',@_); },
|
||||||
|
cc => 1,
|
||||||
|
ac => 1,
|
||||||
|
sn => 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
my $fake_data_processed = $fake_data->process('numbers');
|
||||||
|
$test_machine->DATA_ITEM_STORE($fake_data_processed);
|
||||||
|
$test_machine->form_data_item();
|
||||||
|
|
||||||
|
{
|
||||||
|
my $ticket = '32913';
|
||||||
|
my $time = time();
|
||||||
|
my $subscriber_test_machine = Test::Collection->new(
|
||||||
|
name => 'subscribers',
|
||||||
|
);
|
||||||
|
$subscriber_test_machine->DATA_ITEM_STORE($fake_data->process('subscribers'));
|
||||||
|
my $subscriber1 = $fake_data->create('subscribers')->[0];
|
||||||
|
my $subscriber2 = $fake_data->create('subscribers')->[0];
|
||||||
|
#print Dumper $subscriber1;
|
||||||
|
$subscriber1->{content}->{alias_numbers} = [
|
||||||
|
{ cc=> '111', ac => $ticket, sn => $time },
|
||||||
|
{ cc=> '112', ac => $ticket, sn => $time },
|
||||||
|
{ cc=> '113', ac => $ticket, sn => $time },
|
||||||
|
];
|
||||||
|
$subscriber2->{content}->{alias_numbers} = [
|
||||||
|
{ cc=> '211', ac => $ticket, sn => $time },
|
||||||
|
{ cc=> '212', ac => $ticket, sn => $time },
|
||||||
|
{ cc=> '213', ac => $ticket, sn => $time },
|
||||||
|
];
|
||||||
|
my ($res,$content,$request);
|
||||||
|
($res,$content,$request) = $subscriber_test_machine->request_put(@{$subscriber1}{qw/content location/});
|
||||||
|
($res,$content,$request) = $subscriber_test_machine->request_put(@{$subscriber2}{qw/content location/});
|
||||||
|
my ($alias1) = $test_machine->get_item_hal('numbers', '/api/numbers/?type=alias&subscriber_id='.$subscriber1->{content}->{id});
|
||||||
|
my ($alias2) = $test_machine->get_item_hal('numbers','/api/numbers/?type=alias&subscriber_id='.$subscriber2->{content}->{id});
|
||||||
|
|
||||||
|
test_numbers_reassign($alias1,$alias2,$subscriber1,$subscriber2);
|
||||||
|
|
||||||
|
my $pbxsubscriberadmin = $fake_data->create('subscribers')->[0];
|
||||||
|
($res) = $subscriber_test_machine->request_patch([
|
||||||
|
{ op => 'replace', path => '/administrative', value => 1 },
|
||||||
|
{ op => 'replace', path => '/webpassword', value => 'pbxadminpwd' },
|
||||||
|
{ op => 'replace', path => '/password', value => 'pbxadminpwd' }
|
||||||
|
] , $pbxsubscriberadmin->{location});
|
||||||
|
$subscriber_test_machine->http_code_msg(200, "PATCH for /pbxsubscriberadmin/", $res);
|
||||||
|
$pbxsubscriberadmin = $subscriber_test_machine->get_item_hal('subscribers', $pbxsubscriberadmin->{location});
|
||||||
|
|
||||||
|
$test_machine->set_subscriber_credentials($pbxsubscriberadmin->{content});
|
||||||
|
$test_machine->runas('subscriber');
|
||||||
|
|
||||||
|
test_numbers_reassign($alias1,$alias2,$subscriber1,$subscriber2);
|
||||||
|
|
||||||
|
$test_machine->runas('admin');
|
||||||
|
|
||||||
|
$subscriber_test_machine->clear_test_data_all();#fake data aren't registered in this test
|
||||||
|
}
|
||||||
|
|
||||||
|
$fake_data->clear_test_data_all();
|
||||||
|
$test_machine->clear_test_data_all();#fake data aren't registered in this test machine, so they will stay.
|
||||||
|
$fake_data->clear_test_data_all();
|
||||||
|
undef $test_machine;
|
||||||
|
undef $fake_data;
|
||||||
|
done_testing;
|
||||||
|
|
||||||
|
sub test_numbers_reassign{
|
||||||
|
my($alias1,$alias2,$subscriber1,$subscriber2) = @_;
|
||||||
|
my $res;
|
||||||
|
$alias1->{content}->{subscriber_id} = $subscriber2->{content}->{id};
|
||||||
|
($res) = $test_machine->request_patch([ { op => 'replace', path => '/subscriber_id', value => $subscriber2->{content}->{id} } ] , $alias1->{location});
|
||||||
|
$test_machine->http_code_msg(200, "PATCH for /numbers/", $res);
|
||||||
|
|
||||||
|
($res) = $test_machine->request_patch([ { op => 'replace', path => '/subscriber_id', value => $subscriber1->{content}->{id} } ] , $alias1->{location});
|
||||||
|
$test_machine->http_code_msg(200, "PATCH for /numbers/", $res);
|
||||||
|
|
||||||
|
($res) = $test_machine->request_patch([ { op => 'replace', path => '/subscriber_id', value => $subscriber1->{content}->{id} } ] , $alias2->{location});
|
||||||
|
$test_machine->http_code_msg(200, "PATCH for /numbers/", $res);
|
||||||
|
|
||||||
|
($res) = $test_machine->request_patch([ { op => 'replace', path => '/subscriber_id', value => $subscriber2->{content}->{id} } ] , $alias2->{location});
|
||||||
|
$test_machine->http_code_msg(200, "PATCH for /numbers/", $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
# vim: set tabstop=4 expandtab:
|
Loading…
Reference in new issue