TT#175000 remove rtcengine logic

* rtcengine related logic and apps is now removed
* remove /api/rtcapps endpoint
* remove /api/rtcnetworks endpoint
* remove rtcengine relations from resellers such as
  enable_rtc flags
* remove rtcengine related API tests
* remove rtcengine and comx related libraries
* remove csc webphone ui app
* remove webrtc related selenium tests
* remove rtcengine flags from /api/capabilities

Change-Id: I83a4b0457fac2e0df23d267f8dbc82841dfb3001
mr11.0
Kirill Solomko 3 years ago
parent 9d7990ef47
commit d3dc152cdc

@ -11,7 +11,6 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::Reseller;
use NGCP::Panel::Utils::Rtc;
sub allowed_methods{
return [qw/GET POST OPTIONS HEAD/];
@ -169,16 +168,6 @@ 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(
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
die "failed to create rtcengine reseller";
});
} 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.");

@ -1,86 +0,0 @@
package NGCP::Panel::Controller::API::RtcApps;
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);
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
sub api_description {
return 'Show a collection of RTC apps, belonging to a specific reseller.';
};
sub query_params {
return [];
}
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::RtcApps/;
sub resource_name{
return 'rtcapps';
}
sub dispatch_path{
return '/api/rtcapps/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-rtcapps';
}
__PACKAGE__->set_config({
allowed_roles => [qw/admin reseller/],
});
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, my $resellers_rows) = $self->paginate_order_collection($c, $resellers);
my (@embedded, @links);
for my $reseller (@$resellers_rows) {
push @embedded, $self->hal_from_item($c, $reseller);
push @links, Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
href => sprintf('%s%d', $self->dispatch_path, $reseller->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/'),
$self->collection_nav_links($c, $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;
}
1;
# vim: set tabstop=4 expandtab:

@ -1,194 +0,0 @@
package NGCP::Panel::Controller::API::RtcAppsItem;
use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
require Catalyst::ActionRole::ACL;
require NGCP::Panel::Role::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
sub allowed_methods{
return [qw/GET OPTIONS HEAD PATCH PUT/];
}
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::RtcApps/;
sub resource_name{
return 'rtcapps';
}
sub dispatch_path{
return '/api/rtcapps/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-rtcapps';
}
sub journal_query_params {
my($self,$query_params) = @_;
return $self->get_journal_query_params($query_params);
}
__PACKAGE__->set_config({
allowed_roles => {
Default => [qw/admin reseller/],
Journal => [qw/admin reseller/],
}
});
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 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(@_);
}
1;
# vim: set tabstop=4 expandtab:

@ -1,86 +0,0 @@
package NGCP::Panel::Controller::API::RtcNetworks;
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);
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
sub api_description {
return 'Show a collection of RTC networks, belonging to a specific reseller.';
};
sub query_params {
return [];
}
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::RtcNetworks/;
sub resource_name{
return 'rtcnetworks';
}
sub dispatch_path{
return '/api/rtcnetworks/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-rtcnetworks';
}
__PACKAGE__->set_config({
allowed_roles => [qw/admin reseller/],
});
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, my $resellers_rows) = $self->paginate_order_collection($c, $resellers);
my (@embedded, @links);
for my $subscriber (@$resellers_rows) {
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/'),
$self->collection_nav_links($c, $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;
}
1;
# vim: set tabstop=4 expandtab:

@ -1,194 +0,0 @@
package NGCP::Panel::Controller::API::RtcNetworksItem;
use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
require Catalyst::ActionRole::ACL;
require NGCP::Panel::Role::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
sub allowed_methods{
return [qw/GET OPTIONS HEAD PATCH PUT/];
}
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::RtcNetworks/;
sub resource_name{
return 'rtcnetworks';
}
sub dispatch_path{
return '/api/rtcnetworks/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-rtcnetworks';
}
sub journal_query_params {
my($self,$query_params) = @_;
return $self->get_journal_query_params($query_params);
}
__PACKAGE__->set_config({
allowed_roles => {
Default => [qw/admin reseller/],
Journal => [qw/admin reseller/],
}
});
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 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(@_);
}
1;
# vim: set tabstop=4 expandtab:

@ -1,147 +0,0 @@
package NGCP::Panel::Controller::API::RtcSessions;
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);
sub api_description {
return 'Show a collection of RTC sessions, belonging to a specific subscriber.';
}
sub allowed_methods{
return [qw/GET POST OPTIONS HEAD/];
}
sub query_params {
return [];
}
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::RtcSessions/;
sub resource_name{
return 'rtcsessions';
}
sub dispatch_path{
return '/api/rtcsessions/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-rtcsessions';
}
__PACKAGE__->set_config({
allowed_roles => [qw/admin reseller subscriber subscriberadmin/],
});
sub GET :Allow {
my ($self, $c) = @_;
my $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
{
my $subscribers = $self->item_rs($c);
(my $total_count, $subscribers, my $subscribers_rows) = $self->paginate_order_collection($c, $subscribers);
my (@embedded, @links);
for my $subscriber (@$subscribers_rows) {
my $hal = $self->hal_from_item($c, $subscriber);
next unless $hal;
push @embedded, $hal;
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/'),
$self->collection_nav_links($c, $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 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;
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$resource->{reseller_id} = $c->user->reseller_id; # TODO: ?
} else {
$resource->{subscriber_id} = $c->user->voip_subscriber->id;
}
my $subscriber_item = $c->model('DB')->resultset('voip_subscribers')->search_rs({
id => $resource->{subscriber_id},
})->first;
unless ($subscriber_item) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Subscriber invalid or not found.");
last;
}
# my $form = $self->get_form();
# $resource->{reseller_id} //= undef;
# last unless $self->validate_form(
# c => $c,
# resource => $resource,
# form => $form,
# );
my $session_item = NGCP::Panel::Utils::Rtc::create_rtc_session(
config => $c->config,
subscriber_item => $subscriber_item,
resource => $resource,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, $msg);
return;
});
last unless $session_item;
$guard->commit;
$c->response->status(HTTP_CREATED);
$c->response->header(Location => sprintf('%s%d', $self->dispatch_path, $session_item->id));
$c->response->body(q());
}
return;
}
1;
# vim: set tabstop=4 expandtab:

@ -1,80 +0,0 @@
package NGCP::Panel::Controller::API::RtcSessionsItem;
use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
require Catalyst::ActionRole::ACL;
require NGCP::Panel::Role::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::RtcSessions/;
sub resource_name{
return 'rtcsessions';
}
sub dispatch_path{
return '/api/rtcsessions/';
}
sub relation{
return 'http://purl.org/sipwise/ngcp-api/#rel-rtcsessions';
}
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
sub journal_query_params {
my($self,$query_params) = @_;
return $self->get_journal_query_params($query_params);
}
sub get_journal_methods{
return [qw/handle_item_base_journal handle_journals_get handle_journalsitem_get handle_journals_options handle_journalsitem_options handle_journals_head handle_journalsitem_head/];
}
__PACKAGE__->set_config({
allowed_roles => {
Default => [qw/admin reseller subscriber subscriberadmin/],
Journal => [qw/admin reseller/],
}
});
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, rtc_session => $item);
my $hal = $self->hal_from_item($c, $item);
unless ($hal) {
$c->log->error("Session not found. It may have expired.");
$self->error($c, HTTP_NOT_FOUND, "Session not found. It may have expired.");
last;
}
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;
}
1;
# vim: set tabstop=4 expandtab:

@ -31,8 +31,6 @@ sub auto :Private {
sub list_reseller :Chained('/') :PathPart('reseller') :CaptureArgs(0) {
my ($self, $c) = @_;
my $can_rtc = exists $c->config->{rtc};
$c->stash(
resellers => $c->model('DB')
->resultset('resellers')->search({
@ -46,7 +44,6 @@ sub list_reseller :Chained('/') :PathPart('reseller') :CaptureArgs(0) {
{ name => "contract_id", search => 1, title => $c->loc("Contract #") },
{ name => "name", search => 1, title => $c->loc("Name") },
{ name => "status", search => 1, title => $c->loc("Status") },
$can_rtc ? { name => "enable_rtc", search => 0, title => $c->loc("RTC") } : (),
]);
# we need this in ajax_contracts also
@ -67,10 +64,7 @@ sub root :Chained('list_reseller') :PathPart('') :Args(0) :Does(ACL) :ACLDetachT
sub ajax :Chained('list_reseller') :PathPart('ajax') :Args(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(ccareadmin) :AllowedRole(lintercept){
my ($self, $c) = @_;
my $resellers = $c->stash->{resellers};
NGCP::Panel::Utils::Datatables::process($c, $resellers, $c->stash->{reseller_dt_columns}, sub {
my ($item) = @_;
return (enable_rtc => ( $item->rtc_user ? $c->loc("yes") : $c->loc("no") ));
});
NGCP::Panel::Utils::Datatables::process($c, $resellers, $c->stash->{reseller_dt_columns});
$c->detach($c->view('JSON'));
return;
}
@ -85,13 +79,7 @@ sub create :Chained('list_reseller') :PathPart('create') :Args(0) :Does(ACL) :AC
$params = merge($params, $c->session->{created_objects});
my $posted = $c->request->method eq 'POST';
my $can_rtc = exists $c->config->{rtc};
my $form;
if ($can_rtc) {
$form = NGCP::Panel::Form::get("NGCP::Panel::Form::ResellerRtc", $c);
} else {
$form = NGCP::Panel::Form::get("NGCP::Panel::Form::Reseller", $c);
}
my $form = NGCP::Panel::Form::get("NGCP::Panel::Form::Reseller", $c);
$form->process(
posted => $posted,
params => $c->request->params,
@ -121,18 +109,6 @@ sub create :Chained('list_reseller') :PathPart('create') :Args(0) :Does(ACL) :AC
status => $form->values->{status},
});
NGCP::Panel::Utils::Reseller::create_email_templates( c => $c, reseller => $reseller );
my $resource = $form->values;
$resource->{rtc_networks} = [qw/sip xmpp webrtc conference/];
NGCP::Panel::Utils::Rtc::modify_reseller_rtc(
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
die $msg,"\n";
});
delete $c->session->{created_objects}->{contract};
$c->session->{created_objects}->{reseller} = { id => $reseller->id };
@ -292,13 +268,7 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) :Does(ACL) :ACLDetachTo('/d
my $reseller = $c->stash->{reseller}->first;
my $posted = $c->request->method eq 'POST';
my $can_rtc = exists $c->config->{rtc};
my $form;
if ($can_rtc) {
$form = NGCP::Panel::Form::get("NGCP::Panel::Form::ResellerRtc", $c);
} else {
$form = NGCP::Panel::Form::get("NGCP::Panel::Form::Reseller", $c);
}
my $form = NGCP::Panel::Form::get("NGCP::Panel::Form::Reseller", $c);
# we need this in the ajax call to not filter it as used contract
$c->session->{edit_contract_id} = $reseller->contract_id;
@ -306,7 +276,6 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) :Does(ACL) :ACLDetachTo('/d
my $params = { $reseller->get_inflated_columns };
$params->{contract}{id} = delete $params->{contract_id};
$params = merge($params, $c->session->{created_objects});
$params->{enable_rtc} = !!$reseller->rtc_user;
$form->process(
posted => $posted,
params => $c->request->params,
@ -320,7 +289,6 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) :Does(ACL) :ACLDetachTo('/d
);
if($posted && $form->validated) {
my $rtc_err = '';
try {
$c->model('DB')->txn_do(sub {
$form->params->{contract_id} = delete $form->params->{contract}{id};
@ -331,20 +299,6 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) :Does(ACL) :ACLDetachTo('/d
name => $form->values->{name},
status => $form->values->{status},
});
my $resource = $form->values;
$resource->{rtc_networks} = [qw/sip xmpp webrtc conference/];
NGCP::Panel::Utils::Rtc::modify_reseller_rtc(
old_resource => $params,
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
die $msg,"\n";
},
);
if($reseller->status ne $old_status) {
NGCP::Panel::Utils::Reseller::_handle_reseller_status_change($c, $reseller);
@ -392,19 +346,7 @@ sub terminate :Chained('base') :PathPart('terminate') :Args(0) :Does(ACL) :ACLDe
try {
$c->model('DB')->txn_do(sub {
my $old_status = $reseller->status;
my $old_enable_rtc = !!$reseller->rtc_user;
$reseller->update({ status => 'terminated' });
NGCP::Panel::Utils::Rtc::modify_reseller_rtc(
old_resource => {status => $old_status, enable_rtc => $old_enable_rtc},
resource => {status => 'terminated'},
config => $c->config,
reseller_item => $reseller,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
die $msg,"\n";
});
if($reseller->status ne $old_status) {
NGCP::Panel::Utils::Reseller::_handle_reseller_status_change($c,$reseller);
@ -518,7 +460,6 @@ sub create_defaults :Path('create_defaults') :Args(0) :Does(ACL) :ACLDetachTo('/
contract_id => $r{contracts}->id,
});
NGCP::Panel::Utils::Reseller::create_email_templates( c => $c, reseller => $r{resellers} );
#TODO: do we need also to call NGCP::Panel::Utils::Rtc::modify_reseller_rtc ???
my $mappings_to_create = [];
my $resource = { $r{contracts}->get_inflated_columns };
$resource->{billing_profile_id} = 1;

@ -314,52 +314,6 @@ sub webfax_ajax :Chained('base') :PathPart('webfax/ajax') :Args(0) {
$c->detach( $c->view("JSON") );
}
sub webphone :Chained('base') :PathPart('webphone') :Args(0) {
my ($self, $c) = @_;
$c->stash(template => 'subscriber/webphone.tt');
}
sub webphone_ajax :Chained('base') :PathPart('webphone/ajax') :Args(0) {
my ($self, $c) = @_;
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$c->detach('/denied_page')
unless($c->stash->{subscriber}->contract->contact->reseller_id != $c->user->reseller_id);
} elsif($c->user->roles eq "subscriberadmin") {
$c->detach('/denied_page')
unless($c->stash->{subscriber}->contract_id != $c->user->account_id);
} else {
}
my $subscriber = $c->stash->{subscriber}->provisioning_voip_subscriber;
# TODO: use from config.yml.
# Important: ws vs wss (issues with self-signed certs on cross-domain)
my $config = {
sip => {
# wss/5061 vs ws/5060
ws_servers => 'wss://' . $c->request->uri->host . ':' . $c->request->uri->port . '/wss/sip/',
uri => 'sip:' . $subscriber->username . '@' . $subscriber->domain->domain,
password => $subscriber->password,
},
xmpp => {
# wss/5281 vs ws/5280
# - ws causes "insecure" error in firefox
# - wss fails if self signed cert is not accepted in firefox/chromium
wsURL => 'wss://' . $c->request->uri->host . ':' . $c->request->uri->port . '/wss/xmpp/',
jid => $subscriber->username . '@' . $subscriber->domain->domain,
server => $subscriber->domain->domain,
credentials => { password => $subscriber->password },
},
};
$c->stash(aaData => $config);
$c->detach( $c->view("JSON") );
}
sub ajax :Chained('sub_list') :PathPart('ajax') :Args(0) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) :AllowedRole(ccareadmin) :AllowedRole(ccare) {
my ($self, $c) = @_;

@ -10,36 +10,10 @@ has_field 'submitid' => ( type => 'Hidden' );
sub build_render_list {[qw/submitid fields actions/]}
sub build_form_element_class { [qw/form-horizontal/] }
has_field 'enable_rtc' => (
type => 'Boolean',
required => 0,
default => 0,
element_attr => {
rel => ['tooltip'],
title => ['Whether an RTC-entity should be created for this reseller.'],
}
);
has_field 'rtc_networks' => (
type => 'Multiple', # Select
required => '0',
widget => 'CheckboxGroup',
options => [
{ value => 'sip', label => 'SIP' },
{ value => 'xmpp', label => 'XMPP' },
{ value => 'webrtc', label => 'WebRTC' },
{ value => 'conference', label => 'Conference' },
],
element_attr => {
rel => ['tooltip'],
title => ['The RTC networks that should be preinitialized for this reseller.'],
}
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/contract name status enable_rtc rtc_networks/],
render_list => [qw/contract name status/],
);
1;

@ -1,30 +0,0 @@
package NGCP::Panel::Form::ResellerRtc;
use HTML::FormHandler::Moose;
extends 'NGCP::Panel::Form::Reseller';
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 'enable_rtc' => (
type => 'Boolean',
required => 0,
default => 0,
element_attr => {
rel => ['tooltip'],
title => ['Whether an RTC-entity should be created for this reseller.'],
}
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/contract name status enable_rtc/],
);
1;
# vim: set tabstop=4 expandtab:

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

@ -1,95 +0,0 @@
package NGCP::Panel::Form::Rtc::AppsReseller;
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).'],
},
readonly => 1,
);
has_field 'apps' => (
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 "name", "domain", "secret" and "api_key" to create RTC apps for this reseller'],
},
num_when_empty => 1,
add_extra => 1,
);
has_field 'apps.domain' => (
type => 'Text',
element_attr => {
rel => ['tooltip'],
title => ['Domain where the cdk is included.'],
},
);
has_field 'apps.name' => (
type => 'Text',
element_attr => {
rel => ['tooltip'],
title => ['Arbitrary text. Name of the app.'],
},
);
has_field 'apps.secret' => (
type => 'Text',
element_attr => {
rel => ['tooltip'],
title => ['The secret (readonly).'],
},
readonly => 1,
);
has_field 'apps.api_key' => (
type => 'Text',
element_attr => {
rel => ['tooltip'],
title => ['The API key (readonly).'],
},
readonly => 1,
);
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 apps/],
);
has_block 'actions' => (
tag => 'div',
class => [qw/modal-footer/],
render_list => [qw/save/],
);
1;

@ -1,22 +0,0 @@
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:

@ -1,90 +0,0 @@
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).'],
},
readonly => 1,
);
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;

@ -52,29 +52,24 @@ sub hal_from_item {
sub _item_rs {
my ($self, $c) = @_;
my ($cloudpbx, $sms, $faxserver, $rtcengine, $fileshare, $mobilepush, $csc_show_rtcengine_features);
my ($cloudpbx, $sms, $faxserver, $fileshare, $mobilepush);
$cloudpbx = $c->config->{features}->{cloudpbx} // 0;
$sms = $c->config->{features}->{sms} // 0;
$faxserver = $c->config->{features}->{faxserver} // 0;
$rtcengine = $c->config->{features}->{rtcengine} // 0;
$fileshare = $c->config->{features}->{fileshare} // 0;
$mobilepush = $c->config->{features}->{mobilepush} // 0;
$csc_show_rtcengine_features = $c->config->{features}->{csc_show_rtcengine_features} // 0;
if($c->user->roles eq "admin") {
# nothing to be done
} elsif($c->user->roles eq "reseller") {
# TODO: is it correct to just check rtc_user of reseller?
$rtcengine &= ($c->user->reseller->rtc_user // 0);
# nothing to be done
} else {
my $customer = $c->user->voip_subscriber->contract;
$rtcengine &= ($customer->contact->reseller->rtc_user // 0);
my $cpbx = ($customer->product->class eq 'pbxaccount') ? 1 : 0;
$cloudpbx &= $cpbx;
# TODO: sms and rtcengine are not specially restricted; should it?
my $profile = $c->user->voip_subscriber_profile;
if($profile) {
my $attrs = [ map { $_->attribute->attribute } $profile->profile_attributes->all ];
@ -90,10 +85,8 @@ sub _item_rs {
{ id => 1, name => 'cloudpbx', enabled => $cloudpbx },
{ id => 2, name => 'sms', enabled => $sms },
{ id => 3, name => 'faxserver', enabled => $faxserver },
{ id => 4, name => 'rtcengine', enabled => $rtcengine },
{ id => 5, name => 'fileshare', enabled => $fileshare},
{ id => 6, name => 'mobilepush', enabled => $mobilepush},
{ id => 7, name => 'csc_show_rtcengine_features', enabled => $csc_show_rtcengine_features},
];
if($c->req->param('name')) {

@ -30,8 +30,6 @@ sub resource_from_item {
my %resource = $item->get_inflated_columns;
$resource{enable_rtc} = $item->rtc_user ? JSON::true : JSON::false;
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
@ -139,25 +137,6 @@ sub update_reseller {
contract_id => $resource->{contract_id},
});
eval {
NGCP::Panel::Utils::Rtc::modify_reseller_rtc(
old_resource => $old_resource,
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
die $msg,"\n";
});
};
my $rtc_err = $@ // '';
if ($rtc_err) {
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Could not modify rtc_user: $rtc_err");
return;
}
if($old_resource->{status} ne $resource->{status}) {
NGCP::Panel::Utils::Reseller::_handle_reseller_status_change($c, $reseller);
}

@ -1,143 +0,0 @@
package NGCP::Panel::Role::API::RtcApps;
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 JSON::Types;
use NGCP::Panel::Utils::Subscriber;
use NGCP::Panel::Utils::Rtc;
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::get("NGCP::Panel::Form::Rtc::AppsAdmin", $c);
}
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->{apps} = NGCP::Panel::Utils::Rtc::get_rtc_apps(
rtc_user_id => $rtc_user_id,
config => $c->config,
include_id => $include_id,
err_code => sub {
my $rtc_error = shift;
$c->log->warn($rtc_error);
return;
});
} else {
}
#for get=>put compatibility
if ('ARRAY' ne ref $resource->{apps}) {
$resource->{apps} = [];
}
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)),
Data::HAL::Link->new(relation => 'ngcp:resellers', href => sprintf("/api/rtcnetworks/%d", $item->id)),
$self->get_journal_relation_link($c, $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,
);
}
$self->expand_fields($c, $resource);
$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->{apps} ne "ARRAY") {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid field 'apps'. 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_apps(
old_resource => $old_resource,
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
my $rtc_error = shift;
$c->log->warn($rtc_error);
return;
});
try {
} catch($e) {
$c->log->error("failed to update rtcapps: $e");
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update rtcapps.");
return;
};
return $reseller;
}
1;
# vim: set tabstop=4 expandtab:

@ -1,142 +0,0 @@
package NGCP::Panel::Role::API::RtcNetworks;
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 JSON::Types;
use NGCP::Panel::Utils::Subscriber;
use NGCP::Panel::Utils::Rtc;
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::get("NGCP::Panel::Form::Rtc::NetworksAdmin", $c);
}
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 {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
return;
});
} else {
}
#for get=>put compatibility
if ('ARRAY' ne ref $resource->{networks}) {
$resource->{networks} = [];
}
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($c, $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,
);
}
$self->expand_fields($c, $resource);
$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,
);
try {
NGCP::Panel::Utils::Rtc::modify_rtc_networks(
old_resource => $old_resource,
resource => $resource,
config => $c->config,
reseller_item => $reseller,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
return;
});
} catch($e) {
$c->log->error("failed to update rtcnetworks: $e");
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update rtcnetworks.");
return;
};
return $reseller;
}
1;
# vim: set tabstop=4 expandtab:

@ -1,103 +0,0 @@
package NGCP::Panel::Role::API::RtcSessions;
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 JSON::Types;
use NGCP::Panel::Utils::Subscriber;
use NGCP::Panel::Utils::Rtc;
sub get_form {
my ($self) = @_;
#return NGCP::Panel::Form::get("NGCP::Panel::Form::Rtc::NetworksAdmin", $c);
return;
}
sub hal_from_item {
my ($self, $c, $item) = @_;
my $resource = {
subscriber_id => $item->subscriber->voip_subscriber->id, # this may be confusing but we store the provisioning-subscriber-id but show the billing one
rtc_network_tag => $item->rtc_network_tag,
};
my $rtc_session = NGCP::Panel::Utils::Rtc::get_rtc_session(
config => $c->config,
item => $item,
err_code => sub {
my ($msg, $debug) = @_;
$c->log->debug($debug) if $debug;
$c->log->warn($msg);
return;
});
if ($rtc_session) {
$resource->{rtc_browser_token} = $rtc_session->{data}{token};
$resource->{rtc_app_name} = $rtc_session->{data}{app}{name} if $rtc_session->{data}{app}{name};
} else {
# here either delete our DB entry, or recreate it accordingly
$item->delete;
return;
}
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:subscribers', href => sprintf("/api/subscribers/%d", $item->subscriber->voip_subscriber->id)),
$self->get_journal_relation_link($c, $item->id),
],
relation => 'ngcp:'.$self->resource_name,
);
$self->expand_fields($c, $resource);
$hal->resource($resource);
return $hal;
}
sub item_rs {
my ($self, $c) = @_;
my $item_rs;
$item_rs = $c->model('DB')->resultset('rtc_session');
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({
'contact.reseller_id' => $c->user->reseller_id,
},{
join => {subscriber => { voip_subscriber => { contract => 'contact' }}},
});
} else {
$item_rs = $item_rs->search({
'subscriber.id' => $c->user->id,
},{
join => 'subscriber',
});
}
return $item_rs;
}
sub item_by_id {
my ($self, $c, $id) = @_;
my $item_rs = $self->item_rs($c);
return $item_rs->find($id);
}
1;
# vim: set tabstop=4 expandtab:

@ -1,306 +0,0 @@
package NGCP::Panel::Utils::ComxAPIClient;
use warnings;
use strict;
use Moo;
# use Digest::MD5 qw/md5_hex/;
# use HTTP::Tiny;
# use Storable qw/freeze/;
use Types::Standard qw(Int HashRef);
# with 'Role::REST::Client';
use LWP::UserAgent;
use JSON qw/decode_json encode_json/;
has 'ua' => ( is => 'rw', default => sub {
return LWP::UserAgent->new(
ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0 },
timeout => 20,
);
});
has 'host' => (is => 'rw', default => 'https://www.api-cdk.tld:8191');
has 'login_status' => (is => 'rw',
#isa => 'HTTP::Response',
default => sub {return {};},
);
# returns appid or 0
sub login {
my ( $self, $username, $password, $netloc ) = @_;
my $ua = $self->ua;
$netloc //= $self->host =~ s!^https?://(.*:[0-9]*)(/.*$|$)!$1!r;
$ua->credentials($netloc, "rtcengine", $username, $password);
my $resp = $ua->get($self->host . '/users');
$self->login_status( $self->_create_response($resp) );
return;
}
# outdated: only one account (with one network) for the session
sub create_session_and_account {
my ($self, $appid, $network_tag, $identifier, $access_token, $owner, $account_config) = @_;
my $session = $self->create_session($appid, $owner);
$session->{data}{accounts} = [] if($session->{data});
my $account = $self->create_account(
$session->{data}{id},
$owner,
$identifier,
$network_tag,
$access_token,
$account_config );
push @{ $session->{data}{accounts} }, $account->{data};
return $session;
}
sub create_session {
my ($self, $appid, $owner) = @_;
my $ua = $self->ua;
my $session_content = encode_json({
app => $appid,
owner => $owner,
});
my $session = $self->_create_response(
$ua->post($self->host . '/sessions', 'Content-Type' => 'application/json', Content => $session_content),
);
return $session;
}
sub create_account {
my ($self, $session_id, $owner, $identifier, $network_tag, $access_token, $account_config) = @_;
my $ua = $self->ua;
my $account_content = encode_json({
session => $session_id,
network => $network_tag,
identifier => $identifier,
accessToken => $access_token,
owner => $owner,
$account_config ? (config => encode_json($account_config)) : (),
});
my $account = $self->_create_response(
$ua->post($self->host . '/accounts', 'Content-Type' => 'application/json', Content => $account_content),
);
return $account;
}
sub create_network {
my ($self, $tag, $connector, $config, $owner) = @_;
my $ua = $self->ua;
my $network_content = encode_json({
tag => $tag,
connector => $connector,
config => encode_json($config),
owner => $owner,
});
my $network = $self->_create_response(
$ua->post($self->host . '/networks', 'Content-Type' => 'application/json', Content => $network_content),
);
return $network;
}
sub create_user {
my ($self, $email, $password) = @_;
my $ua = $self->ua;
my $user_content = encode_json({
email => $email,
password => $password,
});
my $user = $self->_create_response(
$ua->post($self->host . '/users', 'Content-Type' => 'application/json', Content => $user_content),
);
return $user;
}
sub delete_network {
my ($self, $network_id) = @_;
my $ua = $self->ua;
$network_id //= "";
my $resp;
$resp = $ua->delete($self->host . "/networks/id/$network_id");
return $self->_create_response($resp);
}
sub delete_user {
my ($self, $user_id) = @_;
my $ua = $self->ua;
$user_id //= "";
my $resp;
$resp = $ua->delete($self->host . "/users/id/$user_id");
return $self->_create_response($resp);
}
sub delete_app {
my ($self, $app_id) = @_;
my $ua = $self->ua;
$app_id //= "";
my $resp;
$resp = $ua->delete($self->host . "/apps/id/$app_id");
return $self->_create_response($resp);
}
sub create_app {
my ($self, $name, $domain, $owner) = @_;
my $ua = $self->ua;
my $app_content = encode_json({
name => $name,
domain => $domain,
owner => $owner,
});
my $app = $self->_create_response(
$ua->post($self->host . '/apps', 'Content-Type' => 'application/json', Content => $app_content),
);
return $app;
}
sub get_sessions {
my ($self, $max_rows) = @_;
my $sessions = $self->_resolve_collection_fast( '/sessions', $max_rows );
if ('ARRAY' eq ref $sessions->{data} && @{ $sessions->{data} }) {
for my $session (@{ $sessions->{data} }) {
$session->{accounts} = $self->_resolve_collection_fast( $session->{accounts}{href} );
}
}
return $sessions;
}
sub get_session {
my ($self, $session_id) = @_;
my $ua = $self->ua;
my $session = $self->_create_response(
$ua->get($self->host . "/sessions/id/$session_id"),
);
if ($session->{data}) {
my ($app_id) = $session->{data}{app}{href} =~ m!apps/id/(.*)$!;
my $item_res = $ua->get($self->host . "/apps/id/$app_id");
my $item_data = decode_json($item_res->content);
$session->{data}{app} = $item_data;
}
return $session;
}
sub delete_all_sessions {
my ($self) = @_;
my $ua = $self->ua;
my $resp;
for my $session_data ($self->get_sessions->{data}) {
my $session_id = $session_data->{id};
$resp = $ua->delete($self->host . "/sessions/id/$session_id");
last if $resp->code >= 300;
}
return $resp;
}
sub get_users {
my ($self, $max_rows) = @_;
my $users = $self->_resolve_collection_fast( '/users', $max_rows );
return $users;
}
sub get_apps_by_user_id {
my ($self, $user_id) = @_;
my $apps = $self->_resolve_collection_fast( "/users/id/$user_id/apps" );
return $apps;
}
sub get_networks {
my ($self) = @_;
my $networks = $self->_resolve_collection_fast( '/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;
my $rel_url = $self->_strip_host( $bare_url );
my $res = $ua->get($self->host . $rel_url);
my @result;
return {code => $res->code, response => $res} unless $res->code == 200;
my $collection = JSON::decode_json($res->content);
return {code => $res->code, response => $res,
error_detail => 'could not decode_json'} unless $collection;
my $item_res;
for my $item (@{ $collection->{items} }) {
last if (defined $max_rows && $max_rows-- <= 0);
my $url = $self->_strip_host( $item->{href} );
$item_res = $ua->get($self->host . $url);
my $item_data = decode_json($item_res->content);
push @result, $item_data;
}
return {
response => $item_res, # latest response
code => $item_res->code,
data => \@result,
total_count => scalar(@result),
};
}
sub _resolve_collection_fast {
my ($self, $bare_url, $max_rows) = @_;
my $ua = $self->ua;
my $rel_url = $self->_strip_host( $bare_url );
$rel_url =
$rel_url .
( ($rel_url =~ m/\?/) ? '&' : '?' ) .
'expand=true';
my $res = $ua->get($self->host . $rel_url);
my @result;
return $self->_create_response($res) unless $res->code == 200;
my $collection = JSON::decode_json($res->content);
return {code => $res->code, response => $res,
error_detail => 'could not decode_json'} unless $collection;
if ('HASH' eq ref $collection) { # everything ok
return {
response => $res,
code => $res->code,
data => $collection->{items},
total_count => $collection->{total} // (scalar @{ $collection->{items} }),
};
} else { # unknown error
return {
response => $res,
code => $res->code,
data => $collection,
};
}
}
sub _strip_host {
my ($self, $url) = @_;
my $url_orig = $self->host;
my $url_noip = $url_orig =~ s!:\d+!!r;
return $url =~ s!$url_orig|$url_noip!!r;
}
sub _create_response {
my ($self, $res) = @_;
my $data;
my $debug;
if ($res->is_success && $res->content) {
$data = decode_json($res->content);
} else {
$debug = "RTC response: " . $res->decoded_content
. ", RTC request: " . $res->request->as_string;
}
return {
code => $res->code,
data => $data,
response => $res,
$debug ? (debug => $debug) : (),
};
}
1;
# vim: set tabstop=4 expandtab:

@ -1,747 +0,0 @@
package NGCP::Panel::Utils::Rtc;
use warnings;
use strict;
use JSON qw//;
use UUID;
use NGCP::Panel::Utils::ComxAPIClient;
use NGCP::Panel::Utils::Generic qw/compare/;
sub modify_reseller_rtc {
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)) { # newly created reseller
# 1. enable_rtc is off -> do nothing
if (!$resource->{enable_rtc}) {
return;
}
_create_rtc_user(
resource => $resource,
config => $config,
reseller_item => $reseller_item,
err_code => $err_code);
} elsif ((defined $old_resource) && (defined $resource)) {
if($old_resource->{status} ne 'terminated' &&
$resource->{status} eq 'terminated' &&
$old_resource->{enable_rtc}) { # just terminated
$resource->{enable_rtc} = JSON::false;
_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 => $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 => $resource,
config => $config,
reseller_item => $reseller_item,
err_code => $err_code);
}
}
return;
}
sub _create_rtc_user {
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)) {
$rtc_networks = [$rtc_networks];
}
# 2. create user w reseller-name and reseller-name _ "pass"
my $reseller_name = $resource->{name} =~ s/\s+//rg;
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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
}
my ($uuid_bin, $uuid);
UUID::generate($uuid_bin);
UUID::unparse($uuid_bin, $uuid);
my $rand = get_random(10, $err_code);
return unless $rand;
my $pass = unpack("H*", $rand);
my $user = $comx->create_user(
$uuid . '@ngcp.local',
$pass,
);
if ($user->{code} != 201) {
return unless &{$err_code}(
'Creating rtc user failed. Error code: ' . $user->{code}, $user->{debug});
}
# 3. create relation in our db
$reseller_item->create_related('rtc_user', {
rtc_user_id => $user->{data}{id},
});
# 4. create related app
my $app = $comx->create_app(
$uuid . '_default_app',
$uuid . '.sipwise.local',
$user->{data}{id},
);
if ($app->{code} != 201) {
return unless &{$err_code}(
'Creating rtc app failed. Error code: ' . $app->{code}, $app->{debug});
}
# 5. create related networks
for my $n (@{ $rtc_networks }) {
my $connector;
if ($n =~ m/^(sip|xmpp)$/) {
$connector = "$n-connector";
} else {
$connector = $n;
}
my $n_response = $comx->create_network(
$n,
$connector,
{xms => JSON::false},
$user->{data}{id},
);
if ($n_response->{code} != 201) {
return unless &{$err_code}(
'Creating rtc network failed. Error code: ' . $n_response->{code}, $n_response->{debug});
}
}
return;
}
sub _delete_rtc_user {
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}.'://'.
$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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
}
my $rtc_user = $reseller_item->rtc_user;
if (!defined $rtc_user) {
return unless &{$err_code}(
'No rtc user found in db for this reseller.');
}
# app and networks are deleted automatically
my $delete_resp = $comx->delete_user(
$rtc_user->rtc_user_id,
);
if ($delete_resp->{code} == 200) {
$rtc_user->delete;
} else {
return unless &{$err_code}(
'Deleting rtc user failed. Error code: ' . $delete_resp->{code}, $delete_resp->{debug});
}
return;
}
sub get_rtc_apps {
my %params = @_;
my ($rtc_user_id, $config, $include_id, $err_code) =
@params{qw/rtc_user_id config 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 $apps_resp = $comx->get_apps_by_user_id($rtc_user_id);
my $apps = $apps_resp->{data};
unless (defined $apps && 'ARRAY' eq ref $apps && @{ $apps }) {
return unless &{$err_code}(
'Fetching apps failed. Code: ' . $apps_resp->{code});
}
my $res = [map {{
domain =>$_->{domain},
name => $_->{name},
secret => $_->{secret},
api_key => $_->{apiKey}, # todo: which spelling do we use?
$include_id ? (id => $_->{id}) : (),
}} @{ $apps }];
return $res;
}
sub modify_rtc_apps {
my %params = @_;
my ($old_resource, $resource, $config, $reseller_item, $err_code) =
@params{qw/old_resource resource config reseller_item err_code/};
#TODO: stub, to be done
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 app. 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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
}
my (@deleted, @new);
for my $a (@{ $resource->{apps} }) {
my $app_name = $a->{name};
my ($old_app) = grep {$app_name eq $_->{name}} @{ $old_resource->{apps} };
if (!defined $old_app) {
push @new, $a;
} else {
if ($a->{domain} ne $old_app->{domain}) {
push @deleted, $old_app;
push @new, $a;
}
}
}
for my $a (@{ $old_resource->{apps} }) {
my $app_name = $a->{name};
my ($new_app) = grep {$app_name eq $_->{name}} @{ $resource->{apps} };
if (!defined $new_app) {
push @deleted, $a;
}
}
for my $app (@deleted) {
my $a_response = $comx->delete_app($app->{id});
if ($a_response->{code} != 200) {
return unless &{$err_code}(
'Deleting rtc app failed. Error code: ' . $a_response->{code});
}
}
for my $app (@new) {
my $a_response = $comx->create_app(
$app->{name},
$app->{domain},
$old_resource->{rtc_user_id},
);
if ($a_response->{code} != 201) {
return unless &{$err_code}(
'Creating rtc app failed. Error code: ' . $a_response->{code});
}
}
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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
}
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}, $networks_resp->{debug});
}
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}, $n_response->{debug});
}
}
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}, $n_response->{debug});
}
}
return;
}
# returns enable_rtc (true|false) and rtc_browser_token (string)
sub get_rtc_subscriber_data {
my %params = @_;
my ($prov_subs, $config, $err_code) =
@params{qw/prov_subs config err_code/};
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
}
unless ($prov_subs) {
return unless &{$err_code}(
"Couldn't get rtc_subscriber_data. No provisioning subscriber.");
}
my $rtc_session = $prov_subs->rtc_session;
unless ($rtc_session) {
return {enable_rtc => 0}; # JSON::false ?
}
# TODO: huh? is this the right browser token?
return {enable_rtc => 1, rtc_browser_token => 'abcde TODO'};
}
sub modify_subscriber_rtc {
my %params = @_;
my ($old_resource, $resource, $config, $prov_subs, $err_code) =
@params{qw/old_resource resource config prov_subs err_code/};
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
}
if ((!defined $old_resource) && (defined $resource)) { # newly created reseller
# 1. enable_rtc is off -> do nothing
if (!$resource->{enable_rtc}) {
return;
}
_create_subscriber_rtc(
resource => $resource,
config => $config,
prov_subs => $prov_subs,
err_code => $err_code);
} elsif ((defined $old_resource) && (defined $resource)) {
if($old_resource->{status} ne 'terminated' &&
$resource->{status} eq 'terminated' &&
$old_resource->{enable_rtc}) { # just terminated
$resource->{enable_rtc} = JSON::false;
_delete_subscriber_rtc(
config => $config,
prov_subs => $prov_subs,
err_code => $err_code);
} elsif ($old_resource->{enable_rtc} &&
!$resource->{enable_rtc}) { # disable rtc
_delete_subscriber_rtc(
config => $config,
prov_subs => $prov_subs,
err_code => $err_code);
} elsif (!$old_resource->{enable_rtc} &&
$resource->{enable_rtc} &&
$resource->{status} ne 'terminated') { # enable rtc
_create_rtc_user(
resource => $resource,
config => $config,
prov_subs => $prov_subs,
err_code => $err_code);
}
}
return;
}
sub _create_subscriber_rtc {
my %params = @_;
my ($resource, $config, $prov_subs, $err_code) =
@params{qw/resource config prov_subs err_code/};
my $reseller = $prov_subs->voip_subscriber->contract->contact->reseller;
unless ($reseller) {
return unless &{err_code}(
'Creating subscriber rtc data failed. Reseller not found.');
}
my $rtc_user = $reseller->rtc_user;
unless ($rtc_user) {
return unless &{err_code}(
'Creating subscriber rtc data failed. Reseller has not enabled rtc.');
}
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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
}
my $comx_apps = $comx->get_apps_by_user_id($rtc_user->rtc_user_id);
my $comx_app;
if ($comx_apps->{data} && @{ $comx_apps->{data} }){
$comx_app = $comx_apps->{data}[0];
} else {
return unless &{$err_code}(
'_create_subscriber_rtc: Could not find app.');
}
my $session = $comx->create_session(
$comx_app->{id},
$rtc_user->rtc_user_id,
);
if ($session->{code} != 201) {
return unless &{$err_code}(
'Creating rtc session failed. Error code: ' . $session->{code}, $session->{debug});
}
# # 3. create relation in our db
# $prov_subs->create_related('rtc_session', {
# rtc_session_id => $session->{data}{id},
# });
# # 4. create related app
# my $app = $comx->create_app(
# $reseller_name . '_app',
# $reseller_name . 'www.sipwise.com',
# $user->{data}{id},
# );
# if ($app->{code} != 201) {
# return unless &{$err_code}(
# 'Creating rtc app failed. Error code: ' . $app->{code});
# }
# # 5. create related networks
# for my $n (@{ $rtc_networks }) {
# my $n_response = $comx->create_network(
# $reseller_name . "_$n",
# $n . '-connector',
# {xms => JSON::false},
# $user->{data}{id},
# );
# if ($n_response->{code} != 201) {
# return unless &{$err_code}(
# 'Creating rtc network failed. Error code: ' . $n_response->{code});
# }
# }
# return;
}
sub _delete_subscriber_rtc {
# my %params = @_;
# my ($config, $prov_subs, $err_code) =
# @params{qw/config prov_subs err_code/};
# 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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
# }
# my $rtc_user = $reseller_item->rtc_user;
# if (!defined $rtc_user) {
# return unless &{$err_code}(
# 'No rtc user found in db for this reseller.');
# }
# # app and networks are deleted automatically
# my $delete_resp = $comx->delete_user(
# $rtc_user->rtc_user_id,
# );
# if ($delete_resp->{code} == 200) {
# $rtc_user->delete;
# } else {
# return unless &{$err_code}(
# 'Deleting rtc user failed. Error code: ' . $delete_resp->{code});
# }
# return;
}
sub create_rtc_session {
my %params = @_;
my ($config, $err_code, $resource, $subscriber_item) =
@params{qw/config err_code resource subscriber_item/};
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
}
my $reseller = $subscriber_item->contract->contact->reseller;
unless ($reseller) {
return unless &{$err_code}(
'Creating subscriber rtc data failed. Reseller not found.');
}
my $rtc_user = $reseller->rtc_user;
unless ($rtc_user) {
return unless &{$err_code}(
'Creating subscriber rtc data failed. Reseller has not enabled rtc.');
}
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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
}
my $comx_apps = $comx->get_apps_by_user_id($rtc_user->rtc_user_id);
my $comx_app;
if ($comx_apps->{data} && @{ $comx_apps->{data} }){
if ($resource->{rtc_app_name}) {
($comx_app) = grep {$_->{name} eq $resource->{rtc_app_name}} @{ $comx_apps->{data} };
} else { # default app
($comx_app) = grep {$_->{name} =~ m/_default_app$/;} @{ $comx_apps->{data} };
}
}
unless ($comx_app) {
return unless &{$err_code}(
'create_rtc_session: Could not find app.');
}
my $comx_networks = $comx->get_networks_by_user_id($rtc_user->rtc_user_id);
my $comx_network_tags = [];
if ($comx_networks->{data} && 'ARRAY' eq ref $comx_networks->{data}) {
$comx_network_tags = [ map { $_->{tag} } @{ $comx_networks->{data} } ];
} else {
return unless &{$err_code}(
'create_rtc_session: Could not fetch networks for given rtc user.');
}
my $session = $comx->create_session(
$comx_app->{id},
$rtc_user->rtc_user_id,
);
if ($session->{code} != 201) {
return unless &{$err_code}(
'Creating rtc session failed. Error code: ' . $session->{code}, $session->{debug});
}
for my $n (@{ $comx_networks->{data} }) {
my $identifier;
if ($n->{connector} eq "sip-connector") {
$identifier = 'sip:' . $subscriber_item->username . '@' . $subscriber_item->domain->domain;
} elsif ($n->{connector} eq "xmpp-connector") {
$identifier = 'xmpp:' . $subscriber_item->username . '@' . $subscriber_item->domain->domain;
} else { # webrtc, ...
$identifier = $subscriber_item->username;
}
my $account = $comx->create_account(
$session->{data}{id},
$rtc_user->rtc_user_id,
$identifier,
$n->{tag},
$subscriber_item->provisioning_voip_subscriber->password,
{xms => JSON::false},
);
if ($account->{code} != 201) {
return unless &{$err_code}(
"Creating rtc account ($n->{tag}) failed. Error code: " . $account->{code}, $account->{debug});
}
}
my $rtc_session_item = $subscriber_item->provisioning_voip_subscriber->create_related('rtc_session', {
rtc_session_id => $session->{data}{id},
});
return $rtc_session_item;
}
sub get_rtc_session {
my %params = @_;
my ($config, $item, $err_code) =
@params{qw/config item 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. Status code: ' . $comx->login_status->{code}, $comx->login_status->{debug});
}
my $session = $comx->get_session($item->rtc_session_id);
if ($session->{code} != 200) {
return unless &{$err_code}(
"Couldn't find session. Error code: " . $session->{code}, $session->{debug});
}
return $session;
}
sub get_random {
my ($num, $err_code) = @_;
my ($fd, $buf);
unless(open($fd, '<', '/dev/urandom')) {
return unless &{$err_code}("Failed to open /dev/urandom: $!");
}
unless(read($fd, $buf, $num) == $num) {
return unless &{$err_code}("Failed to read $num bytes from /dev/urandom: $!");
}
close($fd);
return $buf;
}
1;
# vim: set tabstop=4 expandtab:

@ -1,630 +0,0 @@
[% site_config.title = c.loc('Web Phone for ') _ subscriber.username _ '@' _ subscriber.domain.domain -%]
<script type="text/javascript" src="/js/libs/jssip-0.3.0.min.js"></script>
<!--<script type="text/javascript" src="/js/libs/stanzaio.bundle.min.js"></script>-->
<script type="text/javascript" src="/js/libs/stanzaio.bundle.js"></script>
<script type="text/javascript" src="/js/libs/bootstrap-select.min.js"></script>
<script type="text/javascript" src="/js/libs/bootstrap-switch.js"></script>
<script type="text/javascript" src="/js/libs/jquery.slimscroll.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/bootstrap-select/bootstrap-select.min.css"/>
<link rel="stylesheet" type="text/css" href="/css/bootstrap-switch/bootstrap-switch.css"/>
<link rel="stylesheet" type="text/css" href="/font/font-awesome/css/font-awesome.min.css"/>
<style>
#xmpp-roster {
list-style-type: none;
}
#xmpp-roster .popover {
max-width: 100%;
min-height: 150px;
}
.xmpp-roster-entry-col1.available {
background: #080;
}
.xmpp-roster-entry-col1.unavailable {
background: #ddd;
}
.xmpp-roster-entry-col1.chat {
background: #0f0;
}
.xmpp-roster-entry-col1.away,
.xmpp-roster-entry-col1.xa {
background: #fa0;
}
.xmpp-roster-entry-col1.dnd {
background: #f00;
}
.xmpp-roster-entry-ctrl {
margin-right:10px;
cursor: pointer;
}
.switch-left.switch-mini, .switch-right.switch-mini {
/* make more space on left/right */
padding-left: 5px;
padding-right: 5px;
}
.has-switch {
margin-right: 10px;
}
.xmpp-chat-time {
font-style:italic;
color: #999;
}
.xmpp-chat-sent {
float: right;
clear: both;
margin-right: 15px;
margin-left: 30px;
text-align: right;
color: #333;
}
.xmpp-chat-recv {
float: left;
clear: both;
margin-left: 0px;
margin-right: 30px;
text-align: left;
color: #666;
}
.has-switch {
min-width: 200px;
}
</style>
<div class="row">
<span class="pull-left" style="margin:0 5px 0 5px;">
<a class="btn btn-primary btn-large" href="[% c.uri_for('/back') %]"><i class="icon-arrow-left"></i> [% c.loc('Back') %]</a>
</span>
</div>
[% back_created = 1 -%]
<div class="ngcp-separator"></div>
<script type="text/javascript">
var phone = null;
var chat = null;
var sip_configuration = null;
var xmpp_configuration = null;
var xmpp_last_state = 'available';
var orig_page_title = document.title;
var window_focus = true;
var window_timeout;
var xmpp_show_offline = false;
var xmpp_last_time = "";
$.ajax({
url: "[% c.uri_for_action('/subscriber/webphone_ajax', c.req.captures) %]"
}).done(function(data) {
sip_configuration = data.aaData.sip;
sip_configuration.register = true;
sip_configuration.trace_sip = true;
phone = new JsSIP.UA(sip_configuration);
// ws connection events
phone.on('connected', function(e){
console.log("connected");
$("#sip-status").html("[% c.loc('connected - registering...') %]");
});
phone.on('disconnected', function(e){
console.log("disconnected");
$("#sip-status").html("[% c.loc('disconnected.') %]");
});
// in/out call event
phone.on('newRTCSession', function(e){
//console.log("newRTCSession", e.originator, e.session);
console.log("newRTCSession", e);
var session = e.data.session;
if(session.direction == 'incoming') {
session.answer({
mediaConstraints: { audio: true, video: $('#sip_toggle_video').is(':checked') }
});
// TODO: create_incall_window($(obj.target).parents("li.xmpp-roster-entry"), jidid, item.jid.bare);
session.on('started', function(e) {
console.log("RTCSession session started");
var rtcSession = e.sender;
if(rtcSession.getLocalStreams().length > 0) {
selfView.src = window.URL.createObjectURL(rtcSession.getLocalStreams()[0]);
selfView.volume = 0;
}
if(rtcSession.getRemoteStreams().length > 0) {
remoteView.src = window.URL.createObjectURL(rtcSession.getRemoteStreams()[0]);
}
});
}
});
// in/out im event
phone.on('newMessage', function(e){
console.log("newMessage");
});
// registration events
phone.on('registered', function(e){
console.log("registered");
$("#sip-status").html("[% c.loc('registered.') %]");
});
phone.on('unregistered', function(e){
console.log("unregistered");
$("#sip-status").html("[% c.loc('unregistered.') %]");
});
phone.on('registrationFailed', function(e){
console.log("registrationFailed", e.data.response);
$("#sip-status").html("[% c.loc('registration failed:') %] " + e.data.response.status_code + " - " + e.data.response.reason_phrase);
});
phone.start();
xmpp_configuration = data.aaData.xmpp;
// chat client modifies it, so make a copy to have the original
// one later on re-connects
var tmp_xmpp_configuration = jQuery.extend(true, {}, xmpp_configuration);
chat = XMPP.createClient(tmp_xmpp_configuration);
register_chat_callbacks();
chat.connect();
});
function register_chat_callbacks() {
var timer = null;
chat.on('disconnected', function() {
console.log("xmpp disconnection");
$("#xmpp-status").html("[% c.loc('disconnected.') %]");
if(timer)
return 1;
console.log("prepare re-connect timer");
$('#xmpp-roster').empty();
var xmpp_last_state_tmp = xmpp_last_state;
$('#xmpp-pres').val('unavailable');
$('#xmpp-pres').change();
xmpp_last_state = xmpp_last_state_tmp;
timer = window.setInterval(function(){
console.log("perform re-connect");
window.clearInterval(timer);
timer = null;
chat.disconnect();
console.log("create new client", xmpp_configuration);
var tmp_xmpp_configuration = jQuery.extend(true, {}, xmpp_configuration);
chat = XMPP.createClient(tmp_xmpp_configuration);
register_chat_callbacks();
chat.connect();
}, 3000);
});
chat.on('chatState', function(obj) {
console.log("got chat state", obj);
var jid = obj.from.bare;
var msg;
if(obj.chatState == "composing") {
msg = "[% c.loc('is typing...') %]";
} else if(obj.chatState == "paused") {
msg = "[% c.loc('has stopped typing.') %]";
} else if(obj.chatState == "active") {
// chat session started/closed
return;
}
var jidid = jid.replace(/[^a-zA-Z0-9_]/g, '-');
var chat_win = $('#xmpp-roster #' + jidid).find('.popover');
if(!chat_win.length) {
chat_win = create_chat_window($('#xmpp-roster #' + jidid), jidid, jid);
}
$(chat_win).find('.xmpp-chat-history').append('<li><em>' + msg + '</em></li>')
.slimScroll({ scrollBy: '50px' });
});
chat.on('chat', function(obj) {
console.log("got message ", obj);
var jid = obj.from.bare;
var jidid = jid.replace(/[^a-zA-Z0-9_]/g, '-');
var chat_win = $('#xmpp-roster #' + jidid).find('.popover');
if(!chat_win.length) {
chat_win = create_chat_window($('#xmpp-roster #' + jidid), jidid, jid);
}
var now = get_time_string();
if(now != xmpp_last_time) {
xmpp_last_time = now;
$(chat_win).find('.xmpp-chat-history').append('<li><span class="xmpp-chat-recv"><span class="xmpp-chat-time">' + now + '</span></span></li>')
}
$(chat_win).find('.xmpp-chat-history').append('<li><span class="xmpp-chat-recv">' + obj.body + '</span></li>')
.slimScroll({ scrollBy: '50px' });
raise_attention('chat');
});
chat.on('groupchat', function(message) {
console.log("got group message ", message);
});
chat.on('session:started', function() {
$("#xmpp-status").html("[% c.loc('online.') %]");
console.log("XMPP session started");
chat.enableCarbons();
chat.getRoster(function(err, resp) {
console.log(err, resp);
chat.sendPresence();
if(err == null) {
$.each(resp.roster.items, function(index, item) {
console.log(item.name);
var jidid = item.jid.bare.replace(/[^a-zA-Z0-9_]/g, '-');
var entry = create_xmpp_entry_dom(jidid, item.jid.bare, item.name || item.jid.bare);
$('#xmpp-roster').append(entry);
$('#xmpp-roster li').sort(roster_asc_sort).appendTo('#xmpp-roster');
equalHeights($('#' + jidid).find('.xmpp-roster-entry-col1'), $('#' + jidid).find('.xmpp-roster-entry-col2'));
$('#' + jidid)
.mouseenter(function(obj) {
$(obj.currentTarget).find('.xmpp-roster-entry-col3').show();
})
.mouseleave(function(obj) {
$(obj.currentTarget).find('.xmpp-roster-entry-col3').hide();
})
.find('[rel="tooltip"]').tooltip({'html': false});
$('#' + jidid).find('.xmpp-roster-entry-ctrl-chat').click(function(obj) {
console.log("start chat");
create_chat_window($(obj.target).parents("li.xmpp-roster-entry"), jidid, item.jid.bare);
});
$('#' + jidid).find('.xmpp-roster-entry-ctrl-phone').click(function(obj) {
console.log("start chat");
create_outcall_window($(obj.target).parents("li.xmpp-roster-entry"), jidid, item.jid.bare);
var session = call(item.jid.bare);
});
// we get a presence callback where we'll show it
$(entry).hide();
});
}
$('#xmpp-pres').val(xmpp_last_state);
$('#xmpp-pres').change();
});
});
chat.on('presence', function(pres) {
if(pres.from.bare == xmpp_configuration.jid) {
console.log("skip own presence info");
return 1;
}
var type = pres.type || 'available';
var show;
if(type == 'available') {
show = pres.show || 'available';
} else {
show = pres.show || 'unavailable';
}
var jidid = pres.from.bare.replace(/[^a-zA-Z0-9_]/g, '-');
$("#xmpp-roster #" + jidid + " .xmpp-roster-entry-col1").removeClass().addClass("xmpp-roster-entry-col1 " + show);
console.log("xmpp-roster type=" + pres.type + ", show=" + pres.show);
if(show != 'unavailable' || xmpp_show_offline) {
$("#xmpp-roster #" + jidid).show();
} else {
$("#xmpp-roster #" + jidid).hide();
}
});
}
function call(dest_uri) {
var eventHandlers = {
'progress': function(e) {
console.log("call in progress", e);
$("#sip-status").html("[% c.loc('in progress...') %]");
},
'failed': function(e) {
console.log("call failed");
$("#sip-status").html("[% c.loc('call failed:') %] ", e);
},
'started': function(e) {
console.log("call started");
$("#sip-status").html("[% c.loc('call started.') %]");
var rtcSession = e.sender;
if (rtcSession.getLocalStreams().length > 0) {
selfView.src = window.URL.createObjectURL(rtcSession.getLocalStreams()[0]);
}
if (rtcSession.getRemoteStreams().length > 0) {
remoteView.src = window.URL.createObjectURL(rtcSession.getRemoteStreams()[0]);
}
},
'ended': function(e){
console.log("call ended");
$("#sip-status").html("[% c.loc('call ended.') %]");
}
};
var options = {
'eventHandlers': eventHandlers,
'extraHeaders': [ 'X-Foo: foo', 'X-Bar: bar' ],
'mediaConstraints': { 'audio': true, 'video': $('#sip_toggle_video').is(':checked') }
};
phone.call('sip:' + dest_uri, options);
}
function roster_asc_sort(a, b) {
return ($(b).find('.xmpp-roster-entry-name').text().toLowerCase()) < ($(a).find('.xmpp-roster-entry-name').text().toLowerCase()) ? 1 : -1;
}
function get_time_string() {
var now = new Date();
//return ((now.getHours() < 10)?"0":"") + now.getHours() +":"+ ((now.getMinutes() < 10)?"0":"") + now.getMinutes() +":"+ ((now.getSeconds() < 10)?"0":"") + now.getSeconds();
return ((now.getHours() < 10)?"0":"") + now.getHours() +":"+ ((now.getMinutes() < 10)?"0":"") + now.getMinutes();
}
function create_xmpp_entry_dom(jidid, jid, name) {
var entry =
'<li id="' + jidid + '" class="xmpp-roster-entry row span6" style="clear:both; float:left; padding:0; margin:1px; background:#f0f0f0;">' +
' <div class="xmpp-roster-entry-col1 unavailable" style="float:left; width:10px; padding:0; margin:0; ">&nbsp;</div>' +
' <div class="xmpp-roster-entry-col2 span3" style="float:left; padding:20px; margin:0;">' +
' <div>' +
' <span class="xmpp-roster-entry-name" style="font-size:1.3em; font-weight:bold;">' + name + '</span>' +
' </div>' +
' <div>' +
' <span class="xmpp-roster-entry-details" style="font-size:1em; font-weight:normal;">' + jid + '</span>' +
' </div>' +
' </div>' +
' <div class="xmpp-roster-entry-col3" style="float:right; padding:20px 0 20px 0; margin:0; display:none;">' +
' <div style="float:right; font-size:1.5em">' +
' <span class="fa fa-comment xmpp-roster-entry-ctrl xmpp-roster-entry-ctrl-chat" rel="tooltip" title="Start Chat"></span>' +
' <span class="fa fa-phone xmpp-roster-entry-ctrl xmpp-roster-entry-ctrl-phone" rel="tooltip" title="Make Call"></span>' +
' <span class="fa fa-file-text xmpp-roster-entry-ctrl xmpp-roster-entry-ctrl-fax" rel="tooltip" title="Send Fax"></span>' +
' </div>' +
' </div>' +
'</li>';
return entry;
}
function create_chat_window(parent, jidid, jid) {
$(parent).popover("destroy");
$(parent).popover({
placement: 'right',
html: true,
container: '#' + jidid,
trigger: 'manual',
title: '<div>&nbsp;<span class="pull-left">' + jid + '</span><span class="xmpp-chat-close pull-right fa fa-times"></span></div>',
content: '<div class="span4" style="margin:10px;"><ul class="xmpp-chat-history" style="list-style-type:none; margin:0; min-height:100px;"></ul><input data-jid="'+ jid + '" type="text" class="xmpp-chat-input" style="width:100%; margin:10px 0 0 0;"/></div>'
});
$(parent).popover("show");
$(parent).find(".xmpp-chat-input").focus();
$(parent).find(".xmpp-chat-history").slimScroll({
height: '100px',
railVisible: true,
alwaysVisible: true,
start: 'bottom'
});
return $(parent).find(".popover");
}
function create_incall_window(parent, jidid, jid) {
$(parent).popover("destroy");
$(parent).popover({
placement: 'right',
html: true,
container: '#' + jidid,
trigger: 'manual',
title: '<div>&nbsp;<span class="pull-left">[% c.loc('Call from') %] ' + jid + '</span><span class="xmpp-chat-close pull-right fa fa-times"></span></div>',
content: '<div class="span4" style="margin:10px;"><button class="sip-accept-call" style="margin-right:20px;">[% c.loc('Accept') %]</button><button class="sip-reject-call">[% c.loc('Reject') %]</button></div>'
});
$(parent).popover("show");
$(parent).find(".sip-accept-call").click(function(){
console.log("answering call");
/*
var session = ???;
session.answer({
mediaConstraints: { audio: true, video: $('#sip_toggle_video').is(':checked') }
});
*/
});
$(parent).find(".sip-reject-call").click(function(){
console.log("rejecting call");
// var session = ???; session.terminate({ status_code: 486 });
});
return $(parent).find(".popover");
}
function create_outcall_window(parent, jidid, jid) {
$(parent).popover("destroy");
$(parent).popover({
placement: 'right',
html: true,
container: '#' + jidid,
trigger: 'manual',
title: '<div>&nbsp;<span class="pull-left">[% c.loc('Calling') %] ' + jid + '</span><span class="xmpp-chat-close pull-right fa fa-times"></span></div>',
content: '<div class="span4" style="margin:10px;"><button class="sip-stop-call" style="margin-right:20px;">[% c.loc('Terminate Call') %]</button></div>'
});
$(parent).popover("show");
$(parent).find(".sip-stop-call").click(function(){
console.log("stopping call");
/*
var session = ???;
session.terminate();
*/
});
return $(parent).find(".popover");
}
function equalHeights (element1, element2) {
var height;
if (element1.outerHeight() > element2.outerHeight())
{
height = element1.outerHeight();
element2.css('height', height);
}
else {
height = element2.outerHeight();
element1.css('height', height);
}
}
function raise_attention(type) {
if(!window_focus) {
if(type == "chat") {
flash_title('[% c.loc('NEW MESSAGE') %]');
} else {
flash_title(type + '! ' + orig_page_title);
}
}
}
function flash_title(title) {
function step() {
document.title = (document.title == orig_page_title) ? title : orig_page_title;
window_timeout = setTimeout(step, 800);
};
cancel_flash_title(window_timeout);
step();
};
function cancel_flash_title() {
clearTimeout(window_timeout);
document.title = orig_page_title;
};
$(function() {
$('.selectpicker').selectpicker();
$('#xmpp-toggle-offline').bootstrapSwitch();
$('#xmpp-toggle-offline').bootstrapSwitch('setSizeClass', 'switch-mini');
$('#xmpp-toggle-offline').on('switch-change', function(obj) {
if(obj.currentTarget.checked) {
console.log("show offline entries");
xmpp_show_offline = true;
$('.xmpp-roster-entry').show();
} else {
console.log("hide offline entries");
xmpp_show_offline = false;
$('.xmpp-roster-entry-col1.unavailable').parent().hide();
}
});
$('#sip_toggle_video').bootstrapSwitch();
$('#sip_toggle_video').bootstrapSwitch('setSizeClass', 'switch-mini');
$('#xmpp-pres').change(function(obj) {
var show = obj.currentTarget[obj.currentTarget.selectedIndex].value;
xmpp_last_state = show;
var type = (show == "unavailable" ? "unavailable" : "available");
console.log("changing xmpp presence status, show=" + show + ", type=" + type);
if(show == "available") {
chat.sendPresence();
} else {
chat.sendPresence({ type: type, show: show });
}
});
$(document).keypress(function(obj) {
if($(obj.target).hasClass("xmpp-chat-input")) {
// return esc and tab
if (obj.which == 9 || obj.which == 27) {
return false;
}
if(obj.which == 13 && $(obj.target).val().length) {
obj.preventDefault();
chat.sendMessage({ to: $(obj.target).data("jid"), body: $(obj.target).val() });
var chat_win = $(obj.target).parent().find(".xmpp-chat-history");
var now = get_time_string();
if(now != xmpp_last_time) {
xmpp_last_time = now;
$(chat_win).append('<li><span class="xmpp-chat-sent"><span class="xmpp-chat-time">' + now + '</span></span></li>')
}
$(chat_win).append('<li><span class="xmpp-chat-sent">' + $(obj.target).val() + '</span></li>')
.slimScroll({ scrollBy: '50px' });
$(obj.target).val("");
} else {
// TODO: send chatState message somehow
}
}
});
$(document).click(function(obj) {
if($(obj.target).hasClass("xmpp-chat-close")) {
console.log("hiding chat window");
$(obj.target).parents("li.xmpp-roster-entry").popover("destroy");
$(obj.target).parents(".popover").remove();
}
});
$(window).focus(function() {
window_focus = true;
document.title = orig_page_title;
clearTimeout(window_timeout);
}).blur(function() {
window_focus = false;
});
window.onbeforeunload = function(obj) {
if(phone.isRegistered()) {
console.log("unregistering phone before leaving");
phone.unregister({'all': true});
}
};
$('#xmpp-buddy-add').click(function(obj) {
var jid = $('#xmpp-buddy-add-jid').val();
console.log(">>>>>>>>> adding jid " + jid);
chat.subscribe(jid);
});
});
</script>
<div class="row">
<div class="span6">[% c.loc('Phone Status:') %] <span id="sip-status">[% c.loc('connecting...') %]</span></div>
</div>
<div class="row">
<div class="span6">[% c.loc('Chat Status:') %] <span id="xmpp-status">[% c.loc('connecting...') %]</span></div>
</div>
<div>
<h3>[% c.loc('Buddy List') %]</h3>
<div class="row span6" style="margin:0; clear:both; padding:10px;">
<input id="xmpp-toggle-offline" style="float:left;" type="checkbox" data-on="success" data-off="default" data-on-label="[% c.loc('Show Offline') %]" data-off-label="[% c.loc('Hide Offline') %]">
<input id="sip_toggle_video" checked style="float:left" type="checkbox" data-on="success" data-off="default" data-on-label="[% c.loc('Audio&amp;Video') %]" data-off-label="[% c.loc('Audio Only') %]">
</div>
<div class="span6" style="margin:0; clear:both; padding:0;">
<div class="span4" style="margin:0;">
<input type="text" id="xmpp-buddy-add-jid" class="span4"/>
</div>
<div class="span2" style="margin:0; float:right;">
<button class="btn btn-primary btn-medium" id="xmpp-buddy-add"><i class="icon-plus"></i> Add Buddy</button>
</div>
</div>
<div class="row span6" style="margin:0; clear:both;">
<select id="xmpp-pres" class="selectpicker span6">
[% FOR opt IN
[
{ n = "unavailable", d = c.loc('Offline') },
{ n = "available", d = c.loc('Available') },
{ n = "away", d = c.loc('Away') },
{ n = "xa", d = c.loc('Extended Away') },
{ n = "dnd", d = c.loc('Do Not Disturb') },
]
-%]
<option value="[% opt.n %]" data-content="<span class='xmpp-roster-entry-col1 [% opt.n %]'>&nbsp;</span><span> [% opt.d %]</span>">[% opt.d %]</option>
[% END -%]
</select>
</div>
<ul id="xmpp-roster" class="span8" style="list-style-type:none; padding:0; margin:0;">
</ul>
</div>
<video id="selfView" autoplay hidden=true></video>
<video id="remoteView" autoplay hidden=true></video>
[% # vim: set tabstop=4 syntax=html expandtab: -%]

@ -213,10 +213,6 @@ sub init_config{#init config
#'customerzonecosts' => 1,
#fails with: Unsupported media type 'application/json', accepting text/plain or text/xml only.)
#'pbxdeviceconfigs' => 1,
#fails with: not ok 1131 - rtcapps: check_get2put: check put successful (Unprocessable Entity: Invalid field 'apps'. Must be an array.)
#'rtcapps' => 1,
#fails with: not ok 1176 - rtcnetworks: check_get2put: check put successful (Unprocessable Entity: Invalid field 'networks'. Must be an array.)
#'rtcnetworks' => 1,
#--------- interceptions:
# No intercept agents configured in ngcp_panel.conf, rejecting request
@ -321,4 +317,4 @@ sub init_report{
#$test_machine->form_data_item();
##$test_machine->check_create_correct( 1, undef, );
# vim: set tabstop=4 expandtab:
# vim: set tabstop=4 expandtab:

@ -22,11 +22,6 @@ my $ref = $test->reference_data(
hints => [{ name => 'reseller admin' }],
name => 'my_reseller_admin'
},
{
resource => 'admins',
hints => [{ name => 'rtc reseller admin' }],
name => 'my_rtcreseller_admin'
},
{
resource => 'subscriberprofiles',
hints => [{ name => 'subscriber profile' }],
@ -49,7 +44,6 @@ diag("test capabilities as admin");
cloudpbx => $ENV{HAS_CLOUDPBX} // 0,
sms => $ENV{HAS_SMS} // 0,
faxserver => $ENV{HAS_FAXSERVER} // 1,
rtcengine => $ENV{HAS_RTCENGINE} // 0,
fileshare => $ENV{HAS_FILESHARE} // 0,
mobilepush => $ENV{HAS_MOBILEPUSH} // 0,
};
@ -97,8 +91,8 @@ diag("test capabilities as admin");
}
}
# test capabilities as reseller without rtcengine enabled
diag("test capabilities as reseller without rtcengine");
# test capabilities as reseller
diag("test capabilities as reseller");
{
my $c_reseller = $test->client(
role => 'reseller',
@ -110,7 +104,6 @@ diag("test capabilities as reseller without rtcengine");
cloudpbx => $ENV{HAS_CLOUDPBX} // 0,
sms => $ENV{HAS_SMS} // 0,
faxserver => $ENV{HAS_FAXSERVER} // 1,
rtcengine => 0,
fileshare => $ENV{HAS_FILESHARE} // 0,
mobilepush => $ENV{HAS_MOBILEPUSH} // 0,
};
@ -158,67 +151,6 @@ diag("test capabilities as reseller without rtcengine");
}
}
# test capabilities as reseller with rtcengine enabled
diag("test capabilities as reseller with rtcengine");
{
my $c_rtcreseller = $test->client(
role => 'reseller',
username => $ref->data('my_rtcreseller_admin')->{login},
password => "rtcreseller_$sid",
);
my $expected = {
cloudpbx => $ENV{HAS_CLOUDPBX} // 0,
sms => $ENV{HAS_SMS} // 0,
faxserver => $ENV{HAS_FAXSERVER} // 1,
rtcengine => $ENV{HAS_RTCENGINE} // 1,
fileshare => $ENV{HAS_FILESHARE} // 0,
mobilepush => $ENV{HAS_MOBILEPUSH} // 0,
};
my $cap_res = $test->resource(
client => $c_rtcreseller,
resource => 'capabilities',
);
my $caps = $cap_res->test_get(
name => 'fetch capabilities as rtc reseller',
expected_links => [],
expected_fields => [qw/
id name enabled
/],
expected_result => { code => 200 }
);
my $tmpexpected = clone($expected);
my @caps = ();
foreach my $cap (@{ $caps->[0]->{_embedded}->{'ngcp:capabilities'} }) {
my $name = $cap->{name};
my $val = $cap->{enabled} ? 1 : 0;
is(exists $expected->{$name}, 1, "returned rtc reseller capability $name is expected");
$test->inc_test_count();
is($val, $expected->{$name}, "returned rtc reseller capability $name has value $$expected{$name}");
$test->inc_test_count();
delete $tmpexpected->{$cap->{name}};
push @caps, $cap;
}
is(keys %{ $tmpexpected }, 0, "all expected rtc reseller capabilities seen");
$test->inc_test_count();
foreach my $cap(@caps) {
diag("test individual capability $cap->{name} with id $cap->{id} as rtc reseller");
my $items = $cap_res->test_get(
name => "fetch individual capability $cap->{name} as rtc reseller",
item => $cap,
expected_links => [],
expected_fields => [qw/
id name enabled
/],
expected_result => { code => 200 }
);
}
}
diag("test capabilities as pbx subscriber");
{
my $c_subscriber = $test->client(
@ -232,7 +164,6 @@ diag("test capabilities as pbx subscriber");
cloudpbx => 1,
sms => $ENV{HAS_SMS} // 0,
faxserver => $ENV{HAS_FAXSERVER} // 1,
rtcengine => 0,
fileshare => $ENV{HAS_FILESHARE} // 0,
mobilepush => $ENV{HAS_MOBILEPUSH} // 0,
};

@ -146,9 +146,6 @@ $ua = Test::Collection->new()->ua();
resellerbrandinglogos => 1,
rewriterules => 1,
rewriterulesets => 1,
rtcapps => 1,
rtcnetworks => 1,
rtcsessions => 1,
sipcaptures => 1,
sms => 1,
soundfilerecordings => 1,

@ -1,201 +0,0 @@
use warnings;
use strict;
use Net::Domain qw(hostfqdn);
use JSON qw();
use Test::More;
use URI::Escape qw();
#use LWP::Debug;
my $is_local_env = 0;
unless ($ENV{TEST_RTC}) {
plan skip_all => "not testing rtc, enable TEST_RTC=yes to run tests";
exit 0;
}
my $uri = $ENV{CATALYST_SERVER} || ('https://'.hostfqdn.':4443');
my $domain_name = $ENV{TEST_RTC_DOMAIN};
unless ($domain_name) {
($domain_name) = ($uri =~ m!^https?://([^/:]*)(:[0-9]+)?/?.*$!);
}
my ($ua, $req, $res, $data);
use Test::Collection;
$ua = Test::Collection->new()->ua();
my ($domain_id);
{
$req = HTTP::Request->new('GET', "$uri/api/domains/?domain=$domain_name");
$res = $ua->request($req);
is($res->code, 200, "GET search domain");
$data = JSON::from_json($res->decoded_content);
ok($data->{total_count}, "got at least one domain") || die "we can't continue without domain";
my $selected_domain = ( 'ARRAY' eq ref $data->{_embedded}{'ngcp:domains'} )
? $data->{_embedded}{'ngcp:domains'}[0]
: $data->{_embedded}{'ngcp:domains'};
$domain_id = $selected_domain->{id};
$domain_name = $selected_domain->{domain};
diag("domain: $selected_domain->{domain} ($domain_id)");
}
my ($contract_id, $reseller_id, $customer_id, $bprof_id, $customercontact_id, $network_tag);
{
$req = HTTP::Request->new('POST', $uri.'/api/contracts/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
contact_id => 2,
status => 'active',
type => 'reseller',
billing_profile_id => 1,
}));
$res = $ua->request($req);
is($res->code, 201, "POST create contract");
($contract_id) = $res->header('Location') =~ m!/(\d+)$!;
ok($contract_id, "got contract_id") || die "we don't continue here";
$req = HTTP::Request->new('POST', $uri.'/api/resellers/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
contract_id => $contract_id,
name => 'rtc test reseller ' . time,
enable_rtc => JSON::true,
status => 'active',
rtc_networks => ['sip','xmpp','webrtc'],
}));
$res = $ua->request($req);
is($res->code, 201, "POST create reseller");
($reseller_id) = $res->header('Location') =~ m!/(\d+)$!;
ok($reseller_id, "got reseller_id") || die "we don't continue here";
$req = HTTP::Request->new('GET', $uri . "/api/resellers/$reseller_id");
$res = $ua->request($req);
is($res->code, 200, "fetch POSTed reseller");
$data = JSON::from_json($res->decoded_content);
ok($data->{enable_rtc}, "reseller has rtc enabled");
$req = HTTP::Request->new('GET', $uri . "/api/rtcnetworks/$reseller_id");
$res = $ua->request($req);
is($res->code, 200, "fetch rtcnetworks");
$data = JSON::from_json($res->decoded_content);
is($data->{networks}[0]{connector}, 'sip-connector', "rtcnetwork exists");
$network_tag = $data->{networks}[0]{tag};
diag("reseller id: $reseller_id , first network_tag: $network_tag");
$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 => 'rtc test bprof ' . time,
handle => 'rtc_test_bprof_' . time,
reseller_id => $reseller_id,
}));
$res = $ua->request($req);
is($res->code, 201, "POST create billingprofile");
($bprof_id) = $res->header('Location') =~ m!/(\d+)$!;
ok($bprof_id, "got bprof_id") || die "we don't continue here";
$req = HTTP::Request->new('POST', $uri.'/api/customercontacts/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
email => 'rtccustomer@ngcp.com',
reseller_id => $reseller_id,
}));
$res = $ua->request($req);
is($res->code, 201, "POST create customercontact");
($customercontact_id) = $res->header('Location') =~ m!/(\d+)$!;
ok($customercontact_id, "got customercontact_id") || die "we don't continue here";
$req = HTTP::Request->new('POST', $uri.'/api/customers/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
contact_id => $customercontact_id,
billing_profile_id => $bprof_id,
reseller_id => $reseller_id,
status => 'active',
type => 'sipaccount',
}));
$res = $ua->request($req);
is($res->code, 201, "POST create customer");
($customer_id) = $res->header('Location') =~ m!/(\d+)$!;
ok($customer_id, "got customer_id") || die "we don't continue here";
diag("customer id: $customer_id");
}
my ($sub1_id, $sub1_name, $sub2_id, $sub2_name);
{
$sub1_name = 'rtcsub' .int(rand(1000));
$sub2_name = 'rtcsub' .int(rand(1000));
$req = HTTP::Request->new('POST', $uri.'/api/subscribers/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
customer_id => $customer_id,
domain_id => $domain_id,
username => $sub1_name,
password => $sub1_name,
webusername => $sub1_name,
webpassword => $sub1_name,
}));
$res = $ua->request($req);
is($res->code, 201, "POST create subscriber 1");
($sub1_id) = $res->header('Location') =~ m!/(\d+)$!;
ok($sub1_id, "got sub1_id") || die "we don't continue here";
$req = HTTP::Request->new('PATCH', $uri."/api/subscriberpreferences/$sub1_id");
$req->header('Content-Type' => 'application/json-patch+json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json([
{op => 'add', path => '/use_rtpproxy', value => 'never'},
]));
$res = $ua->request($req);
is($res->code, 200, "PATCH set subscriberpreferences sub1");
diag("subscriber $sub1_name\@$domain_name (pass: $sub1_name, id: $sub1_id)");
$req = HTTP::Request->new('POST', $uri.'/api/subscribers/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
customer_id => $customer_id,
domain_id => $domain_id,
username => $sub2_name,
password => $sub2_name,
webusername => $sub2_name,
webpassword => $sub2_name,
}));
$res = $ua->request($req);
is($res->code, 201, "POST create subscriber 2");
($sub2_id) = $res->header('Location') =~ m!/(\d+)$!;
ok($sub2_id, "got sub2_id") || die "we don't continue here";
$req = HTTP::Request->new('PATCH', $uri."/api/subscriberpreferences/$sub2_id");
$req->header('Content-Type' => 'application/json-patch+json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json([
{op => 'add', path => '/use_rtpproxy', value => 'never'},
]));
$res = $ua->request($req);
is($res->code, 200, "PATCH set subscriberpreferences sub2");
diag("subscriber $sub2_name\@$domain_name (pass: $sub2_name, id: $sub2_id)");
diag("you can now create new session using:");
my $noport_uri = ($uri =~ s/:[0-9]+//r);
diag(" curl -XPOST -v -k --user $sub1_name\@$domain_name:$sub1_name -H'Content-Type: application/json' $noport_uri/api/rtcsessions/ --data-binary '{}'");
diag(" curl -XPOST -v -k --user $sub2_name\@$domain_name:$sub2_name -H'Content-Type: application/json' $noport_uri/api/rtcsessions/ --data-binary '{}'");
}
done_testing;

@ -1,91 +0,0 @@
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:

@ -349,26 +349,6 @@
is:
code: 200
#check options for /api/rtcapps/
-
name: check OPTIONS for /api/rtcapps/
type: item
method: OPTIONS
path: /api/rtcapps/
conditions:
is:
code: 200
#GET /api/rtcapps/
-
name: GET /api/rtcapps/
type: item
method: GET
path: /api/rtcapps/
conditions:
is:
code: 200
#check options for /api/callrecordingstreams/
-
name: check OPTIONS for /api/callrecordingstreams/
@ -1329,26 +1309,6 @@
is:
code: 200
#check options for /api/rtcsessions/
-
name: check OPTIONS for /api/rtcsessions/
type: item
method: OPTIONS
path: /api/rtcsessions/
conditions:
is:
code: 200
#GET /api/rtcsessions/
-
name: GET /api/rtcsessions/
type: item
method: GET
path: /api/rtcsessions/
conditions:
is:
code: 200
#check options for /api/invoices/
-
name: check OPTIONS for /api/invoices/
@ -1729,26 +1689,6 @@
is:
code: 200
#check options for /api/rtcnetworks/
-
name: check OPTIONS for /api/rtcnetworks/
type: item
method: OPTIONS
path: /api/rtcnetworks/
conditions:
is:
code: 200
#GET /api/rtcnetworks/
-
name: GET /api/rtcnetworks/
type: item
method: GET
path: /api/rtcnetworks/
conditions:
is:
code: 200
#check options for /api/pbxdevicefirmwarebinaries/
-
name: check OPTIONS for /api/pbxdevicefirmwarebinaries/

@ -13,20 +13,6 @@
"status": "active"
}
},
{
"name": "rtc reseller contract",
"type": "contracts",
"depends": [
"default systemcontact",
"system billingprofile"
],
"data": {
"billing_profile_id": "${system billingprofile}",
"contact_id": "${default systemcontact}",
"type": "reseller",
"status": "active"
}
},
{
"name": "locked reseller contract",
"type": "contracts",

@ -8,8 +8,7 @@
"data": {
"contract_id": "${reseller contract}",
"name": "reseller_${sid}",
"status": "active",
"enable_rtc": false
"status": "active"
}
},
{
@ -23,30 +22,5 @@
"login": "reseller_${sid}",
"password": "reseller_${sid}"
}
},
{
"name": "rtc reseller",
"type": "resellers",
"depends": [
"rtc reseller contract"
],
"data": {
"contract_id": "${rtc reseller contract}",
"name": "rtcreseller_${sid}",
"status": "active",
"enable_rtc": true
}
},
{
"name": "rtc reseller admin",
"type": "admins",
"depends": [
"rtc reseller"
],
"data": {
"reseller_id": "${rtc reseller}",
"login": "rtcreseller_${sid}",
"password": "rtcreseller_${sid}"
}
}
]

@ -285,11 +285,6 @@ sub build_data{
],
},
},
'rtcsessions' => {
'data' => {
subscriber_id => sub { return shift->get_id('subscribers',@_); },
},
},
'topupcash' => {
'data' => {
subscriber_id => sub { return shift->get_id('subscribers',@_); },

@ -921,18 +921,6 @@ class testrun(unittest.TestCase):
self.assertTrue(
len(driver.find_elements_by_xpath('//*[@id="q-app"]/div//main//div//table/tbody/tr[1]/td[6]/span[contains(., "Locked")]')) > 0, "Subscriber status was not edited")
print("OK")
print("Try to enable and disable WebRTC...", end="")
driver.find_element_by_xpath('//*[@id="q-app"]//div//main//div/table/tbody/tr[1]/td[7]/div').click()
driver.implicitly_wait(2)
if len(driver.find_elements_by_xpath('/html/body//div[@role="alert"]//div[contains(., "Status code: 400")]')) == 0:
self.assertTrue(
len(driver.find_elements_by_xpath('//*[@id="q-app"]//div//main//div/table/tbody/tr[1]/td[7]/div[@aria-checked="true"]')) > 0, "WebRTC was not enabled")
driver.find_element_by_xpath('//*[@id="q-app"]//div//main//div/table/tbody/tr[1]/td[7]/div').click()
self.assertTrue(
len(driver.find_elements_by_xpath('//*[@id="q-app"]//div//main//div/table/tbody/tr[1]/td[7]/div[@aria-checked="false"]')) > 0, "WebRTC was not disabled")
print("OK")
else:
print("OK")
driver.implicitly_wait(10)
print("Try to open the reseller edit page...", end="")
driver.find_element_by_xpath('//*[@id="q-app"]/div//main//div//table/tbody/tr[1]/td[2]/button').click()

Loading…
Cancel
Save