MT#15883 implement rtcnetworks API

GET,item PUT, item PATCH implemented
this covers CRUD of rtcnetworks

Change-Id: If64034161886b5c7f943fd5e9a4d651e131c5a52
changes/23/3823/14
Gerhard Jungwirth 10 years ago
parent ece2d274c1
commit e9580d2e9d

@ -190,10 +190,13 @@ sub POST :Allow {
contract_id => $resource->{contract_id},
});
NGCP::Panel::Utils::Reseller::create_email_templates( c => $c, reseller => $reseller );
NGCP::Panel::Utils::Rtc::modify_reseller_rtc(undef, $resource, $c->config,
$reseller, sub {
$c->log->warn(shift); return;
});
NGCP::Panel::Utils::Rtc::modify_reseller_rtc(
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
$c->log->warn(shift); return;
});
} catch($e) {
$c->log->error("failed to create reseller: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create reseller.");

@ -0,0 +1,135 @@
package NGCP::Panel::Controller::API::RtcNetworks;
use NGCP::Panel::Utils::Generic qw(:all);
use Moose;
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);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Show a collection of RTC networks, belonging to a specific reseller.',
);
class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
]},
);
with 'NGCP::Panel::Role::API::RtcNetworks';
class_has('resource_name', is => 'ro', default => 'rtcnetworks');
class_has('dispatch_path', is => 'ro', default => '/api/rtcnetworks/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-rtcnetworks');
__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);
return 1;
}
sub GET :Allow {
my ($self, $c) = @_;
my $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
{
my $resellers = $self->item_rs($c);
(my $total_count, $resellers) = $self->paginate_order_collection($c, $resellers);
my (@embedded, @links);
for my $subscriber ($resellers->all) {
push @embedded, $self->hal_from_item($c, $subscriber);
push @links, Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
href => sprintf('%s%d', $self->dispatch_path, $subscriber->id),
);
}
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/'),
Data::HAL::Link->new(relation => 'self', href => sprintf('%s?page=%s&rows=%s', $self->dispatch_path, $page, $rows));
if(($total_count / $rows) > $page ) {
push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('%s?page=%d&rows=%d', $self->dispatch_path, $page + 1, $rows));
}
if($page > 1) {
push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('/%s?page=%d&rows=%d', $c->request->path, $page - 1, $rows));
}
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);
return 1;
}
no Moose;
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,228 @@
package NGCP::Panel::Controller::API::RtcNetworksItem;
use NGCP::Panel::Utils::Generic qw(:all);
use Moose;
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
with 'NGCP::Panel::Role::API::RtcNetworks';
class_has('resource_name', is => 'ro', default => 'rtcnetworks');
class_has('dispatch_path', is => 'ro', default => '/api/rtcnetworks/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-rtcnetworks');
class_has(@{ __PACKAGE__->get_journal_query_params() });
__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 }),
@{ __PACKAGE__->get_journal_action_config(__PACKAGE__->resource_name,{
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Does => [qw(ACL RequireSSL)],
}) },
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
return 1;
}
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, reseller => $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"|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;
}
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 PUT :Allow {
my ($self, $c, $id) = @_;
my $schema = $c->model('DB');
my $guard = $schema->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $reseller = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, reseller => $reseller);
my $resource = $self->get_valid_put_data(
c => $c,
id => $id,
media_type => 'application/json',
);
last unless $resource;
my $form = $self->get_form($c);
my $old_resource = $self->hal_from_item($c, $reseller, 1)->resource;
$reseller = $self->update_item($c, $reseller, $old_resource, $resource, $form);
last unless $reseller;
my $hal = $self->hal_from_item($c, $reseller);
last unless $self->add_update_journal_item_hal($c,{ hal => $hal, id => $reseller->id });
$guard->commit;
if ('minimal' eq $preference) {
$c->response->status(HTTP_NO_CONTENT);
$c->response->header(Preference_Applied => 'return=minimal');
$c->response->body(q());
} else {
#my $hal = $self->hal_from_item($c, $reseller);
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);
}
}
return;
}
sub PATCH :Allow {
my ($self, $c, $id) = @_;
my $schema = $c->model('DB');
my $guard = $schema->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $reseller = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, reseller => $reseller);
my $json = $self->get_valid_patch_data(
c => $c,
id => $id,
media_type => 'application/json-patch+json',
ops => ["add", "replace", "copy", "remove"],
);
last unless $json;
my $form = $self->get_form($c);
my $old_resource = $self->hal_from_item($c, $reseller, 1)->resource;
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
$reseller = $self->update_item($c, $reseller, $old_resource, $resource, $form);
last unless $reseller;
my $hal = $self->hal_from_item($c, $reseller);
last unless $self->add_update_journal_item_hal($c,{ hal => $hal, id => $reseller->id });
$guard->commit;
if ('minimal' eq $preference) {
$c->response->status(HTTP_NO_CONTENT);
$c->response->header(Preference_Applied => 'return=minimal');
$c->response->body(q());
} else {
#my $hal = $self->hal_from_item($c, $reseller);
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);
}
}
return;
}
sub item_base_journal :Journal {
my $self = shift @_;
return $self->handle_item_base_journal(@_);
}
sub journals_get :Journal {
my $self = shift @_;
return $self->handle_journals_get(@_);
}
sub journalsitem_get :Journal {
my $self = shift @_;
return $self->handle_journalsitem_get(@_);
}
sub journals_options :Journal {
my $self = shift @_;
return $self->handle_journals_options(@_);
}
sub journalsitem_options :Journal {
my $self = shift @_;
return $self->handle_journalsitem_options(@_);
}
sub journals_head :Journal {
my $self = shift @_;
return $self->handle_journals_head(@_);
}
sub journalsitem_head :Journal {
my $self = shift @_;
return $self->handle_journalsitem_head(@_);
}
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
return 1;
}
no Moose;
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,22 @@
package NGCP::Panel::Form::Rtc::NetworksAdmin;
use HTML::FormHandler::Moose;
extends 'NGCP::Panel::Form::Rtc::NetworksReseller';
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
#validate_when_empty => 1,
element_attr => {
rel => ['tooltip'],
title => ['The reseller id this networks belong to.'],
},
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/reseller rtc_user_id networks/],
);
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,89 @@
package NGCP::Panel::Form::Rtc::NetworksReseller;
use HTML::FormHandler::Moose;
use HTML::FormHandler::Widget::Block::Bootstrap;
extends 'HTML::FormHandler';
# with 'NGCP::Panel::Render::RepeatableJs'; # only used in API currently
has '+widget_wrapper' => ( default => 'Bootstrap' );
has_field 'submitid' => ( type => 'Hidden' );
sub build_render_list {return [qw/submitid fields actions/]}
sub build_form_element_class {return [qw(form-horizontal)]}
has_field 'rtc_user_id' => (
# readonly
type => 'Text',
required => 0,
element_attr => {
rel => ['tooltip'],
title => ['ID in the backend RTC API (readonly).'],
},
);
has_field 'networks' => (
type => 'Repeatable',
required => 0, #1,
setup_for_js => 1,
do_wrapper => 1,
do_label => 0,
tags => {
controls_div => 1,
},
wrapper_class => [qw/hfh-rep/],
element_attr => {
rel => ['tooltip'],
title => ['An array of objects with keys "config", "connector" and "tag" to create RTC networks for this reseller'],
},
);
# webrtc, conference, xmpp-connector, sip-connector, sipwise-connector
has_field 'networks.connector' => (
type => 'Select',
options => [
{ value => 'webrtc', label => 'webrtc' },
{ value => 'conference', label => 'conference' },
{ value => 'xmpp-connector', label => 'xmpp-connector' },
{ value => 'sip-connector', label => 'sip-connector' },
{ value => 'sipwise-connector', label => 'sipwise-connector' },
],
element_attr => {
rel => ['tooltip'],
title => ['One of the available options. This defines, to which networks rtc subscribers will be able to connect to.'],
},
);
has_field 'networks.tag' => (
type => 'Text',
element_attr => {
rel => ['tooltip'],
title => ['An arbitrary name, to address that network instance'],
},
);
has_field 'networks.config' => (
type => 'Compound', # Text
element_attr => {
rel => ['tooltip'],
title => ['An arbitrary hash of additional config contents; e.g. {"xms": false}'],
},
);
has_field 'save' => (
type => 'Submit',
value => 'Save',
element_class => [qw/btn btn-primary/],
label => '',
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/rtc_user_id networks/],
);
has_block 'actions' => (
tag => 'div',
class => [qw/modal-footer/],
render_list => [qw/save/],
);
1;

@ -130,10 +130,14 @@ sub update_reseller {
contract_id => $resource->{contract_id},
});
NGCP::Panel::Utils::Rtc::modify_reseller_rtc($old_resource, $resource, $c->config,
$reseller, sub {
$c->log->warn(shift); return;
});
NGCP::Panel::Utils::Rtc::modify_reseller_rtc(
old_resource => $old_resource,
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
$c->log->warn(shift); return;
});
# TODO: should we lock reseller admin logins if reseller gets terminated?
# or terminate all his customers and delete non-billing data?

@ -0,0 +1,138 @@
package NGCP::Panel::Role::API::RtcNetworks;
use NGCP::Panel::Utils::Generic qw(:all);
use Moose::Role;
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 JSON::Types;
use NGCP::Panel::Form::Rtc::NetworksAdmin;
use NGCP::Panel::Utils::Subscriber;
use NGCP::Panel::Utils::Rtc;
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Rtc::NetworksAdmin->new;
}
sub hal_from_item {
my ($self, $c, $item, $include_id) = @_;
my $resource = { reseller_id => $item->id};
if ($item->rtc_user) {
my $rtc_user_id = $item->rtc_user->rtc_user_id;
$resource->{rtc_user_id} = $rtc_user_id if $include_id;
$resource->{networks} = NGCP::Panel::Utils::Rtc::get_rtc_networks(
rtc_user_id => $rtc_user_id,
config => $c->config,
include_id => $include_id,
err_code => sub {
$c->log->warn(shift); return;
});
} else {
}
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)),
Data::HAL::Link->new(relation => 'ngcp:resellers', href => sprintf("/api/resellers/%d", $item->id)),
$self->get_journal_relation_link($item->id),
],
relation => 'ngcp:'.$self->resource_name,
);
my $form = $self->get_form($c);
unless ($include_id) {
return unless $self->validate_form(
c => $c,
form => $form,
resource => $resource,
run => 0,
exceptions => ['rtc_user_id'],
);
}
$hal->resource($resource);
return $hal;
}
sub item_rs {
my ($self, $c) = @_;
my $item_rs;
$item_rs = $c->model('DB')->resultset('resellers')
->search_rs(undef, {
prefetch => 'rtc_user',
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({
id => $c->user->reseller_id,
});
}
return $item_rs;
}
sub item_by_id {
my ($self, $c, $id) = @_;
my $item_rs = $self->item_rs($c);
return $item_rs->find($id);
}
sub update_item {
my ($self, $c, $item, $old_resource, $resource, $form) = @_;
my $reseller = $item;
if (ref $resource->{networks} ne "ARRAY") {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid field 'networks'. Must be an array.");
return;
}
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
resource => $resource,
);
NGCP::Panel::Utils::Rtc::modify_rtc_networks(
old_resource => $old_resource,
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
$c->log->warn(shift); return;
});
try {
} catch($e) {
$c->log->error("failed to update autoattendants: $e");
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update autoattendants.");
return;
};
return $reseller;
}
1;
# vim: set tabstop=4 expandtab:

@ -14,7 +14,7 @@ use JSON qw/decode_json encode_json/;
has 'ua' => ( is => 'rw', default => sub {
return LWP::UserAgent->new(
ssl_opts => { verify_hostname => 0 },
ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0 },
timeout => 20,
);
});
@ -147,7 +147,7 @@ sub get_session {
my $ua = $self->ua;
return $self->_create_response(
$ua->get($self->host . "/sessions/id/$session_id")
$ua->get($self->host . "/sessions/id/$session_id"),
);
}
@ -175,6 +175,12 @@ sub get_networks {
return $networks;
}
sub get_networks_by_user_id {
my ($self, $user_id) = @_;
my $networks = $self->_resolve_collection_fast( "/users/id/$user_id/networks" );
return $networks;
}
sub _resolve_collection {
my ($self, $bare_url, $max_rows) = @_;
my $ua = $self->ua;

@ -6,9 +6,12 @@ use strict;
use JSON qw//;
use NGCP::Panel::Utils::ComxAPIClient;
use NGCP::Panel::Utils::Generic qw/compare/;
sub modify_reseller_rtc {
my ($old_resource, $resource, $config, $reseller_item, $err_code) = @_;
my %params = @_;
my ($old_resource, $resource, $config, $reseller_item, $err_code) =
@params{qw/old_resource resource config reseller_item err_code/};
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
@ -21,7 +24,11 @@ sub modify_reseller_rtc {
return;
}
_create_rtc_user($resource, $config, $reseller_item, $err_code);
_create_rtc_user(
resource => $resource,
config => $config,
reseller_item => $reseller_item,
err_code => $err_code);
} elsif ((defined $old_resource) && (defined $resource)) {
@ -30,24 +37,36 @@ sub modify_reseller_rtc {
$old_resource->{enable_rtc}) { # just terminated
$resource->{enable_rtc} = JSON::false;
_delete_rtc_user($config, $reseller_item, $err_code);
_delete_rtc_user(
config => $config,
reseller_item => $reseller_item,
err_code => $err_code);
} elsif ($old_resource->{enable_rtc} &&
!$resource->{enable_rtc}) { # disable rtc
_delete_rtc_user($config, $reseller_item, $err_code);
_delete_rtc_user(
config => $config,
reseller_item => $reseller_item,
err_code => $err_code);
} elsif (!$old_resource->{enable_rtc} &&
$resource->{enable_rtc} &&
$resource->{status} ne 'terminated') { # enable rtc
_create_rtc_user($resource, $config, $reseller_item, $err_code);
_create_rtc_user(
resource => $resource,
config => $config,
reseller_item => $reseller_item,
err_code => $err_code);
}
}
return;
}
sub _create_rtc_user {
my ($resource, $config, $reseller_item, $err_code) = @_;
my %params = @_;
my ($resource, $config, $reseller_item, $err_code) =
@params{qw/resource config reseller_item err_code/};
my $rtc_networks = $resource->{rtc_networks} // [];
if ('ARRAY' ne (ref $rtc_networks)) {
@ -103,16 +122,18 @@ sub _create_rtc_user {
{xms => JSON::false},
$user->{data}{id},
);
if ($user->{code} != 201) {
if ($n_response->{code} != 201) {
return unless &{$err_code}(
'Creating rtc network failed. Error code: ' . $user->{code});
'Creating rtc network failed. Error code: ' . $n_response->{code});
}
}
return;
}
sub _delete_rtc_user {
my ($config, $reseller_item, $err_code) = @_;
my %params = @_;
my ($config, $reseller_item, $err_code) =
@params{qw/config reseller_item err_code/};
my $comx = NGCP::Panel::Utils::ComxAPIClient->new(
host => $config->{rtc}{schema}.'://'.
@ -146,6 +167,120 @@ sub _delete_rtc_user {
return;
}
sub get_rtc_networks {
my %params = @_;
my ($rtc_user_id, $config, $reseller_item, $include_id, $err_code) =
@params{qw/rtc_user_id config reseller_item include_id err_code/};
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
}
my $comx = NGCP::Panel::Utils::ComxAPIClient->new(
host => $config->{rtc}{schema}.'://'.
$config->{rtc}{host}.':'.$config->{rtc}{port}.
$config->{rtc}{path},
);
$comx->login(
$config->{rtc}{user},
$config->{rtc}{pass},
$config->{rtc}{host}.':'.$config->{rtc}{port});
if ($comx->login_status->{code} != 200) {
return unless &{$err_code}(
'Rtc Login failed. Check config settings.');
}
my $networks_resp = $comx->get_networks_by_user_id($rtc_user_id);
my $networks = $networks_resp->{data};
unless (defined $networks && 'ARRAY' eq ref $networks && @{ $networks }) {
return unless &{$err_code}(
'Fetching networks failed. Code: ' . $networks_resp->{code});
}
my $res = [map {{
config =>$_->{config},
connector => $_->{connector},
tag => $_->{tag},
$include_id ? (id => $_->{id}) : (),
}} @{ $networks }];
return $res;
}
sub modify_rtc_networks {
my %params = @_;
my ($old_resource, $resource, $config, $reseller_item, $err_code) =
@params{qw/old_resource resource config reseller_item err_code/};
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
}
if ((!defined $old_resource) || (!defined $resource)) { # can only modify (no create/delete) the whole resource
return unless &{$err_code}(
'Cannot Modify rtc network. Old or new resource missing.');
}
my $comx = NGCP::Panel::Utils::ComxAPIClient->new(
host => $config->{rtc}{schema}.'://'.
$config->{rtc}{host}.':'.$config->{rtc}{port}.
$config->{rtc}{path},
);
$comx->login(
$config->{rtc}{user},
$config->{rtc}{pass},
$config->{rtc}{host}.':'.$config->{rtc}{port});
if ($comx->login_status->{code} != 200) {
return unless &{$err_code}(
'Rtc Login failed. Check config settings.');
}
my (@deleted, @new);
for my $nw (@{ $resource->{networks} }) {
my $nw_tag = $nw->{tag};
my ($old_nw) = grep {$nw_tag eq $_->{tag}} @{ $old_resource->{networks} };
if (!defined $old_nw) {
push @new, $nw;
} else {
if ($nw->{connector} ne $old_nw->{connector}
|| !compare($nw->{config}, $old_nw->{config})
) {
push @deleted, $old_nw;
push @new, $nw;
}
}
}
for my $nw (@{ $old_resource->{networks} }) {
my $nw_tag = $nw->{tag};
my ($new_nw) = grep {$nw_tag eq $_->{tag}} @{ $resource->{networks} };
if (!defined $new_nw) {
push @deleted, $nw;
}
}
for my $nw (@deleted) {
my $n_response = $comx->delete_network($nw->{id});
if ($n_response->{code} != 200) {
return unless &{$err_code}(
'Deleting rtc network failed. Error code: ' . $n_response->{code});
}
}
for my $nw (@new) {
my $n_response = $comx->create_network(
$nw->{tag},
$nw->{connector},
$nw->{config} // {},
$old_resource->{rtc_user_id},
);
if ($n_response->{code} != 201) {
return unless &{$err_code}(
'Creating rtc network failed. Error code: ' . $n_response->{code});
}
}
return;
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,91 @@
use strict;
use warnings;
use Test::More;
use Test::Collection;
use Test::FakeData;
use Data::Dumper;
unless ($ENV{TEST_RTC}) {
plan skip_all => "not testing rtc, enable TEST_RTC=yes to run tests";
exit 0;
}
my $fake_data = Test::FakeData->new;
$fake_data->set_data_from_script({
'resellers' => {
'data' => {
name => "apitest reseller name " . time(),
contract_id => sub { return shift->create('contracts', @_); },
status => 'active',
enable_rtc => 1, # JSON::false
rtc_networks => ['sip', 'xmpp', 'sipwise'],
},
},
});
my $test_machine = Test::Collection->new(
name => 'resellers',
embedded_resources => [qw/resellers/],
);
$test_machine->methods->{collection}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS)};
$test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS PUT PATCH DELETE)};
# store some basic reseller data, to run tests with
$test_machine->DATA_ITEM_STORE($fake_data->process('resellers'));
$test_machine->form_data_item( );
my $reseller_id;
# test reseller API
{
my ($res, $content) = $test_machine->check_item_get('/api/resellers/?page=1&rows=10', "fetch resellers collection");
my $req;
($res, $content, $req) = $test_machine->check_item_post();
is($res->code, 201, 'create test reseller successful');
#my $reseller_id = $test_machine->get_id_from_created($res);
($reseller_id) = $res->header('Location') =~ m/(\d+)$/;
cmp_ok($reseller_id, '>', 0, 'got valid reseller id');
($res, $content) = $test_machine->check_item_get("/api/resellers/$reseller_id/", "fetch created reseller");
is($res->code, 200, 'reseller successfully retrieved');
ok($content->{enable_rtc}, 'rtc is enabled on created reseller');
}
# test rtcnetworks API
{
my ($res, $content) = $test_machine->check_item_get("/api/rtcnetworks/$reseller_id", "fetch rtcnetwork");
is($res->code, 200, 'rtcnetwork successfully retrieved');
isa_ok($content->{networks}, 'ARRAY', 'networks arrayref exists');
is(scalar(@{ $content->{networks} }), 3, 'should contain the 3 precreated networks');
is($content->{networks}[0]{connector}, 'sip-connector', 'First network is of "sip-connector"');
($res, $content) = $test_machine->request_patch(
[
{ op => 'remove', path => '/networks/2'},
{ op => 'replace', path => '/networks/1/connector', value => 'webrtc'},
],
"/api/rtcnetworks/$reseller_id/",
);
is($res->code, 200, 'PATCH operation on rtcnetworks item');
isa_ok($content->{networks}, 'ARRAY', 'networks arrayref exists');
is(scalar(@{ $content->{networks} }), 2, 'should be left with 2 networks');
is($content->{networks}[1]{connector}, 'webrtc', 'Changed one network to "webrtc"');
}
{
my ($res, $content, $req) = $test_machine->request_patch(
[
{ op => 'replace', path => '/status', value => 'terminated' },
],
"/api/resellers/$reseller_id/",
);
is($res->code, 200, 'terminate reseller successful');
}
$test_machine->clear_test_data_all();
done_testing;
1;
# vim: set tabstop=4 expandtab:
Loading…
Cancel
Save