TT#21348 Add Item class for the conversations API

Change-Id: Id357e747842d5c3e0f11634dfd5fa950ca36cb1c
changes/91/15691/21
Irina Peshinskaya 8 years ago
parent d67c2c09a6
commit 34bc2d5485

@ -10,6 +10,7 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::API::Calllist;
use DateTime::TimeZone;
use NGCP::Panel::Utils::CallList qw();
require Catalyst::ActionRole::ACL;
@ -312,7 +313,7 @@ sub GET :Allow {
return;
}
my $owner = $self->get_owner_data($c, $schema);
my $owner = NGCP::Panel::Utils::API::Calllist::get_owner_data($self, $c, $schema);
last unless $owner;
$c->stash(owner => $owner); # for query_param: direction
my $items = $self->item_rs($c);

@ -5,6 +5,7 @@ use Sipwise::Base;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::API::Calllist;
use NGCP::Panel::Utils::ValidateJSON qw();
use DateTime::TimeZone;
@ -65,7 +66,7 @@ sub GET :Allow {
my $schema = $c->model('DB');
last unless $self->valid_id($c, $id);
my $owner = $self->get_owner_data($c, $schema);
my $owner = NGCP::Panel::Utils::API::Calllist::get_owner_data($self, $c, $schema);
last unless $owner;
my $href_data = $owner->{subscriber} ?
"subscriber_id=".$owner->{subscriber}->id :

@ -0,0 +1,16 @@
package NGCP::Panel::Controller::API::ConversationsItem;
use Sipwise::Base;
use NGCP::Panel::Utils::Generic qw(:all);
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::Conversations/;
__PACKAGE__->set_config();
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
1;
# vim: set tabstop=4 expandtab:

@ -3,22 +3,106 @@ package NGCP::Panel::Form::Conversation::API;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
has_field 'id' => (
type => 'PosInteger',
label => 'The original conversation record id - cdr id/voicemail id/sms id/fax journal record id/prosody message archive mgmt (mam) record id.',
required => 1,
);
has_field 'call_id' => (
type => 'Text',
label => 'Call id.',
required => 1,
);
has_field 'call_type' => (
type => 'Text',
label => 'One of the "call","cfu","cft","cfb","cfna".',
required => 1,
);
has_field 'start_time' => (
type => 'Text',
label => 'The timestamp of the conversation event.',
required => 1,
);
has_field 'type' => (
type => 'Text',
label => 'The conversation event type: call/voicemail/sms/fax/xmpp.',
required => 1,
);
has_field 'id' => (
type => 'PosInteger',
label => 'The original conversation record id - cdr id/voicemail id/sms id/fax journal record id/prosody message archive mgmt (mam) record id.',
has_field 'status' => (
type => 'Text',
label => 'Status of the conversation. Possible values are: "ok","busy","noanswer","cancel","offline","timeout","other".',
required => 1,
);
has_field 'timestamp' => (
has_field 'rating_status' => (
type => 'Text',
label => 'The timestamp of the conversation event.',
label => 'Status of the rate processing for the conversation. Possible values are: "unrated","ok","failed".',
required => 1,
);
has_field 'caller' => (
type => 'Text',
label => 'Conversation initiator.',
required => 1,
);
has_field 'callee' => (
type => 'Text',
label => 'Conversation receiver.',
required => 1,
);
has_field 'direction' => (
type => 'Text',
label => 'Conversation direction.',
required => 1,
);
has_field 'duration' => (
type => 'Text',
label => 'Conversation duration.',
required => 1,
);
has_field 'subscriber_id' => (
type => 'PosInteger',
label => 'Subscriber who can manage fax record.',
required => 0,
);
has_field 'pages' => (
type => 'Integer',
label => 'Number of the pages in the fax document.',
required => 0,
);
has_field 'filename' => (
type => 'Text',
label => 'Filename of the fax document.',
required => 0,
);
has_field 'folder' => (
type => 'Text',
label => 'The folder the message is currently in (one of INBOX, Old, Work, Friends, Family, Cust1-Cust6).',
required => 0,
);
has_field 'context' => (
type => 'Text',
label => 'TBD',
required => 0,
);
has_field 'voicemail_subscriber_id' => (
type => 'Integer',
label => 'The subscriber id the message belongs to.',
required => 0,
);
1;

@ -589,6 +589,7 @@ sub paginate_order_collection_rs {
my ($self, $c, $item_rs, $params) = @_;
my($page,$rows,$order_by,$direction) = @$params{qw/page rows order_by direction/};
my $result_class = $item_rs->result_class();
my $total_count = int($item_rs->count);
$item_rs = $item_rs->search(undef, {
page => $page,
@ -608,6 +609,11 @@ sub paginate_order_collection_rs {
$c->log->debug("ordering by $col");
}
}
my $result_class_after = $item_rs->result_class();
if($result_class ne $result_class_after){
$item_rs->result_class($result_class);
}
return ($total_count, $item_rs);
}
@ -891,6 +897,7 @@ sub hal_from_item {
}
my $resource = $self->resource_from_item($c, $item, $form);
$resource = $self->process_hal_resource($c, $item, $resource, $form);
return unless $resource;
my $links = $self->hal_links($c, $item, $resource, $form) // [];
my $hal = NGCP::Panel::Utils::DataHal->new(
links => [
@ -902,8 +909,22 @@ sub hal_from_item {
),
NGCP::Panel::Utils::DataHalLink->new(relation => 'collection', href => sprintf("/api/%s/", $self->resource_name)),
NGCP::Panel::Utils::DataHalLink->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
NGCP::Panel::Utils::DataHalLink->new(relation => 'self', href => sprintf("%s%s", $self->dispatch_path, $self->get_item_id($c, $item))),
NGCP::Panel::Utils::DataHalLink->new(relation => "ngcp:".$self->resource_name, href => sprintf("/api/%s/%s", $self->resource_name, $self->get_item_id($c, $item))),
NGCP::Panel::Utils::DataHalLink->new(
relation => 'self',
href => sprintf(
"%s%s",
$self->dispatch_path,
$self->get_item_id($c, $item, undef, undef, { purpose => 'hal_links_href' })
),
),
NGCP::Panel::Utils::DataHalLink->new(
relation => "ngcp:".$self->resource_name,
href => sprintf(
"/api/%s/%s",
$self->resource_name,
$self->get_item_id($c, $item, undef, undef, { purpose => 'hal_links_href' })
)
),
@$links
],
relation => 'ngcp:'.$self->resource_name,
@ -1027,8 +1048,13 @@ sub get_form {
return ;
}
sub get_list{
my ($self, $c) = @_;
return $self->item_rs($c);
}
sub get_item_id{
my($self, $c, $item, $resource, $form) = @_;
my($self, $c, $item, $resource, $form, $params) = @_;
return int(blessed $item ? $item->id : $item->{id});
}
@ -1189,5 +1215,6 @@ sub return_requested_type {
$self->error($c, HTTP_BAD_REQUEST, $e);
}
}
1;
# vim: set tabstop=4 expandtab:

@ -126,76 +126,7 @@ sub item_by_id {
return $item_rs->find($id);
}
sub get_owner_data {
my ($self, $c, $schema) = @_;
my $ret;
if($c->user->roles eq "admin" || $c->user->roles eq "reseller") {
if($c->req->param('subscriber_id')) {
my $sub = $schema->resultset('voip_subscribers')->find($c->req->param('subscriber_id'));
unless($sub) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
if($c->user->roles eq "reseller" && $sub->contract->contact->reseller_id != $c->user->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
return {
subscriber => $sub,
customer => $sub->contract,
};
} elsif($c->req->param('customer_id')) {
my $cust = $schema->resultset('contracts')->find($c->req->param('customer_id'));
unless($cust && $cust->contact->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'customer_id'.");
return;
}
if($c->user->roles eq "reseller" && $cust->contact->reseller_id != $c->user->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'customer_id'.");
return;
}
return {
subscriber => undef,
customer => $cust,
};
} else {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Mandatory parameter 'subscriber_id' or 'customer_id' missing in request");
return;
}
} elsif($c->user->roles eq "subscriberadmin") {
if($c->req->param('subscriber_id')) {
my $sub = $schema->resultset('voip_subscribers')->find($c->req->param('subscriber_id'));
unless($sub) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
if($sub->contract_id != $c->user->account_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
return {
subscriber => $sub,
customer => $sub->contract,
};
} else {
my $cust = $schema->resultset('contracts')->find($c->user->account_id);
unless($cust && $cust->contact->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'customer_id'.");
return;
}
return {
subscriber => undef,
customer => $cust,
};
}
} else {
return {
subscriber => $c->user->voip_subscriber,
customer => $c->user->voip_subscriber->contract,
};
}
}
1;
# vim: set tabstop=4 expandtab:

@ -3,15 +3,19 @@ package NGCP::Panel::Role::API::Conversations;
use parent qw/NGCP::Panel::Role::API/;
use Sipwise::Base;
#use NGCP::Panel::Utils::Generic qw(:all);
use NGCP::Panel::Utils::Generic qw(:all);
use NGCP::Panel::Utils::CallList qw();
use NGCP::Panel::Utils::API::Calllist qw();
use NGCP::Panel::Utils::Fax;
use NGCP::Panel::Utils::DateTime qw();
use DateTime::Format::Strptime qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Form;
use Data::Dumper;
use Tie::IxHash;
#use Class::Hash;
my %call_fields = ();
my $call_fields_tied = tie(%call_fields, 'Tie::IxHash');
@ -38,53 +42,33 @@ $call_fields{source_customer_cost} = 'me.source_customer_cost';
$call_fields{destination_customer_cost} = 'me.destination_customer_cost';
$call_fields{source_customer_free_time} = 'me.source_customer_free_time';
my $cdr_proto = _hash2obj(
classname => 'cdr_item',
accessors => {
(map { $_ => _get_alias($call_fields_tied->Indices($_) + 1); } keys %call_fields),
#call_id => sub { #override
# my $self = shift;
# use Data::Dumper;
# my $test = { %$self };
# delete $test->{c};
# $self->{c}->log->debug(Dumper([map { $_ => _get_alias($call_fields_tied->Indices($_) + 1); } keys %call_fields]));
# return $self->{'field15'};
#},
get_column => sub {
my ($self,$colname) = @_;
return $self->{$colname};
},
source_subscriber => sub {
my $self = shift;
return $self->{c}->model('DB')->resultset('voip_subscribers')->find({ uuid => $self->source_user_id() });
},
destination_subscriber => sub {
my $self = shift;
return $self->{c}->model('DB')->resultset('voip_subscribers')->find({ uuid => $self->destination_user_id() });
},
},
);
#this is exactly cdr item, although we take call fields.
#Call fields contain everyithing that process_cdr_item requires
my %voicemail_fields = ();
tie(%voicemail_fields, 'Tie::IxHash');
my $voicemail_fields_tied = tie(%voicemail_fields, 'Tie::IxHash');
$voicemail_fields{duration} = 'me.duration';
$voicemail_fields{origtime} = 'me.origtime';
$voicemail_fields{callerid} = 'me.callerid';
$voicemail_fields{callee} = 'me.callerid';
$voicemail_fields{mailboxuser} = 'me.mailboxuser';
$voicemail_fields{context} = 'me.context';
$voicemail_fields{macrocontext} = 'me.macrocontext';
$voicemail_fields{mailboxcontext} = 'me.mailboxcontext';
$voicemail_fields{dir} = 'me.dir';
my %sms_fields = ();
tie(%sms_fields, 'Tie::IxHash');
my $sms_fields_tied = tie(%sms_fields, 'Tie::IxHash');
$sms_fields{subscriber_id} = 'me.subscriber_id';
$sms_fields{time} = 'me.time';
$sms_fields{direction} = 'me.direction';
$sms_fields{caller} = 'me.callee';
$sms_fields{text} = 'me.text';
$sms_fields{caller} = 'me.caller';
$sms_fields{callee} = 'me.callee';
$sms_fields{text} = 'me.text';
$sms_fields{reason} = 'me.reason';
$sms_fields{status} = 'me.status';
my %fax_fields = ();
tie(%fax_fields, 'Tie::IxHash');
my $fax_fields_tied = tie(%fax_fields, 'Tie::IxHash');
$fax_fields{subscriber_id} = 'me.subscriber_id';
$fax_fields{time} = 'me.time';
$fax_fields{direction} = 'me.direction';
@ -102,7 +86,7 @@ $fax_fields{caller_uuid} = 'me.caller_uuid';
$fax_fields{callee_uuid} = 'me.callee_uuid';
my %xmpp_fields = ();
tie(%xmpp_fields, 'Tie::IxHash');
my $xmpp_fields_tied = tie(%xmpp_fields, 'Tie::IxHash');
$xmpp_fields{subscriber_id} = 'me.id';
$xmpp_fields{user} = 'me.user';
$xmpp_fields{with} = 'me.with';
@ -115,12 +99,85 @@ $max_fields = scalar keys %sms_fields if ((scalar keys %sms_fields) > $max_field
$max_fields = scalar keys %fax_fields if ((scalar keys %fax_fields) > $max_fields);
$max_fields = scalar keys %xmpp_fields if ((scalar keys %xmpp_fields) > $max_fields);
my $cdr_proto = NGCP::Panel::Utils::Generic::hash2obj(
classname => 'cdr_item',
accessors => {
%{_get_fields_names(\%call_fields,$call_fields_tied)},
get_column => sub {
my ($self,$colname) = @_;
return $self->{$colname};
},
source_subscriber => sub {
my $self = shift;
return $self->{c}->model('DB')->resultset('voip_subscribers')->find({ uuid => $self->source_user_id() });
},
destination_subscriber => sub {
my $self = shift;
return $self->{c}->model('DB')->resultset('voip_subscribers')->find({ uuid => $self->destination_user_id() });
},
},
);
my $fax_proto = NGCP::Panel::Utils::Generic::hash2obj(
classname => 'fax_item',
accessors => {
%{_get_fields_names(\%fax_fields,$fax_fields_tied)},
get_column => sub {
my ($self,$colname) = @_;
return $self->{$colname};
},
caller_subscriber => sub {
my $self = shift;
return $self->{c}->model('DB')->resultset('voip_subscribers')->find({ uuid => $self->caller_uuid() });
},
callee_subscriber => sub {
my $self = shift;
return $self->{c}->model('DB')->resultset('voip_subscribers')->find({ uuid => $self->callee_uuid() });
},
provisioning_voip_subscriber => sub {
my $self = shift;
return $self->{c}->model('DB')->resultset('provisioning_voip_subscribers')->find({ id => $self->suscriber_id() });
},
},
);
my $voicemail_proto = NGCP::Panel::Utils::Generic::hash2obj(
classname => 'voicemail_item',
accessors => {
%{_get_fields_names(\%voicemail_fields,$voicemail_fields_tied)},
get_column => sub {
my ($self,$colname) = @_;
return $self->{$colname};
},
},
);
my $sms_proto = NGCP::Panel::Utils::Generic::hash2obj(
classname => 'sms_item',
accessors => {
%{_get_fields_names(\%sms_fields,$sms_fields_tied)},
get_column => sub {
my ($self,$colname) = @_;
return $self->{$colname};
},
},
);
my $xmpp_proto = NGCP::Panel::Utils::Generic::hash2obj(
classname => 'xmpp_item',
accessors => {
%{_get_fields_names(\%xmpp_fields,$xmpp_fields_tied)},
get_column => sub {
my ($self,$colname) = @_;
return $self->{$colname};
},
},
);
my %enabled_conversations = (
call => 1,
call => 1,
voicemail => 1,
sms => 1,
fax => 1,
xmpp => 0,
sms => 1,
fax => 1,
xmpp => 0,
);
sub item_name{
@ -143,29 +200,59 @@ sub config_allowed_roles {
return [qw/admin reseller subscriberadmin subscriber/];
}
sub get_list{
my ($self, $c) = @_;
#TODO: move to config and return to the SUPER (Entities) again
#So: if config->{methtod}->{required_params} eq '' owner
#and for other types of possible required parameters or predefined parameters groups
my $schema = $c->model('DB');
my $owner = NGCP::Panel::Utils::API::Calllist::get_owner_data($self, $c, $schema);
unless (defined $owner) {
return;
}
return $self->item_rs($c, $owner);
}
sub valid_id {
my ($self, $c, $id) = @_;
my $source = $c->req->params;
my $type = $source->{type};
unless($type){
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Mandatory parameter 'type' missing in request");
return;
}
return $self->SUPER::valid_id($c, $id);
}
sub get_item_id{
my($self, $c, $item, $resource, $form, $params) = @_;
my ($id, $type);
if('HASH' eq ref $item){
$id = int($item->{id});
$type = $item->{type};
}elsif(blessed $item){
$id = int($item->id);
$type = $item->type;
}
if(('HASH' eq ref $params) && 'hal_links_href' eq $params->{purpose}){
return $id.'?type='.$type;
}
return $id ;
}
sub _item_rs {
my ($self, $c, $params) = @_;
my ($self, $c, $owner, $params) = @_;
$params //= $c->req->params;
$params = { %$params };
my $schema = $c->model('DB');
my ($uuid,$contract_id,$reseller_id,$provider_id,$show);
(my $subscriber,$uuid) = $self->_get_subscriber($c,$params);
(my $contract,$contract_id) = $self->_get_contract($c,$params);
if ($c->user->roles eq "subscriber") {
$uuid = $c->user->voip_subscriber->uuid;
} elsif ($c->user->roles eq "subscriberadmin") {
$contract_id = $c->user->account_id;
} elsif ($c->user->roles eq "reseller") {
$reseller_id = $c->user->reseller_id;
$provider_id = $c->user->reseller->contract_id;
}
unless (defined $uuid or defined $contract_id) {
#die subscriber_id or customer_id required
}
$contract_id = $owner->{customer} ? $owner->{customer}->id : undef ;
$uuid = $owner->{subscriber} ? $owner->{subscriber}->uuid : undef;
$reseller_id = $owner->{customer} ? $owner->{customer}->contact->reseller_id : undef;
$provider_id = $owner->{customer} ? $owner->{customer}->contact->reseller->contract_id : undef;
my $item_rs;
my $type_param = ((exists $params->{type}) ? ($params->{type} // '') : undef);
@ -190,41 +277,6 @@ sub _item_rs {
}
sub _get_subscriber {
my ($self, $c, $params) = @_;
$params //= $c->req->params;
my ($subscriber,$uuid);
if ($params->{subscriber_id}) {
eval {
$subscriber = $c->model('DB')->resultset('voip_subscribers')->find($params->{subscriber_id});
};
if ($subscriber) {
$uuid = $subscriber->uuid;
} else {
#die invalid subscriber_id '$params->{subscriber_id}'
}
}
return ($subscriber,$uuid);
}
sub _get_contract {
my ($self, $c, $params) = @_;
$params //= $c->req->params;
my ($contract,$contract_id);
if ($params->{customer_id}) {
# ensure integer, allow terminated
eval {
$contract = $c->model('DB')->resultset('contracts')->find($params->{customer_id});
};
if ($contract) {
$contract_id = $contract->id;
} else {
#die invalid customer_id '$params->{customer_id}'
}
}
return ($contract,$contract_id);
}
sub _apply_timestamp_from_to {
my $self = shift;
my %params = @_;
@ -471,14 +523,14 @@ sub _get_sms_rs {
$rs = $rs->search_rs({
'contact.reseller_id' => $reseller_id,
},{
join => { provisioning_voip_subscriber => { subscriber => { contract => 'contact'} } },
join => { provisioning_voip_subscriber => { voip_subscriber => { contract => 'contact'} } },
});
}
if ($contract_id) {
$rs = $rs->search({
'contract.id' => $contract_id,
},{
join => { provisioning_voip_subscriber => { subscriber => 'contract' } },
join => { provisioning_voip_subscriber => { voip_subscriber => 'contract' } },
});
}
if ($uuid) {
@ -527,14 +579,14 @@ sub _get_fax_rs {
$rs = $rs->search_rs({
'contact.reseller_id' => $reseller_id,
},{
join => { provisioning_voip_subscriber => { subscriber => { contract => 'contact'} } },
join => { provisioning_voip_subscriber => { voip_subscriber => { contract => 'contact'} } },
});
}
if ($contract_id) {
$rs = $rs->search({
'contract.id' => $contract_id,
},{
join => { provisioning_voip_subscriber => { subscriber => 'contract' } },
join => { provisioning_voip_subscriber => { voip_subscriber => 'contract' } },
});
}
if ($uuid) {
@ -671,7 +723,6 @@ sub _get_as_list {
#push(@as,($accessors[$i - 1] ? $accessors[$i - 1] : 'field'.$i));
}
return @as;
}
sub _get_alias {
@ -680,47 +731,118 @@ sub _get_alias {
sub get_form {
my ($self, $c) = @_;
return (NGCP::Panel::Form::get("NGCP::Panel::Form::Conversation::API", $c),['id']);
return (NGCP::Panel::Form::get("NGCP::Panel::Form::Conversation::API", $c),[qw/id call_id source_user_id source_account_id destination_user_id destination_account_id call_id subscriber_id voicemail_subscriber_id/]);
}
sub process_hal_resource {
my($self, $c, $item, $resource, $form) = @_;
use Data::Dumper;
#$c->log->debug(Dumper($item));
#$c->log->debug(Dumper($resource));
my $schema = $c->model('DB');
# todo: mashal specific fields, per conversation event type ...
my ($item_mock_obj, $item_accessors_hash) = _get_item_object($c, $item);
if('call' eq $item->{type}){
my $cdr_subscriber_id = $c->model('DB')->resultset('voip_subscribers')->search_rs({
'uuid' => $item_mock_obj->source_user_id,
})->first->id;
my $cdr_customer_id = $item_mock_obj->source_account_id;
my $owner = NGCP::Panel::Utils::API::Calllist::get_owner_data($self, $c, $schema, { subscriber_id => $cdr_subscriber_id } );
if(!$owner){
return;
}
$resource = NGCP::Panel::Utils::CallList::process_cdr_item(
$c,
$item_mock_obj,
$owner,
);
@{$resource}{qw/caller callee/} = @{$resource}{qw/own_cli other_cli/};
}elsif('fax' eq $item->{type}){
my $fax_subscriber = $c->model('DB')->resultset('provisioning_voip_subscribers')->search_rs({
'id' => $item_mock_obj->subscriber_id,
})->first->voip_subscriber;
$resource = NGCP::Panel::Utils::Fax::process_fax_journal_item($c, $item_mock_obj, $fax_subscriber);
foreach my $field (qw/type id status reason pages filename/){
$resource->{$field} = $item_mock_obj->$field;
}
}elsif('voicemail' eq $item->{type}){
$resource = $item_accessors_hash;
$resource->{caller} = $item_mock_obj->callerid;
$resource->{voicemail_subscriber_id} = $c->model('DB')->resultset('voicemail_spool')->search_rs({
'mailboxuser' => $item_mock_obj->mailboxuser,
})->first->mailboxuser->provisioning_voip_subscriber->voip_subscriber->id;
# type is last item of path like /var/spool/asterisk/voicemail/default/uuid/INBOX
my @p = split '/', $item_mock_obj->dir;
$resource->{folder} = pop @p;
}elsif('sms' eq $item->{type}){
$resource = $item_accessors_hash;
}elsif('xmpp' eq $item->{type}){
$resource = $item_accessors_hash;
}
$resource->{start_time} //= $item_mock_obj->timestamp;
my $datetime_fmt = DateTime::Format::Strptime->new(
pattern => '%F %T',
);
my $timestamp = NGCP::Panel::Utils::DateTime::epoch_local($resource->{timestamp});
my $timestamp = NGCP::Panel::Utils::DateTime::epoch_local($resource->{start_time});
#if($c->req->param('tz') && DateTime::TimeZone->is_valid_name($c->req->param('tz'))) {
# $timestamp->set_time_zone($c->req->param('tz'));
#}
$resource->{timestamp} = $datetime_fmt->format_datetime($timestamp);
$resource->{timestamp} .= '.' . $timestamp->millisecond if $timestamp->millisecond > 0.0;
$resource->{start_time} = $datetime_fmt->format_datetime($timestamp);
$resource->{start_time} .= '.' . $timestamp->millisecond if $timestamp->millisecond > 0.0;
# todo: mashal specific fields, per conversation event type ...
return $resource;
}
if ('call' eq $resource->{type}) {
my $cdr_item = NGCP::Panel::Utils::CallList::process_cdr_item($c,_hash2obj(
classname => ref $cdr_proto,
hash => $resource,
private => { c => $c, },
),{
subscriber => ($self->_get_subscriber($c))[0],
customer => ($self->_get_contract($c))[0],
},
);
# todo: populate $resource ...
$c->log->debug(Dumper($cdr_item));
sub _get_fields_names{
my ($fields, $fields_tied) = @_;
return { map { $_ => $fields_tied->Indices($_) ? _get_alias($fields_tied->Indices($_) + 1) : $_; } (keys %$fields, 'id','type','timestamp') };
}
sub _get_item_object{
my($c, $item) = @_;
my ($fields, $fields_tied, $class_proto) = _get_fields_by_type($item->{type});
my $item_accessors_hash = {%$item};
foreach(keys %$fields){
$item_accessors_hash->{$_} = $item->{_get_alias($fields_tied->Indices($_) + 1)};
}
return $resource;
my $item_mock_obj = NGCP::Panel::Utils::Generic::hash2obj(
classname => ref $class_proto,
hash => $item_accessors_hash,
private => { c => $c, },
);
return ($item_mock_obj, $item_accessors_hash);
}
sub _get_fields_by_type{
my($type) = @_;
my ($fields, $fields_tied, $proto);
if('call' eq $type){
$fields = \%call_fields;
$fields_tied = $call_fields_tied;
$proto = $cdr_proto;
}elsif('voicemail' eq $type){
$fields = \%voicemail_fields;
$fields_tied = $voicemail_fields_tied;
$proto = $voicemail_proto;
}elsif('sms' eq $type){
$fields = \%sms_fields;
$fields_tied = $sms_fields_tied;
$proto = $sms_proto;
}elsif('fax' eq $type){
$fields = \%fax_fields;
$fields_tied = $fax_fields_tied;
$proto = $fax_proto;
}elsif('xmpp' eq $type){
$fields = \%xmpp_fields;
$fields_tied = $xmpp_fields_tied;
$proto = $xmpp_proto;
}
return $fields,$fields_tied,$proto;
}
sub hal_links {
my($self, $c, $item, $resource, $form) = @_;
#$c->log->debug("hal_links: type=".($resource->{type} // 'undefined')."; id=".($resource->{id} // 'undefined').";");
return [
('call' eq $resource->{type} ?
NGCP::Panel::Utils::DataHalLink->new(relation => 'ngcp:calls', href => sprintf("/api/calls/%d", $resource->{id})) : ()),
@ -731,42 +853,10 @@ sub hal_links {
('fax' eq $resource->{type} ?
NGCP::Panel::Utils::DataHalLink->new(relation => 'ngcp:faxes', href => sprintf("/api/faxes/%d", $resource->{id})) : ()),
# todo - add xmpp mam rail:
# ('xmpp' eq $item->{type} ?
#('xmpp' eq $item->{type} ?
# NGCP::Panel::Utils::DataHalLink->new(relation => 'ngcp:xmpp', href => sprintf("/api/xmpp/%d", $item->{id})) : ()),
];
}
sub _hash2obj {
my %params = @_;
my ($hash,$private,$classname,$accessors) = @params{qw/hash private classname accessors/};
my $obj;
$obj = $hash if 'HASH' eq ref $hash;
$obj //= {};
$obj = { %$obj, %$private } if 'HASH' eq ref $private;
unless (defined $classname and length($classname) > 0) {
my @chars = ('A'..'Z');
$classname //= '';
$classname .= $chars[rand scalar @chars] for 1..8;
}
$classname = __PACKAGE__ . '::' . $classname unless $classname =~ /::/;
bless($obj,$classname);
no strict "refs";
return $obj if scalar %{$classname . '::'};
print "registering class $classname\n";
$accessors //= {};
foreach my $accessor (keys %$accessors) {
print "registering accessor $classname::$accessor\n";
*{$classname . '::' . $accessor} = sub {
my $self = shift;
return &{$accessors->{$accessor}}($self,@_);
} if 'CODE' eq ref $accessors->{$accessor};
*{$classname . '::' . $accessor} = sub {
my $self = shift;
return $self->{$accessors->{$accessor}};
} if '' eq ref $accessors->{$accessor};
}
return $obj;
}
1;

@ -44,11 +44,6 @@ sub config_allowed_roles {
return [qw/admin reseller/];
}
sub get_list{
my ($self, $c) = @_;
return $self->item_rs($c);
}
sub get {
my ($self, $c) = @_;
my $header_accept = $c->request->header('Accept');
@ -60,6 +55,7 @@ sub get {
my $rows = $c->request->params->{rows} // 10;
{
my $items = $self->get_list($c);
return unless $items;
(my $total_count, $items) = $self->paginate_order_collection($c, $items);
my (@embedded, @links);
my ($form) = $self->get_form($c);
@ -68,7 +64,8 @@ sub get {
push @embedded, $self->hal_from_item($c, $item, $form);
push @links, NGCP::Panel::Utils::DataHalLink->new(
relation => 'ngcp:'.$self->resource_name,
href => sprintf('/%s%s', $c->request->path, $self->get_item_id($c, $item)),
href => sprintf('/%s%s', $c->request->path, $self->get_item_id($c,
$item, undef, undef, { purpose => 'hal_links_href' })),
);
}
push @links,

@ -55,8 +55,8 @@ sub get {
my $item = $self->item_by_id_valid($c, $id);
last unless $item;
my $header_accept = $c->request->header('Accept');
if(defined $header_accept
&& ($header_accept ne 'application/json')
if(defined $header_accept
&& ($header_accept ne 'application/json')
&& ($header_accept ne '*/*')
) {
$self->return_requested_type($c,$id,$item);
@ -64,7 +64,7 @@ sub get {
}
my $hal = $self->hal_from_item($c, $item);
return unless $hal;
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-[a-z]+)"|rel="item $1"|r =~
@ -110,11 +110,11 @@ sub patch {
$self->complete_transaction($c);
$self->post_process_commit($c, 'patch', $item, $old_resource, $resource, $form, $process_extras);
$self->return_representation($c,
'item' => $item,
'form' => $form,
'preference' => $preference,
'form_exceptions' => $form_exceptions
$self->return_representation($c,
'item' => $item,
'form' => $form,
'preference' => $preference,
'form_exceptions' => $form_exceptions
);
}
return;
@ -152,11 +152,11 @@ sub put {
$self->complete_transaction($c);
$self->post_process_commit($c, 'put', $item, $old_resource, $resource, $form, $process_extras);
$self->return_representation($c,
'item' => $item,
'form' => $form,
'preference' => $preference,
'form_exceptions' => $form_exceptions
$self->return_representation($c,
'item' => $item,
'form' => $form,
'preference' => $preference,
'form_exceptions' => $form_exceptions
);
}
return;

@ -0,0 +1,111 @@
package NGCP::Panel::Utils::API::Calllist;
use strict;
use warnings;
use HTTP::Status qw(:constants);
sub get_owner_data {
my ($self, $c, $schema, $source) = @_;
my $ret;
$source //= $c->req->params;
my $src_subscriber_id = $source->{subscriber_id};
my $src_customer_id = $source->{customer_id};
if($c->user->roles eq "admin" || $c->user->roles eq "reseller") {
if($src_subscriber_id) {
my $sub = $schema->resultset('voip_subscribers')->find($src_subscriber_id);
unless($sub) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
if($c->user->roles eq "reseller" && $sub->contract->contact->reseller_id != $c->user->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
return {
subscriber => $sub,
customer => $sub->contract,
};
} elsif($src_customer_id) {
my $cust = $schema->resultset('contracts')->find($src_customer_id);
unless($cust && $cust->contact->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'customer_id'.");
return;
}
if($c->user->roles eq "reseller" && $cust->contact->reseller_id != $c->user->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'customer_id'.");
return;
}
return {
subscriber => undef,
customer => $cust,
};
} else {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Mandatory parameter 'subscriber_id' or 'customer_id' missing in request");
return;
}
} elsif($c->user->roles eq "subscriberadmin") {
if($src_subscriber_id) {
my $sub = $schema->resultset('voip_subscribers')->find($src_subscriber_id);
unless($sub) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
if($sub->contract_id != $c->user->account_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'subscriber_id'.");
return;
}
return {
subscriber => $sub,
customer => $sub->contract,
};
} else {
my $cust = $schema->resultset('contracts')->find($c->user->account_id);
unless($cust && $cust->contact->reseller_id) {
$self->error($c, HTTP_NOT_FOUND, "Invalid 'customer_id'.");
return;
}
return {
subscriber => undef,
customer => $cust,
};
}
} elsif($c->user->roles eq "subscriber") {
return {
subscriber => $c->user->voip_subscriber,
customer => $c->user->voip_subscriber->contract,
};
} else {
$self->error($c, HTTP_NOT_FOUND, "Unknown role '".$c->user->roles."' of the user.");
return;
}
}
1;
=head1 NAME
NGCP::Panel::Utils::API::Calllist
=head1 DESCRIPTION
A temporary helper to manipulate calls related data in REST API modules
=head1 METHODS
=head2 get_owner_data
Check if mandatory calls list parameters customer_id or subscriber_id are presented and get proper objects.
=head1 AUTHOR
Irina Peshinskaya
=head1 LICENSE
This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
# vim: set tabstop=4 expandtab:

@ -49,6 +49,8 @@ sub process_cdr_item {
$params //= $c->req->params;
$resource->{call_id} = $item->call_id;
$resource->{id} = $item->id;
$resource->{call_type} = $item->call_type;
my $intra = 0;
if($item->source_user_id && $item->source_account_id == $item->destination_account_id) {
@ -284,7 +286,7 @@ sub process_cdr_item {
$resource->{duration} = NGCP::Panel::Utils::DateTime::sec_to_hms($c,$item->duration,3);
$resource->{customer_cost} = $resource->{direction} eq "out" ?
$item->source_customer_cost : $item->destination_customer_cost;
if ($cust->add_vat) {
if (defined $cust && $cust->add_vat) {
$resource->{total_customer_cost} = $resource->{customer_cost} * (1 + $cust->vat_rate / 100);
} else {
$resource->{total_customer_cost} = $resource->{customer_cost};

@ -7,9 +7,9 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw(Exporter);
@EXPORT = ();
@EXPORT_OK = qw(is_int is_integer is_decimal merge compare is_false is_true get_inflated_columns_all);
@EXPORT_OK = qw(is_int is_integer is_decimal merge compare is_false is_true get_inflated_columns_all hash2obj);
%EXPORT_TAGS = ( DEFAULT => [qw(&is_int &is_integer &is_decimal &merge &compare &is_false &is_true)],
all => [qw(&is_int &is_integer &is_decimal &merge &compare &is_false &is_true &get_inflated_columns_all)]);
all => [qw(&is_int &is_integer &is_decimal &merge &compare &is_false &is_true &get_inflated_columns_all &hash2obj)]);
use Hash::Merge;
use Data::Compare qw//;
@ -105,4 +105,37 @@ sub get_inflated_columns_all{
#return [ map { { $_->get_inflated_columns }; } $rs->all ];
}
sub hash2obj {
my %params = @_;
my ($hash,$private,$classname,$accessors) = @params{qw/hash private classname accessors/};
my $obj;
$obj = $hash if 'HASH' eq ref $hash;
$obj //= {};
$obj = { %$obj, %$private } if 'HASH' eq ref $private;
unless (defined $classname and length($classname) > 0) {
my @chars = ('A'..'Z');
$classname //= '';
$classname .= $chars[rand scalar @chars] for 1..8;
}
$classname = __PACKAGE__ . '::' . $classname unless $classname =~ /::/;
bless($obj,$classname);
no strict "refs";
return $obj if scalar %{$classname . '::'};
print "registering class $classname\n";
$accessors //= {};
foreach my $accessor (keys %$accessors) {
print "registering accessor $classname::$accessor\n";
*{$classname . '::' . $accessor} = sub {
my $self = shift;
return &{$accessors->{$accessor}}($self,@_);
} if 'CODE' eq ref $accessors->{$accessor};
*{$classname . '::' . $accessor} = sub {
my $self = shift;
return $self->{$accessors->{$accessor}};
} if '' eq ref $accessors->{$accessor};
}
return $obj;
}
1;

Loading…
Cancel
Save