MT#7649 API: Implement create/update for pbx devs

Delete is not there yet.
gjungwirth/voicemail_number
Andreas Granig 11 years ago
parent 9511ca767e
commit 3845260070

@ -0,0 +1,80 @@
package NGCP::Panel::Controller::API::PbxDeviceConfigFiles;
use Sipwise::Base;
use namespace::sweep;
use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines the actual <a href="#pbxdevicefirmwares">PbxDeviceConfigs</a> Files.',
);
class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
]},
);
with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
class_has('resource_name', is => 'ro', default => 'pbxdeviceconfigfiles');
class_has('dispatch_path', is => 'ro', default => '/api/pbxdeviceconfigfiles/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdeviceconfigfiles');
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Args => 0,
Does => [qw(ACL CheckTrailingSlash RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods },
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
#$self->log_request($c);
return 1;
}
sub OPTIONS :Allow {
my ($self, $c) = @_;
my $allowed_methods = $self->allowed_methods;
$c->response->headers(HTTP::Headers->new(
Allow => $allowed_methods->join(', '),
Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name,
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
return;
}
# vim: set tabstop=4 expandtab:

@ -0,0 +1,85 @@
package NGCP::Panel::Controller::API::PbxDeviceConfigFilesItem;
use Sipwise::Base;
use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
with 'NGCP::Panel::Role::API::PbxDeviceConfigs';
class_has('resource_name', is => 'ro', default => 'pbxdeviceconfigfiles');
class_has('dispatch_path', is => 'ro', default => '/api/pbxdeviceconfigfiles/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdeviceconfigfiles');
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Args => 1,
Does => [qw(ACL RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub GET :Allow {
my ($self, $c, $id) = @_;
{
last unless $self->valid_id($c, $id);
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdevicefirmwarebinary => $item);
my $resource = $self->resource_from_item($c, $item);
$resource->{data} = $item->data;
$c->response->header ('Content-Disposition' => 'attachment; filename="pbxdeviceconfig_' . $item->device_id . '_' . $item->id . '"');
$c->response->content_type($item->content_type);
$c->response->body($resource->{data});
return;
}
return;
}
sub HEAD :Allow {
my ($self, $c, $id) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub OPTIONS :Allow {
my ($self, $c, $id) = @_;
my $allowed_methods = $self->allowed_methods;
$c->response->headers(HTTP::Headers->new(
Allow => $allowed_methods->join(', '),
Accept_Patch => 'application/json-patch+json',
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub end : Private {
my ($self, $c) = @_;
#$self->log_response($c);
}
# vim: set tabstop=4 expandtab:

@ -8,6 +8,8 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
@ -18,7 +20,7 @@ class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Specifies a config to be set in <a href="#pbxdeviceprofiles">PbxDeviceProfiles</a>.',
'Defines configs for a <a href="#pbxdevicemodels">PbxDeviceModel</a>. To create or update a config, do a POST or PUT with the proper Content-Type (e.g. text/xml) and pass the properties via query parameters, e.g. <span>/api/pbxdeviceconfigs/?device_id=1&amp;version=1.0</span>',
);
class_has 'query_params' => (
@ -26,31 +28,19 @@ class_has 'query_params' => (
isa => 'ArrayRef',
default => sub {[
{
param => 'content_type',
description => 'Filter for configs matching a content_type pattern',
param => 'device_id',
description => 'Filter for configs of a specific device model',
query => {
first => sub {
my $q = shift;
{ content_type => { like => $q } };
return { 'device_id' => $q };
},
second => sub {},
},
},
{
param => 'version',
description => 'Filter for configs matching a version name pattern',
query => {
first => sub {
my $q = shift;
{ version => { like => $q } };
},
second => sub {},
second => sub { },
},
},
]},
);
with 'NGCP::Panel::Role::API::PbxDeviceConfigs';
class_has('resource_name', is => 'ro', default => 'pbxdeviceconfigs');
@ -66,7 +56,7 @@ __PACKAGE__->config(
Does => [qw(ACL CheckTrailingSlash RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods },
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
@ -76,7 +66,6 @@ sub auto :Private {
$self->set_body($c);
$self->log_request($c);
return 1;
}
sub GET :Allow {
@ -84,15 +73,15 @@ sub GET :Allow {
my $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
{
my $field_devs = $self->item_rs($c);
(my $total_count, $field_devs) = $self->paginate_order_collection($c, $field_devs);
my $items = $self->item_rs($c);
(my $total_count, $items) = $self->paginate_order_collection($c, $items);
my (@embedded, @links);
for my $dev ($field_devs->all) {
push @embedded, $self->hal_from_item($c, $dev);
my $form = $self->get_form($c);
for my $item ($items->all) {
push @embedded, $self->hal_from_item($c, $item, $form);
push @links, Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
href => sprintf('%s%d', $self->dispatch_path, $dev->id),
href => sprintf('/%s%d', $c->request->path, $item->id),
);
}
push @links,
@ -103,12 +92,12 @@ sub GET :Allow {
templated => true,
),
Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
Data::HAL::Link->new(relation => 'self', href => sprintf('%s?page=%s&rows=%s', $self->dispatch_path, $page, $rows));
Data::HAL::Link->new(relation => 'self', href => sprintf('/%s?page=%s&rows=%s', $c->request->path, $page, $rows));
if(($total_count / $rows) > $page ) {
push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('%s?page=%d&rows=%d', $self->dispatch_path, $page + 1, $rows));
push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('/%s?page=%d&rows=%d', $c->request->path, $page + 1, $rows));
}
if($page > 1) {
push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('%s?page=%d&rows=%d', $self->dispatch_path, $page - 1, $rows));
push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('/%s?page=%d&rows=%d', $c->request->path, $page - 1, $rows));
}
my $hal = Data::HAL->new(
@ -146,11 +135,67 @@ sub OPTIONS :Allow {
return;
}
sub POST :Allow {
my ($self, $c) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $data = $self->get_valid_raw_post_data(
c => $c,
media_type => [qw#text/plain text/xml#],
);
last unless $data;
my $resource = $c->req->query_params;
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
exceptions => [ "device_id" ],
);
my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
id => $resource->{device_id}
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$model_rs = $model_rs->search({
reseller_id => $c->user->reseller_id,
});
}
my $model = $model_rs->first;
unless($model) {
$c->log->error("invalid device_id '$$resource{device_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
last;
}
$resource->{data} = $data;
$resource->{content_type} = $c->request->header('Content-Type');
my $item;
try {
$item = $model->autoprov_configs->create($resource);
} catch($e) {
$c->log->error("failed to create pbxdeviceconfig: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create pbxdeviceconfig.");
last;
}
$guard->commit;
$c->response->status(HTTP_CREATED);
$c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
return 1;
}
# vim: set tabstop=4 expandtab:

@ -1,14 +1,11 @@
package NGCP::Panel::Controller::API::PbxDeviceConfigsItem;
use Sipwise::Base;
use namespace::sweep;
use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::ValidateJSON qw();
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
@ -31,7 +28,7 @@ __PACKAGE__->config(
Does => [qw(ACL RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods },
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
@ -41,22 +38,22 @@ sub auto :Private {
$self->set_body($c);
$self->log_request($c);
return 1;
}
sub GET :Allow {
my ($self, $c, $id) = @_;
{
last unless $self->valid_id($c, $id);
my $field_dev = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, deviceprofile => $field_dev);
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdeviceconfig => $item);
my $hal = $self->hal_from_item($c, $field_dev);
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;
s|rel="(http://purl.org/sipwise/ngcp-api/#rel-resellers)"|rel="item $1"|;
s/rel=self/rel="item self"/;
$_
} $hal->http_headers),
), $hal->as_json);
$c->response->headers($response->headers);
@ -85,11 +82,71 @@ sub OPTIONS :Allow {
return;
}
sub PUT :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdeviceconfig => $item);
my $data = $self->get_valid_raw_put_data(
c => $c,
id => $id,
media_type => [qw#text/plain text/xml#],
);
last unless $data;
my $resource = $c->req->query_params;
$resource->{data} = $data;
my $form = $self->get_form($c);
my $old_resource = $self->resource_from_item($c, $item, $form);
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
last unless $item;
$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, $item, $form);
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->header(Preference_Applied => 'return=representation');
$c->response->body($response->content);
}
}
return;
}
=pod
sub DELETE :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdeviceconfig => $item);
$item->delete;
$guard->commit;
$c->response->status(HTTP_NO_CONTENT);
$c->response->body(q());
}
return;
}
=cut
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
return 1;
}
# vim: set tabstop=4 expandtab:

@ -0,0 +1,80 @@
package NGCP::Panel::Controller::API::PbxDeviceFirmwareBinaries;
use Sipwise::Base;
use namespace::sweep;
use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines the actual <a href="#pbxdevicefirmwares">PbxDeviceFirmwares</a> Binary.',
);
class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
]},
);
with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwarebinaries');
class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwarebinaries/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwarebinaries');
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Args => 0,
Does => [qw(ACL CheckTrailingSlash RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods },
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
#$self->log_request($c);
return 1;
}
sub OPTIONS :Allow {
my ($self, $c) = @_;
my $allowed_methods = $self->allowed_methods;
$c->response->headers(HTTP::Headers->new(
Allow => $allowed_methods->join(', '),
Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name,
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
return;
}
# vim: set tabstop=4 expandtab:

@ -0,0 +1,85 @@
package NGCP::Panel::Controller::API::PbxDeviceFirmwareBinariesItem;
use Sipwise::Base;
use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwarebinaries');
class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwarebinaries/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwarebinaries');
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Args => 1,
Does => [qw(ACL RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub GET :Allow {
my ($self, $c, $id) = @_;
{
last unless $self->valid_id($c, $id);
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdevicefirmwarebinary => $item);
my $resource = $self->resource_from_item($c, $item);
$resource->{data} = $item->data;
$c->response->header ('Content-Disposition' => 'attachment; filename="' . $resource->{filename} . '"');
$c->response->content_type('application/octet-stream');
$c->response->body($resource->{data});
return;
}
return;
}
sub HEAD :Allow {
my ($self, $c, $id) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub OPTIONS :Allow {
my ($self, $c, $id) = @_;
my $allowed_methods = $self->allowed_methods;
$c->response->headers(HTTP::Headers->new(
Allow => $allowed_methods->join(', '),
Accept_Patch => 'application/json-patch+json',
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub end : Private {
my ($self, $c) = @_;
#$self->log_response($c);
}
# vim: set tabstop=4 expandtab:

@ -0,0 +1,201 @@
package NGCP::Panel::Controller::API::PbxDeviceFirmwares;
use Sipwise::Base;
use namespace::sweep;
use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
'Defines firmwares for a <a href="#pbxdevicemodels">PbxDeviceModel</a>. To create or update a firmware, do a POST or PUT with Content-Type application/octet-stream and pass the properties via query parameters, e.g. <span>/api/pbxdevicefirmwares/?device_id=1&amp;filename=test.bin&amp;version=1.0</span>',
);
class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
{
param => 'device_id',
description => 'Filter for firmwares of a specific device model',
query => {
first => sub {
my $q = shift;
return { 'device_id' => $q };
},
second => sub { },
},
},
]},
);
with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwares');
class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwares/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwares');
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Args => 0,
Does => [qw(ACL CheckTrailingSlash RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub GET :Allow {
my ($self, $c) = @_;
my $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
{
my $items = $self->item_rs($c);
(my $total_count, $items) = $self->paginate_order_collection($c, $items);
my (@embedded, @links);
my $form = $self->get_form($c);
for my $item ($items->all) {
push @embedded, $self->hal_from_item($c, $item, $form);
push @links, Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
href => sprintf('/%s%d', $c->request->path, $item->id),
);
}
push @links,
Data::HAL::Link->new(
relation => 'curies',
href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}',
name => 'ngcp',
templated => true,
),
Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
Data::HAL::Link->new(relation => 'self', href => sprintf('/%s?page=%s&rows=%s', $c->request->path, $page, $rows));
if(($total_count / $rows) > $page ) {
push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('/%s?page=%d&rows=%d', $c->request->path, $page + 1, $rows));
}
if($page > 1) {
push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('/%s?page=%d&rows=%d', $c->request->path, $page - 1, $rows));
}
my $hal = Data::HAL->new(
embedded => [@embedded],
links => [@links],
);
$hal->resource({
total_count => $total_count,
});
my $response = HTTP::Response->new(HTTP_OK, undef,
HTTP::Headers->new($hal->http_headers(skip_links => 1)), $hal->as_json);
$c->response->headers($response->headers);
$c->response->body($response->content);
return;
}
return;
}
sub HEAD :Allow {
my ($self, $c) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub OPTIONS :Allow {
my ($self, $c) = @_;
my $allowed_methods = $self->allowed_methods;
$c->response->headers(HTTP::Headers->new(
Allow => $allowed_methods->join(', '),
Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name,
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub POST :Allow {
my ($self, $c) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $binary = $self->get_valid_raw_post_data(
c => $c,
media_type => 'application/octet-stream',
);
last unless $binary;
my $resource = $c->req->query_params;
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
exceptions => [ "device_id" ],
);
my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
id => $resource->{device_id}
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$model_rs = $model_rs->search({
reseller_id => $c->user->reseller_id,
});
}
my $model = $model_rs->first;
unless($model) {
$c->log->error("invalid device_id '$$resource{device_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
last;
}
$resource->{data} = $binary;
last unless($resource);
my $item;
try {
$item = $model->autoprov_firmwares->create($resource);
} catch($e) {
$c->log->error("failed to create pbxdevicefirmware: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create pbxdevicefirmware.");
last;
}
$guard->commit;
$c->response->status(HTTP_CREATED);
$c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
}
# vim: set tabstop=4 expandtab:

@ -0,0 +1,152 @@
package NGCP::Panel::Controller::API::PbxDeviceFirmwaresItem;
use Sipwise::Base;
use namespace::sweep;
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::HTTPMethods;
require Catalyst::ActionRole::RequireSSL;
with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwares');
class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwares/');
class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwares');
__PACKAGE__->config(
action => {
map { $_ => {
ACLDetachTo => '/api/root/invalid_user',
AllowedRole => [qw/admin reseller/],
Args => 1,
Does => [qw(ACL RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
} } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub GET :Allow {
my ($self, $c, $id) = @_;
{
last unless $self->valid_id($c, $id);
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdevicefirmware => $item);
my $hal = $self->hal_from_item($c, $item);
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
(map { # XXX Data::HAL must be able to generate links with multiple relations
s|rel="(http://purl.org/sipwise/ngcp-api/#rel-resellers)"|rel="item $1"|;
s/rel=self/rel="item self"/;
$_
} $hal->http_headers),
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->body($response->content);
return;
}
return;
}
sub HEAD :Allow {
my ($self, $c, $id) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub OPTIONS :Allow {
my ($self, $c, $id) = @_;
my $allowed_methods = $self->allowed_methods;
$c->response->headers(HTTP::Headers->new(
Allow => $allowed_methods->join(', '),
Accept_Patch => 'application/json-patch+json',
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub PUT :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdevicefirmware => $item);
my $recording = $self->get_valid_raw_put_data(
c => $c,
id => $id,
media_type => 'application/octet-stream',
);
last unless $recording;
my $resource = $c->req->query_params;
$resource->{data} = $recording;
my $form = $self->get_form($c);
my $old_resource = $self->resource_from_item($c, $item, $form);
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
last unless $item;
$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, $item, $form);
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->header(Preference_Applied => 'return=representation');
$c->response->body($response->content);
}
}
return;
}
=pod
sub DELETE :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdevicefirmware => $item);
$item->delete;
$guard->commit;
$c->response->status(HTTP_NO_CONTENT);
$c->response->body(q());
}
return;
}
=cut
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
}
# vim: set tabstop=4 expandtab:

@ -25,6 +25,17 @@ class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
{
param => 'reseller_id',
description => 'Filter for models belonging to a certain reseller',
query => {
first => sub {
my $q = shift;
{ reseller_id => $q };
},
second => sub {},
},
},
{
param => 'model',
description => 'Filter for models matching a model name pattern',
@ -146,6 +157,82 @@ sub OPTIONS :Allow {
return;
}
sub POST :Allow {
my ($self, $c) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $resource = $self->get_valid_post_data(
c => $c,
media_type => 'application/json',
);
last unless $resource;
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
);
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$resource->{reseller_id} = $c->user->reseller_id;
}
my $reseller = $c->model('DB')->resultset('resellers')->find($resource->{reseller_id});
unless($reseller) {
$c->log->error("invalid reseller_id '$$resource{reseller_id}', does not exist");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid reseller_id, does not exist");
last;
}
my $item;
$item = $c->model('DB')->resultset('autoprov_devices')->find({
reseller_id => $resource->{reseller_id},
vendor => $resource->{vendor},
model => $resource->{model},
});
if($item) {
$c->log->error("device model with vendor '$$resource{vendor}' and model '$$resource{model}'already exists for reseller_id '$$resource{reseller_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Device model already exists for this reseller");
last;
}
my $linerange = delete $resource->{linerange};
unless(ref $linerange eq "ARRAY") {
$c->log->error("linerange must be array");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid linerange parameter, must be array");
last;
}
try {
$item = $c->model('DB')->resultset('autoprov_devices')->create($resource);
foreach my $range(@{ $linerange }) {
unless(ref $range eq "HASH") {
use Data::Dumper;
$c->log->error("all elements in linerange must be hashes, but this is " . ref $range . ": " . Dumper $range);
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid range definition inside linerange parameter, all must be hash");
return;
}
$item->autoprov_device_line_ranges->create($range);
}
} catch($e) {
$c->log->error("failed to create device model: $e");
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create device model.");
last;
}
$guard->commit;
$c->response->status(HTTP_CREATED);
$c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;

@ -85,6 +85,116 @@ sub OPTIONS :Allow {
return;
}
sub PATCH :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $json = $self->get_valid_patch_data(
c => $c,
id => $id,
media_type => 'application/json-patch+json',
);
last unless $json;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdevicemodel => $item);
my $old_resource = { $item->get_inflated_columns };
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = $self->get_form($c);
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
last unless $item;
$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, $item, $form);
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->header(Preference_Applied => 'return=representation');
$c->response->body($response->content);
}
}
return;
}
sub PUT :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdevicemodel => $item);
my $resource = $self->get_valid_put_data(
c => $c,
id => $id,
media_type => 'application/json',
);
last unless $resource;
my $old_resource = { $item->get_inflated_columns };
my $form = $self->get_form($c);
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
last unless $item;
$guard->commit;
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, $item, $form);
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->header(Preference_Applied => 'return=representation');
$c->response->body($response->content);
}
}
return;
}
=pod
sub DELETE :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, emailtemplate => $item);
foreach(qw/subscriber_email_template_id passreset_email_template_id invoice_email_template_id/){
$c->model('DB')->resultset('contracts')->search({
$_ => $item->id,
})->update({
$_ => undef,
});
}
$item->delete;
$guard->commit;
$c->response->status(HTTP_NO_CONTENT);
$c->response->body(q());
}
return;
}
=cut
sub end : Private {
my ($self, $c) = @_;

@ -135,6 +135,70 @@ sub OPTIONS :Allow {
return;
}
sub POST :Allow {
my ($self, $c) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $resource = $self->get_valid_post_data(
c => $c,
media_type => 'application/json',
);
last unless $resource;
my $form = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
);
my $item;
$item = $c->model('DB')->resultset('autoprov_profiles')->find({
config_id => $resource->{config_id},
name => $resource->{name},
});
if($item) {
$c->log->error("Pbx device profile with name '$$resource{name}' already exists for config_id '$$resource{config_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device profile with this name already exists for this config");
last;
}
my $config_rs = $c->model('DB')->resultset('autoprov_configs')->search({
id => $resource->{config_id},
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$config_rs = $config_rs->search({
'device.reseller_id' => $c->user->reseller_id,
},{
join => 'device',
});
}
my $config = $config_rs->first;
unless($config) {
$c->log->error("Pbx device config with confg_id '$$resource{config_id}' does not exist");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device config does not exist");
last;
}
try {
$item = $config->autoprov_profiles->create($resource);
} catch($e) {
$c->log->error("failed to create pbx device profile: $e"); # TODO: user, message, trace, ...
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create pbx device profile.");
last;
}
$guard->commit;
$c->response->status(HTTP_CREATED);
$c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
$c->response->body(q());
}
return;
}
sub end : Private {
my ($self, $c) = @_;

@ -85,6 +85,116 @@ sub OPTIONS :Allow {
return;
}
sub PATCH :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $json = $self->get_valid_patch_data(
c => $c,
id => $id,
media_type => 'application/json-patch+json',
);
last unless $json;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdeviceprofile => $item);
my $old_resource = { $item->get_inflated_columns };
my $resource = $self->apply_patch($c, $old_resource, $json);
last unless $resource;
my $form = $self->get_form($c);
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
last unless $item;
$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, $item, $form);
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->header(Preference_Applied => 'return=representation');
$c->response->body($response->content);
}
}
return;
}
sub PUT :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $preference = $self->require_preference($c);
last unless $preference;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, pbxdeviceprofile => $item);
my $resource = $self->get_valid_put_data(
c => $c,
id => $id,
media_type => 'application/json',
);
last unless $resource;
my $old_resource = { $item->get_inflated_columns };
my $form = $self->get_form($c);
$item = $self->update_item($c, $item, $old_resource, $resource, $form);
last unless $item;
$guard->commit;
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, $item, $form);
my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new(
$hal->http_headers,
), $hal->as_json);
$c->response->headers($response->headers);
$c->response->header(Preference_Applied => 'return=representation');
$c->response->body($response->content);
}
}
return;
}
=pod
sub DELETE :Allow {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
{
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, emailtemplate => $item);
foreach(qw/subscriber_email_template_id passreset_email_template_id invoice_email_template_id/){
$c->model('DB')->resultset('contracts')->search({
$_ => $item->id,
})->update({
$_ => undef,
});
}
$item->delete;
$guard->commit;
$c->response->status(HTTP_NO_CONTENT);
$c->response->body(q());
}
return;
}
=cut
sub end : Private {
my ($self, $c) = @_;

@ -0,0 +1,51 @@
package NGCP::Panel::Form::Device::ConfigAPI;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
use Moose::Util::TypeConstraints;
use HTML::FormHandler::Widget::Block::Bootstrap;
has '+widget_wrapper' => ( default => 'Bootstrap' );
has_field 'submitid' => ( type => 'Hidden' );
has '+enctype' => ( default => 'multipart/form-data');
sub build_render_list {[qw/submitid fields actions/]}
sub build_form_element_class {[qw(form-horizontal)]}
has_field 'device_id' => (
type => 'PosInteger',
required => 1,
label => 'Device Model',
element_attr => {
rel => ['tooltip'],
title => ['The pbx device model id this config belongs to.']
},
);
has_field 'version' => (
type => 'Text',
required => 1,
label => 'Version',
element_attr => {
rel => ['tooltip'],
title => ['The version number of this config.']
},
);
has_field 'content_type' => (
type => 'Text',
label => 'Filename',
element_attr => {
rel => ['tooltip'],
title => ['The content type this config is served as.']
},
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/device_id version content_type/],
);
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,52 @@
package NGCP::Panel::Form::Device::FirmwareAPI;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
use Moose::Util::TypeConstraints;
use HTML::FormHandler::Widget::Block::Bootstrap;
has '+widget_wrapper' => ( default => 'Bootstrap' );
has_field 'submitid' => ( type => 'Hidden' );
has '+enctype' => ( default => 'multipart/form-data');
sub build_render_list {[qw/submitid fields actions/]}
sub build_form_element_class {[qw(form-horizontal)]}
has_field 'device_id' => (
type => 'PosInteger',
required => 1,
label => 'Device Model',
element_attr => {
rel => ['tooltip'],
title => ['The pbx device model id this firmware belongs to.']
},
);
has_field 'version' => (
type => 'Text',
required => 1,
label => 'Version',
element_attr => {
rel => ['tooltip'],
title => ['The version number of this firmware.']
},
);
has_field 'filename' => (
type => 'Text',
required => 1,
label => 'Filename',
element_attr => {
rel => ['tooltip'],
title => ['The filename of this firmware.']
},
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/device_id version filename/],
);
1;
# vim: set tabstop=4 expandtab:

@ -18,12 +18,20 @@ has_field 'vendor' => (
type => 'Text',
required => 1,
label => 'Vendor',
element_attr => {
rel => ['tooltip'],
title => ['The vendor name of this device.'],
},
);
has_field 'model' => (
type => 'Text',
required => 1,
label => 'Model',
element_attr => {
rel => ['tooltip'],
title => ['The model name of this device.'],
},
);
has_field 'front_image' => (
@ -45,6 +53,10 @@ has_field 'sync_uri' => (
required => 0,
label => 'Bootstrap Sync URI',
default => 'http://[% client.ip %]/admin/resync',
element_attr => {
rel => ['tooltip'],
title => ['The sync URI to set the provisioning server of the device (e.g. http://client.ip/admin/resync. The client.ip variable is automatically expanded during provisioning time.'],
},
);
has_field 'sync_method' => (
@ -56,6 +68,10 @@ has_field 'sync_method' => (
{ label => 'POST', value => 'POST' },
],
default => 'GET',
element_attr => {
rel => ['tooltip'],
title => ['The HTTP method to set the provisioning server (one of GET, POST).'],
},
);
has_field 'sync_params' => (
@ -63,6 +79,10 @@ has_field 'sync_params' => (
required => 0,
label => 'Bootstrap Sync Parameters',
default => '[% server.uri %]/$MA',
element_attr => {
rel => ['tooltip'],
title => ['The parameters appended to the sync URI when setting the provisioning server, e.g. server.uri/$MA. The server.uri variable is automatically expanded during provisioning time.'],
},
);
has_field 'linerange' => (
@ -76,6 +96,10 @@ has_field 'linerange' => (
controls_div => 1,
},
wrapper_class => [qw/hfh-rep-block/],
element_attr => {
rel => ['tooltip'],
title => ['An array of line/key definitions for this device. Each element is a hash containing the keys name, num_lines (defining the number of lines/keys for this range), can_private, can_shared, can_blf.'],
},
);
has_field 'linerange.id' => (

@ -0,0 +1,25 @@
package NGCP::Panel::Form::Device::ModelAPI;
use HTML::FormHandler::Moose;
extends 'NGCP::Panel::Form::Device::ModelAdmin';
use Moose::Util::TypeConstraints;
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/reseller vendor model linerange sync_uri sync_method sync_params/],
);
override 'field_list' => sub {
my $self = shift;
my $c = $self->ctx;
return unless $c;
super();
foreach my $f(qw/front_image mac_image linerange_add/) {
$self->field($f)->inactive(1);
}
};
1;
# vim: set tabstop=4 expandtab:

@ -200,9 +200,18 @@ sub forbid_link_header {
sub valid_media_type {
my ($self, $c, $media_type) = @_;
return 1 if($c->request->header('Content-Type') &&
my $type;
if(ref $media_type eq "ARRAY") {
$type = join ' or ', @{ $media_type };
return 1 if $c->request->header('Content-Type') &&
$c->request->header('Content-Type') ~~ $media_type;
} else {
$type = $media_type;
return 1 if($c->request->header('Content-Type') &&
index($c->request->header('Content-Type'), $media_type) == 0);
$self->error($c, HTTP_UNSUPPORTED_MEDIA_TYPE, "Unsupported media type, accepting '$media_type' only.");
}
$self->error($c, HTTP_UNSUPPORTED_MEDIA_TYPE, "Unsupported media type, accepting $type only.");
return;
}

@ -11,18 +11,33 @@ use TryCatch;
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use JSON::Types;
use NGCP::Panel::Form::Device::Config;
use NGCP::Panel::Form::Device::ConfigAPI;
sub item_rs {
my ($self, $c) = @_;
my $item_rs = $c->model('DB')->resultset('autoprov_configs');
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({
'device.reseller_id' => $c->user->reseller_id
},{
join => 'device',
});
}
return $item_rs;
}
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Device::Config->new;
return NGCP::Panel::Form::Device::ConfigAPI->new(ctx => $c);
}
sub hal_from_item {
my ($self, $c, $item) = @_;
my $form;
my $type = 'pbxdeviceconfigs';
my ($self, $c, $item, $form) = @_;
$form //= $self->get_form($c);
my $resource = $self->resource_from_item($c, $item, $form);
my $hal = Data::HAL->new(
links => [
@ -32,56 +47,80 @@ sub hal_from_item {
name => 'ngcp',
templated => true,
),
Data::HAL::Link->new(relation => 'collection', href => sprintf("%s", $self->dispatch_path)),
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:$type", href => sprintf("/api/%s/%d", $type, $item->id)),
Data::HAL::Link->new(relation => 'ngcp:pbxdevicemodels', href => sprintf("/api/pbxdevicemodels/%d", $item->device_id)),
Data::HAL::Link->new(relation => 'ngcp:pbxdeviceconfigfiles', href => sprintf("/api/pbxdeviceconfigfiles/%d", $item->id)),
],
relation => 'ngcp:'.$self->resource_name,
);
my $resource = $self->resource_from_item($c, $item);
$self->validate_form(
c => $c,
resource => $resource,
form => $form,
run => 0,
exceptions => [qw/device_id/],
);
$resource->{id} = int($item->id);
delete $resource->{data};
$hal->resource($resource);
return $hal;
}
sub resource_from_item {
my ($self, $c, $item) = @_;
my ($self, $c, $item, $form) = @_;
my $resource = { $item->get_inflated_columns };
delete $resource->{data};
my %resource = $item->get_inflated_columns;
delete $resource{device_id};
return $resource;
}
my $form = $self->get_form($c);
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 $binary = delete $resource->{data};
$resource->{content_type} = $c->request->header('Content-Type');
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
resource => \%resource,
run => 0,
resource => $resource,
exceptions => [qw/device_id/],
);
$resource{id} = int($item->id);
return \%resource;
}
sub item_rs {
my ($self, $c) = @_;
my $item_rs = $c->model('DB')->resultset('autoprov_configs');
my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
id => $resource->{device_id}
});
if($c->user->roles eq "admin") {
} elsif ($c->user->roles eq "reseller") {
$item_rs = $item_rs->search(
{ 'device.reseller_id' => $c->user->reseller_id, },
{ prefetch => 'device', });
} elsif($c->user->roles eq "reseller") {
$model_rs = $model_rs->search({
reseller_id => $c->user->reseller_id,
});
}
my $model = $model_rs->first;
unless($model) {
$c->log->error("invalid device_id '$$resource{device_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
last;
}
return $item_rs;
}
$resource->{data} = $binary;
sub item_by_id {
my ($self, $c, $id) = @_;
$item->update($resource);
my $item_rs = $self->item_rs($c);
return $item_rs->find($id);
return $item;
}
1;

@ -0,0 +1,125 @@
package NGCP::Panel::Role::API::PbxDeviceFirmwares;
use Moose::Role;
use Sipwise::Base;
with 'NGCP::Panel::Role::API' => {
-alias =>{ item_rs => '_item_rs', },
-excludes => [ 'item_rs' ],
};
use boolean qw(true);
use TryCatch;
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Form::Device::FirmwareAPI;
sub item_rs {
my ($self, $c) = @_;
my $item_rs = $c->model('DB')->resultset('autoprov_firmwares');
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({
'device.reseller_id' => $c->user->reseller_id
},{
join => 'device',
});
}
return $item_rs;
}
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Device::FirmwareAPI->new(ctx => $c);
}
sub hal_from_item {
my ($self, $c, $item, $form) = @_;
$form //= $self->get_form($c);
my $resource = $self->resource_from_item($c, $item, $form);
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:pbxdevicemodels', href => sprintf("/api/pbxdevicemodels/%d", $item->device_id)),
Data::HAL::Link->new(relation => 'ngcp:pbxdevicefirmwarebinaries', href => sprintf("/api/pbxdevicefirmwarebinaries/%d", $item->id)),
],
relation => 'ngcp:'.$self->resource_name,
);
$self->validate_form(
c => $c,
resource => $resource,
form => $form,
run => 0,
exceptions => [qw/device_id/],
);
$resource->{id} = int($item->id);
delete $resource->{data};
$hal->resource($resource);
return $hal;
}
sub resource_from_item {
my ($self, $c, $item, $form) = @_;
my $resource = { $item->get_inflated_columns };
delete $resource->{data};
return $resource;
}
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 $binary = delete $resource->{data};
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
resource => $resource,
exceptions => [qw/device_id/],
);
my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
id => $resource->{device_id}
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$model_rs = $model_rs->search({
reseller_id => $c->user->reseller_id,
});
}
my $model = $model_rs->first;
unless($model) {
$c->log->error("invalid device_id '$$resource{device_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
last;
}
$resource->{data} = $binary;
$item->update($resource);
return $item;
}
1;
# vim: set tabstop=4 expandtab:

@ -12,17 +12,17 @@ use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use JSON::Types;
use NGCP::Panel::Form::Device::Model;
use NGCP::Panel::Form::Device::ModelAPI;
sub get_form {
my ($self, $c) = @_;
return NGCP::Panel::Form::Device::Model->new;
return NGCP::Panel::Form::Device::ModelAPI->new($c);
}
sub hal_from_item {
my ($self, $c, $item) = @_;
my $form;
my $type = 'pbxdevicemodels';
#my $type = 'pbxdevicemodels';
my $hal = Data::HAL->new(
links => [
@ -35,7 +35,7 @@ sub hal_from_item {
Data::HAL::Link->new(relation => 'collection', href => sprintf("%s", $self->dispatch_path)),
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:$type", href => sprintf("/api/%s/%d", $type, $item->id)),
Data::HAL::Link->new(relation => "ngcp:pbxdevicefirmwares", href => sprintf("/api/pbxdevicefirmwares/?device_id=%d", $item->id)),
],
relation => 'ngcp:'.$self->resource_name,
);
@ -62,6 +62,19 @@ sub resource_from_item {
$resource{reseller_id} = int($item->reseller_id);
$resource{id} = int($item->id);
$resource{linerange} = [];
foreach my $range($item->autoprov_device_line_ranges->all) {
my $r = { $range->get_inflated_columns };
foreach my $f(qw/device_id/) {
delete $r->{$f};
}
$r->{id} = int($r->{id});
foreach my $f(qw/can_private can_shared can_blf/) {
$r->{$f} = JSON::Types::bool($r->{$f});
}
push @{ $resource{linerange} }, $r;
}
return \%resource;
}
@ -83,5 +96,88 @@ sub item_by_id {
return $item_rs->find($id);
}
sub update_item {
my ($self, $c, $item, $old_resource, $resource, $form) = @_;
$form //= $self->get_form($c);
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$resource->{reseller_id} = $c->user->reseller_id;
}
my $reseller = $c->model('DB')->resultset('resellers')->find($resource->{reseller_id});
unless($reseller) {
$c->log->error("invalid reseller_id '$$resource{reseller_id}', does not exist");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid reseller_id, does not exist");
return;
}
my $dup_item;
$dup_item = $c->model('DB')->resultset('autoprov_devices')->find({
reseller_id => $resource->{reseller_id},
vendor => $resource->{vendor},
model => $resource->{model},
});
if($dup_item && $dup_item->id != $item->id) {
$c->log->error("device model with vendor '$$resource{vendor}' and model '$$resource{model}'already exists for reseller_id '$$resource{reseller_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Device model already exists for this reseller");
return;
}
my $linerange = delete $resource->{linerange};
unless(ref $linerange eq "ARRAY") {
$c->log->error("linerange must be array");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid linerange parameter, must be array");
return;
}
$item->update($resource);
my @existing_range = ();
my $range_rs = $item->autoprov_device_line_ranges;
foreach my $range(@{ $linerange }) {
next unless(defined $range);
my $old_range;
if(defined $range->{id}) {
# should be an existing range, do update
$old_range = $range_rs->find($range->{id});
delete $range->{id};
unless($old_range) {
$old_range = $range_rs->create($range);
} else {
# formhandler only passes set check-boxes, so explicitely unset here
$range->{can_private} //= 0;
$range->{can_shared} //= 0;
$range->{can_blf} //= 0;
$old_range->update($range);
}
} else {
# new range
$old_range = $range_rs->create($range);
}
push @existing_range, $old_range->id; # mark as valid (delete others later)
# delete field device line assignments with are out-of-range or use a
# feature which is not supported anymore after edit
foreach my $fielddev_line($c->model('DB')->resultset('autoprov_field_device_lines')
->search({ linerange_id => $old_range->id })->all) {
if($fielddev_line->key_num >= $old_range->num_lines ||
($fielddev_line->line_type eq 'private' && !$old_range->can_private) ||
($fielddev_line->line_type eq 'shared' && !$old_range->can_shared) ||
($fielddev_line->line_type eq 'blf' && !$old_range->can_blf)) {
$fielddev_line->delete;
}
}
}
# delete invalid range ids (e.g. removed ones)
$range_rs->search({
id => { 'not in' => \@existing_range },
})->delete_all;
return $item;
}
1;
# vim: set tabstop=4 expandtab:

@ -61,6 +61,7 @@ sub resource_from_item {
);
$resource{id} = int($item->id);
$resource{config_id} = int($item->config_id);
return \%resource;
}
@ -84,5 +85,41 @@ sub item_by_id {
return $item_rs->find($id);
}
sub update_item {
my ($self, $c, $item, $old_resource, $resource, $form) = @_;
my $dup_item = $c->model('DB')->resultset('autoprov_profiles')->find({
config_id => $resource->{config_id},
name => $resource->{name},
});
if($dup_item && $dup_item->id != $item->id) {
$c->log->error("Pbx device profile with name '$$resource{name}' already exists for config_id '$$resource{config_id}'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device profile with this name already exists for this config");
last;
}
my $config_rs = $c->model('DB')->resultset('autoprov_configs')->search({
id => $resource->{config_id},
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$config_rs = $config_rs->search({
'device.reseller_id' => $c->user->reseller_id,
},{
join => 'device',
});
}
my $config = $config_rs->first;
unless($config) {
$c->log->error("Pbx device config with confg_id '$$resource{config_id}' does not exist");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device config does not exist");
last;
}
$item->update($resource);
return $item;
}
1;
# vim: set tabstop=4 expandtab:

Loading…
Cancel
Save