TT#5559 api/events, event test with "missing end-events" cases

+ api/events resource
+ removing an autoattendant via /api/callforwards: events OK
+ removing an autoattendant via /api/cfdestiantions: OK
+ removing an autoattendant via /api/cfmappings: OK

Change-Id: I4c309753b9338582479dba9951f757bb2ecaad7e
changes/98/9798/9
Rene Krenn 10 years ago
parent 776dd24f6f
commit 5633770f1f

@ -0,0 +1,195 @@
package NGCP::Panel::Controller::API::Events;
use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
#use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
require NGCP::Panel::Role::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
sub api_description {
return 'Browse EDRs (event data records).';
};
sub query_params {
return [
{
param => 'subscriber_id',
description => 'Filter for events of a specific subscriber.',
query => {
first => sub {
my $q = shift;
return { 'subscriber_id' => $q };
},
second => sub {},
},
},
{
param => 'reseller_id',
description => 'Filter for events for customers/subscribers of a specific reseller.',
query => {
first => sub {
my $q = shift;
return { 'reseller.id' => $q };
},
second => sub {},
},
},
{
param => 'type',
description => 'Filter for events of a specific type.',
query => {
first => sub {
my $q = shift;
{ type => { like => $q } };
},
second => sub {},
},
},
{
param => 'timestamp_from',
description => 'Filter for events occurred after or at the given time stamp.',
query => {
first => sub {
my $q = shift;
my $dt = NGCP::Panel::Utils::DateTime::from_string($q);
return { 'timestamp' => { '>=' => $dt->epoch } };
},
second => sub {},
},
},
{
param => 'timestamp_to',
description => 'Filter for events occurred before or at the given time stamp.',
query => {
first => sub {
my $q = shift;
my $dt = NGCP::Panel::Utils::DateTime::from_string($q);
return { 'timestamp' => { '<=' => $dt->epoch } };
},
second => sub {},
},
},
];
}
use parent qw/Catalyst::Controller NGCP::Panel::Role::API::Events/;
sub resource_name{
return 'events';
}
sub dispatch_path{
return '/api/events/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-events';
}
__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(+NGCP::Panel::Role::HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub GET :Allow {
my ($self, $c) = @_;
my $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
{
my $items = $self->item_rs($c);
(my $total_count, $items) = $self->paginate_order_collection($c, $items);
my (@embedded, @links);
my $form = $self->get_form($c);
for my $item ($items->all) {
my $hal = $self->hal_from_item($c, $item, $form);
$hal->_forcearray(1);
push @embedded,$hal;
my $link = Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
href => sprintf('/%s%d', $c->request->path, $item->id),
);
$link->_forcearray(1);
push @links, $link;
}
push @links,
Data::HAL::Link->new(
relation => 'curies',
href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}',
name => 'ngcp',
templated => true,
),
Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/');
push @links, $self->collection_nav_links($page, $rows, $total_count, $c->request->path, $c->request->query_params);
my $hal = Data::HAL->new(
embedded => [@embedded],
links => [@links],
);
$hal->resource({
total_count => $total_count,
});
my $response = HTTP::Response->new(HTTP_OK, undef,
HTTP::Headers->new($hal->http_headers(skip_links => 1)), $hal->as_json);
$c->response->headers($response->headers);
$c->response->body($response->content);
return;
}
return;
}
sub HEAD :Allow {
my ($self, $c) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub OPTIONS :Allow {
my ($self, $c) = @_;
my $allowed_methods = $self->allowed_methods_filtered($c);
$c->response->headers(HTTP::Headers->new(
Allow => join(', ', @{ $allowed_methods }),
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 end : Private {
my ($self, $c) = @_;
$self->log_response($c);
}
1;

@ -0,0 +1,102 @@
package NGCP::Panel::Controller::API::EventsItem;
use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
#use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
require Catalyst::ActionRole::ACL;
require NGCP::Panel::Role::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
use parent qw/Catalyst::Controller NGCP::Panel::Role::API::Events/;
sub resource_name{
return 'events';
}
sub dispatch_path{
return '/api/events/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-events';
}
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Args => 1,
Does => [qw(ACL RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(+NGCP::Panel::Role::HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub GET :Allow {
my ($self, $c, $id) = @_;
{
last unless $self->valid_id($c, $id);
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, event => $item);
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"|;
s/rel=self/rel="item self"/;
$_
} $hal->http_headers),
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->body($response->content);
return;
}
return;
}
sub HEAD :Allow {
my ($self, $c, $id) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub OPTIONS :Allow {
my ($self, $c, $id) = @_;
my $allowed_methods = $self->allowed_methods_filtered($c);
$c->response->headers(HTTP::Headers->new(
Allow => join(', ', @{ $allowed_methods }),
Accept_Patch => 'application/json-patch+json',
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
}
1;

@ -0,0 +1,28 @@
package NGCP::Panel::Form::Event::Admin;
use HTML::FormHandler::Moose;
extends 'NGCP::Panel::Form::Event::Reseller';
has_field 'reseller_id' => (
type => 'PosInteger',
label => 'The subscriber contract\'s reseller.',
required => 1,
);
has_field 'export_status' => (
type => 'Select',
label => 'The status of the exporting process.',
options => [
{ label => 'unexported', 'value' => 'unexported' },
{ label => 'ok', 'value' => 'ok' },
{ label => 'failed', 'value' => 'failed' },
],
);
has_field 'exported_at' => (
type => 'Text',
title => 'The timestamp when the exporting occured.',
required => 0,
);
1;

@ -0,0 +1,50 @@
package NGCP::Panel::Form::Event::Reseller;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
has_field 'id' => (
type => 'Hidden'
);
has_field 'type' => (
type => 'Text',
label => 'The event type.',
required => 1,
);
#has_field 'type' => (
# type => 'Select',
# label => 'The top-up request type.',
# options => [
# { value => 'cash', label => 'Cash top-up' },
# { value => 'voucher', label => 'Voucher top-up' },
# ],
# required => 1,
#);
has_field 'subscriber_id' => (
type => 'PosInteger',
label => 'The subscriber the event is related to.',
required => 1,
);
has_field 'old_status' => (
type => 'Text',
label => 'Status information before the event, if applicable.',
required => 0,
);
has_field 'new_status' => (
type => 'Text',
label => 'Status information after the event, if applicable.',
required => 0,
);
has_field 'timestamp' => (
type => '+NGCP::Panel::Field::DateTime',
label => 'The timestamp of the event.',
required => 1,
);
1;

@ -0,0 +1,86 @@
package NGCP::Panel::Role::API::Events;
use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use parent 'NGCP::Panel::Role::API';
use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Form::Event::Reseller;
use NGCP::Panel::Form::Event::Admin;
use Data::Dumper;
sub _item_rs {
my ($self, $c) = @_;
my $item_rs = $c->model('DB')->resultset('events');
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({
'reseller_id' => $c->user->reseller_id,
},undef);
}
return $item_rs;
}
sub get_form {
my ($self, $c) = @_;
if($c->user->roles eq "admin") {
return NGCP::Panel::Form::Event::Admin->new;
} elsif($c->user->roles eq "reseller") {
return NGCP::Panel::Form::Event::Reseller->new;
}
}
sub hal_from_item {
my ($self, $c, $item, $form) = @_;
my %resource = $item->get_inflated_columns;
my $datetime_fmt = DateTime::Format::Strptime->new(
pattern => '%F %T',
);
$resource{timestamp} = $datetime_fmt->format_datetime($resource{timestamp}) if defined $resource{timestamp};
my $hal = Data::HAL->new(
links => [
Data::HAL::Link->new(
relation => 'curies',
href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}',
name => 'ngcp',
templated => true,
),
Data::HAL::Link->new(relation => 'collection', href => sprintf("/api/%s/", $self->resource_name)),
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)),
(defined $item->subscriber_id ? Data::HAL::Link->new(relation => 'ngcp:subscribers', href => sprintf("/api/subscribers/%d", $item->subscriber_id)) : ()),
(defined $item->reseller_id ? Data::HAL::Link->new(relation => 'ngcp:resellers', href => sprintf("/api/resellers/%d", $item->reseller_id)) : ()),
],
relation => 'ngcp:'.$self->resource_name,
);
$form //= $self->get_form($c);
$self->validate_form(
c => $c,
resource => \%resource,
form => $form,
run => 0,
exceptions => [qw/id subscriber_id reseller_id/],
);
$resource{id} = int($item->id);
$hal->resource({%resource});
return $hal;
}
sub item_by_id {
my ($self, $c, $id) = @_;
my $item_rs = $self->item_rs($c);
return $item_rs->find($id);
}
1;

@ -27,6 +27,15 @@ sub current_local {
}
}
sub current_local_hires {
#If the epoch value is a floating-point value, it will be rounded to nearest microsecond.
return DateTime->from_epoch( epoch => Time::HiRes::time,
time_zone => DateTime::TimeZone->new(name => 'local')
);
}
sub set_local_tz {
my $dt = shift;
if (defined $dt && ref $dt eq 'DateTime' && !is_infinite($dt)) {

@ -11,14 +11,14 @@ sub insert {
my $old = $params{old};
my $new = $params{new};
$schema->resultset('events')->create({
type => $type,
subscriber_id => $subscriber->id,
reseller_id => $subscriber->contract->contact->reseller_id,
old_status => $old // '',
new_status => $new // '',
timestamp => NGCP::Panel::Utils::DateTime::current_local->hires_epoch,
timestamp => NGCP::Panel::Utils::DateTime::current_local_hires,
export_status => 'unexported',
exported_at => undef,
});

@ -0,0 +1,470 @@
#use Sipwise::Base;
use Net::Domain qw(hostfqdn);
use LWP::UserAgent;
use JSON qw();
use Test::More;
my $is_local_env = 0;
my $uri = $ENV{CATALYST_SERVER} || ('https://'.hostfqdn.':4443');
my ($netloc) = ($uri =~ m!^https?://(.*)/?.*$!);
my ($ua, $req, $res);
$ua = LWP::UserAgent->new;
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => 0,
);
my $user = $ENV{API_USER} // 'administrator';
my $pass = $ENV{API_PASS} // 'administrator';
$ua->credentials($netloc, "api_admin_http", $user, $pass);
#$ua->add_handler("request_send", sub {
# my ($request, $ua, $h) = @_;
# print $request->method . ' ' . $request->uri . "\n" . ($request->content ? $request->content . "\n" : '') unless $request->header('authorization');
# return undef;
#});
#$ua->add_handler("response_done", sub {
# my ($response, $ua, $h) = @_;
# print $response->decoded_content . "\n" if $response->code != 401;
# return undef;
#});
my $t = time;
my $reseller_id = 1;
$req = HTTP::Request->new('POST', $uri.'/api/domains/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
domain => 'test' . $t . '.example.org',
reseller_id => $reseller_id,
}));
$res = $ua->request($req);
is($res->code, 201, "create test domain");
$req = HTTP::Request->new('GET', $uri.'/'.$res->header('Location'));
$res = $ua->request($req);
is($res->code, 200, "fetch created test domain");
my $domain = JSON::from_json($res->decoded_content);
$req = HTTP::Request->new('POST', $uri.'/api/billingprofiles/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
name => "test profile $t",
handle => "testprofile$t",
reseller_id => $reseller_id,
}));
$res = $ua->request($req);
is($res->code, 201, "create test billing profile");
my $billing_profile_id = $res->header('Location');
$billing_profile_id =~ s/^.+\/(\d+)$/$1/;
$req = HTTP::Request->new('POST', $uri.'/api/customercontacts/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
firstname => "cust_contact_first",
lastname => "cust_contact_last",
email => "cust_contact\@custcontact.invalid",
reseller_id => $reseller_id,
}));
$res = $ua->request($req);
is($res->code, 201, "create test customer contact");
$req = HTTP::Request->new('GET', $uri.'/'.$res->header('Location'));
$res = $ua->request($req);
is($res->code, 200, "fetch test customer contact");
my $custcontact = JSON::from_json($res->decoded_content);
my %subscriber_map = ();
my %customer_map = ();
#goto SKIP;
{
my $customer = _create_customer(
type => "sipaccount",
);
my $subscriber = _create_subscriber($customer,
primary_number => { cc => 888, ac => '1'.(scalar keys %subscriber_map), sn => $t },
);
my $call_forwards = set_callforwards($subscriber,{ cfu => {
destinations => [
{ destination => "5678" },
{ destination => "autoattendant", },
],
}});
$call_forwards = set_callforwards($subscriber,{ cfu => {
destinations => [
{ destination => "5678" },
],
}});
_check_event_history("events generated using /api/callforwards: ",$subscriber->{id},"%ivr",[
{ subscriber_id => $subscriber->{id}, type => "start_ivr" },
{ subscriber_id => $subscriber->{id}, type => "end_ivr" },
]);
}
#$t = time;
SKIP:
{
my $customer = _create_customer(
type => "sipaccount",
);
my $subscriber = _create_subscriber($customer,
primary_number => { cc => 888, ac => '2'.(scalar keys %subscriber_map), sn => $t },
);
my $destinationset_1 = _create_cfdestinationset($subscriber,"dest1_$t",[{ destination => "1234",
timeout => '10',
priority => '1',
simple_destination => undef },{ destination => "autoattendant",
timeout => '10',
priority => '1',
simple_destination => undef }
]);
my $destinationset_2 = _create_cfdestinationset($subscriber,"dest2_$t",[{ destination => "1234",
timeout => '10',
priority => '1',
simple_destination => undef },{ destination => "autoattendant",
timeout => '10',
priority => '1',
simple_destination => undef }
]);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t);
my $timeset = _create_cftimeset($subscriber,[{ year => $year + 1900,
month => $mon + 1,
mday => $mday,
wday => $wday + 1,
hour => $hour,
minute => $min}]);
my $mappings = _create_cfmapping($subscriber,{
#cfb => [{ destinationset => $cfdestinationset->{name},
# timeset => $cftimeset->{name}}],
#cfna => [{ destinationset => $cfdestinationset->{name},
# timeset => $cftimeset->{name}}],
#cft => [{ destinationset => $cfdestinationset->{name},
# timeset => $cftimeset->{name}}],
cfb => [],
cfna => [],
cft => [{ destinationset => $destinationset_1->{name},
timeset => $timeset->{name}}],
cfu => [{ destinationset => $destinationset_2->{name},
timeset => $timeset->{name}}],
});
#1. update destination set:
$destinationset_1 = _update_cfdestinationset($destinationset_1,[{ destination => "1234",
timeout => '10',
priority => '1',
simple_destination => undef },
]);
_check_event_history("events generated by updating /api/cfdestinationsets: ",$subscriber->{id},"%ivr",[
{ subscriber_id => $subscriber->{id}, type => "start_ivr" },
{ subscriber_id => $subscriber->{id}, type => "start_ivr" },
{ subscriber_id => $subscriber->{id}, type => "end_ivr" },
]);
#2. update cfmappings:
$mappings = _update_cfmapping($subscriber,"cfu",[]);
_check_event_history("events generated by updating /api/cfmappings: ",$subscriber->{id},"%ivr",[
{ subscriber_id => $subscriber->{id}, type => "start_ivr" },
{ subscriber_id => $subscriber->{id}, type => "start_ivr" },
{ subscriber_id => $subscriber->{id}, type => "end_ivr" },
{ subscriber_id => $subscriber->{id}, type => "end_ivr" },
]);
}
sub _create_cfmapping {
my ($subscriber,$mappings) = @_;
my $cfmapping_uri = $uri.'/api/cfmappings/'.$subscriber->{id};
$req = HTTP::Request->new('PUT', $cfmapping_uri); #$customer->{id});
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json($mappings));
$res = $ua->request($req);
is($res->code, 200, "create test cfmappings");
$req = HTTP::Request->new('GET', $cfmapping_uri); # . '?page=1&rows=' . (scalar keys %$put_data));
$res = $ua->request($req);
is($res->code, 200, "fetch test cfmappings");
return JSON::from_json($res->decoded_content);
}
sub _update_cfmapping {
my ($subscriber,$cf_type,$mapping) = @_;
my $cfmapping_uri = $uri.'/api/cfmappings/'.$subscriber->{id};
$req = HTTP::Request->new('PATCH', $cfmapping_uri);
$req->header('Content-Type' => 'application/json-patch+json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json(
[ { op => 'replace', path => '/'.$cf_type, value => $mapping } ]
));
$res = $ua->request($req);
is($res->code, 200, "update test cfmappings");
$req = HTTP::Request->new('GET', $cfmapping_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch updated test cfmappings");
return JSON::from_json($res->decoded_content);
}
sub _create_cftimeset {
my ($subscriber,$times) = @_;
$req = HTTP::Request->new('POST', $uri.'/api/cftimesets/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
name => "cf_time_set_".$t,
subscriber_id => $subscriber->{id},
times => \@times,
}));
$res = $ua->request($req);
is($res->code, 201, "create test cftimeset");
my $cftimeset_uri = $uri.'/'.$res->header('Location');
$req = HTTP::Request->new('GET', $cftimeset_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch created test cftimeset");
return JSON::from_json($res->decoded_content);
}
sub _create_cfdestinationset {
my ($subscriber,$name,$destinations) = @_;
$req = HTTP::Request->new('POST', $uri.'/api/cfdestinationsets/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
name => $name,
subscriber_id => $subscriber->{id},
destinations => $destinations,
}));
$res = $ua->request($req);
is($res->code, 201, "create test cfdestinationset");
my $cfdestinationset_uri = $uri.'/'.$res->header('Location');
$req = HTTP::Request->new('GET', $cfdestinationset_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch created test cfdestinationset");
return JSON::from_json($res->decoded_content);
}
sub _update_cfdestinationset {
my ($destinationset,$destinations) = @_;
my $cfdestinationset_uri = $uri.'/api/cfdestinationsets/'.$destinationset->{id};
$req = HTTP::Request->new('PATCH', $cfdestinationset_uri);
$req->header('Content-Type' => 'application/json-patch+json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json(
[ { op => 'replace', path => '/destinations', value => $destinations } ]
));
$res = $ua->request($req);
is($res->code, 200, "update test cfdestinationset");
$req = HTTP::Request->new('GET', $cfdestinationset_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch updated test cfdestinationset");
return JSON::from_json($res->decoded_content);
}
sub set_callforwards {
my ($subscriber,$call_forwards) = @_;
my $callforward_uri = $uri.'/api/callforwards/'.$subscriber->{id};
$req = HTTP::Request->new('PUT', $callforward_uri); #$customer->{id});
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json($call_forwards));
$res = $ua->request($req);
is($res->code, 200, "set test callforwards");
$req = HTTP::Request->new('GET', $callforward_uri); # . '?page=1&rows=' . (scalar keys %$put_data));
$res = $ua->request($req);
is($res->code, 200, "fetch test callforwards");
return JSON::from_json($res->decoded_content);
}
sub _get_subscriber {
my ($subscriber) = @_;
$req = HTTP::Request->new('GET', $uri.'/api/subscribers/'.$subscriber->{id});
$res = $ua->request($req);
is($res->code, 200, "fetch test subscriber");
$subscriber = JSON::from_json($res->decoded_content);
$subscriber_map{$subscriber->{id}} = $subscriber;
return $subscriber;
}
sub _create_subscriber {
my ($customer,@further_opts) = @_;
$req = HTTP::Request->new('POST', $uri.'/api/subscribers/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
domain_id => $domain->{id},
username => 'subscriber_' . (scalar keys %subscriber_map) . '_'.$t,
password => 'subscriber_password',
customer_id => $customer->{id},
#status => "active",
@further_opts,
}));
$res = $ua->request($req);
is($res->code, 201, "create test subscriber");
$req = HTTP::Request->new('GET', $uri.'/'.$res->header('Location'));
$res = $ua->request($req);
is($res->code, 200, "fetch test subscriber");
my $subscriber = JSON::from_json($res->decoded_content);
$subscriber_map{$subscriber->{id}} = $subscriber;
return $subscriber;
}
sub _update_subscriber {
my ($subscriber,@further_opts) = @_;
$req = HTTP::Request->new('PUT', $uri.'/api/subscribers/'.$subscriber->{id});
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
%$subscriber,
@further_opts,
}));
$res = $ua->request($req);
is($res->code, 200, "update test subscriber");
$subscriber = JSON::from_json($res->decoded_content);
$subscriber_map{$subscriber->{id}} = $subscriber;
return $subscriber;
}
sub _create_customer {
my (@further_opts) = @_;
$req = HTTP::Request->new('POST', $uri.'/api/customers/');
$req->header('Content-Type' => 'application/json');
$req->content(JSON::to_json({
status => "active",
contact_id => $custcontact->{id},
type => "sipaccount",
billing_profile_id => $billing_profile_id,
max_subscribers => undef,
external_id => undef,
#status => "active",
@further_opts,
}));
$res = $ua->request($req);
is($res->code, 201, "create test customer");
$req = HTTP::Request->new('GET', $uri.'/'.$res->header('Location'));
$res = $ua->request($req);
is($res->code, 200, "fetch test customer");
my $customer = JSON::from_json($res->decoded_content);
$customer_map{$customer->{id}} = $customer;
return $customer;
}
sub _check_event_history {
my ($label,$subscriber_id,$type,$expected_events) = @_;
if (defined $subscriber_id) {
$subscriber_id = '&subscriber_id=' . $subscriber_id;
} else {
$subscriber_id = '';
}
if (defined $type) {
$type = '&type=' . $type;
} else {
$type = '';
}
my $total_count = (scalar @$expected_events);
my $i = 0;
my $ok = 1;
my @events = ();
my @requests = ();
my $last_request;
$last_request = _req_to_debug($req) if $req;
my $nexturi = $uri.'/api/events/?page=1&rows=10&order_by_direction=asc&order_by=id'.$subscriber_id.$type;
do {
$req = HTTP::Request->new('GET',$nexturi);
$res = $ua->request($req);
is($res->code, 200, $label . "fetch events collection page");
push(@requests,_req_to_debug($req));
my $collection = JSON::from_json($res->decoded_content);
my $selfuri = $uri . $collection->{_links}->{self}->{href};
my $colluri = URI->new($selfuri);
$ok = ok($collection->{total_count} == $total_count, $label . "check 'total_count' of collection") && $ok;
if($collection->{_links}->{next}->{href}) {
$nexturi = $uri . $collection->{_links}->{next}->{href};
} else {
$nexturi = undef;
}
$collection->{_embedded}->{'ngcp:events'} = [
$collection->{_embedded}->{'ngcp:events'}
] if "HASH" eq ref $collection->{_embedded}->{'ngcp:events'};
my $page_items = {};
foreach my $event (@{ $collection->{_embedded}->{'ngcp:events'} }) {
$ok = _compare_event($event,$expected_events->[$i],$label) && $ok;
delete $event->{'_links'};
push(@events,$event);
$i++
}
} while($nexturi);
ok($i == $total_count,$label . "check if all expected items are listed");
diag(Dumper({last_request => $last_request, collection_requests => \@requests, result => \@events})) if !$ok;
}
sub _compare_event {
my ($got,$expected,$label) = @_;
my $ok = 1;
if ($expected->{id}) {
$ok = is($got->{id},$expected->{id},$label . "check event " . $got->{id} . " id") && $ok;
}
if ($expected->{subscriber_id}) {
$ok = is($got->{subscriber_id},$expected->{subscriber_id},$label . "check event " . $got->{id} . " subscriber_id") && $ok;
}
if ($expected->{type}) {
$ok = is($got->{type},$expected->{type},$label . "check event " . $got->{id} . " type '".$expected->{type}."'") && $ok;
}
return $ok;
}
sub _req_to_debug {
my $request = shift;
return { request => $request->method . " " . $request->uri,
headers => $request->headers };
}
sub _get_query_string {
my ($filters) = @_;
my $query = '';
foreach my $param (keys %$filters) {
if (length($query) == 0) {
$query .= '?';
} else {
$query .= '&';
}
$query .= $param . '=' . $filters->{$param};
}
return $query;
};
done_testing;
Loading…
Cancel
Save