MT#17173 Adapt common API method for not DB entities

And add some IoC methods

Change-Id: I165824e682c1d0a5ae34f5d2da4b9f2fbd040555
changes/85/8885/4
Irina Peshinskaya 10 years ago
parent f288ec2563
commit ad208784a1

@ -375,7 +375,13 @@ sub require_valid_patch {
return 1;
}
sub item_by_id_valid {
my ($self, $c, $id) = @_;
return unless $self->valid_id($c, $id);
my $item = $self->item_by_id($c, $id);
return unless $self->resource_exists($c, $self->item_name => $item);
return $item;
}
sub resource_exists {
my ($self, $c, $entity_name, $resource) = @_;
return 1 if $resource;
@ -384,12 +390,41 @@ sub resource_exists {
}
sub paginate_order_collection {
my ($self, $c, $item_rs) = @_;
my ($self, $c, $items) = @_;
my $params = {
page => $c->request->params->{page} // 1,
rows => $c->request->params->{rows} // 10,
order_by => $c->request->params->{order_by},
direction => $c->request->params->{order_by_direction} // "asc",
};
my($total_count, $item_rs);
if('ARRAY' eq ref $items){
($total_count, $item_rs) = $self->paginate_order_collection_array($c, $items, $params);
}else{
($total_count, $item_rs) = $self->paginate_order_collection_rs($c, $items, $params);
}
return ($total_count, $item_rs);
}
sub paginate_order_collection_array {
my ($self, $c, $items, $params) = @_;
my($page,$rows,$order_by,$direction) = @$params{qw/page rows order_by direction/};
my $total_count = scalar @$items;
if(defined $order_by ){
if(defined $order_by && defined $direction && (lc($direction) eq 'desc') ){
$items = [sort { $b->{$order_by} cmp $a->{$order_by} } @$items];
}else{
$items = [sort { $a->{$order_by} cmp $b->{$order_by} } @$items];
}
}
$items = [splice(@$items, ( $page - 1 )*$rows, $rows) ];
return ($total_count, $items);
}
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 $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
my $order_by = $c->request->params->{order_by};
my $direction = $c->request->params->{order_by_direction} // "asc";
my $total_count = int($item_rs->count);
$item_rs = $item_rs->search(undef, {
page => $page,
@ -596,7 +631,9 @@ sub apply_query_params {
foreach my $param(keys %{ $c->req->query_params }) {
my @p = grep { $_->{param} eq $param } @{ $query_params };
next unless($p[0]->{query} || $p[0]->{new_rs}); # skip "dummy" query parameters
#todo: we can generate default filters for all item_rs fields here
#the only reason not to do this is a security
next unless($p[0]->{query} || $p[0]->{query_type} || $p[0]->{new_rs}); # skip "dummy" query parameters
my $q = $c->req->query_params->{$param}; # TODO: arrayref?
$q =~ s/\*/\%/g;
$q = undef if $q eq "NULL"; # IS NULL translation
@ -604,7 +641,7 @@ sub apply_query_params {
if (defined $p[0]->{new_rs}) {
#compose fresh rs based on current, to support set operations with filters:
$item_rs = $p[0]->{new_rs}($c,$q,$item_rs);
} elsif (defined $p[0]->{query}) {
} elsif (defined $p[0]->{query} || defined $p[0]->{query_type}) {
#regular chaining:
my($sub_where,$sub_attributes) = $self->get_query_callbacks(\@p);
$item_rs = $item_rs->search($sub_where->($q,$c), $sub_attributes->($q,$c));
@ -649,9 +686,14 @@ sub delay_commit {
$guard->commit();
}
#---------------- Entities staff
#---------------- default methods
sub hal_from_item {
my ($self, $c, $item, $form) = @_;
my $resource = {$item->get_inflated_columns};
my ($self, $c, $item, $form, $params) = @_;
my ($form_exceptions) = @$params{qw/form_exceptions/};
my $resource = $self->resource_from_item($c, $item, $form);
$resource = $self->process_hal_resource($c, $item, $resource, $form);
my $links = $self->hal_links($c, $item, $resource, $form) // [];
my $hal = Data::HAL->new(
@ -664,71 +706,151 @@ sub hal_from_item {
),
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 => 'self', href => sprintf("%s%s", $self->dispatch_path, $self->get_item_id($c, $item))),
@$links
],
relation => 'ngcp:'.$self->resource_name,
);
if(!$form){
($form,$form_exceptions) = $self->get_form($c);
}
if($form){
$self->validate_form(
c => $c,
resource => $resource,
form => $form,
run => 0,
);
}
$resource->{id} = $self->get_item_id($c, $item);
$hal->resource({%$resource});
return $hal;
}
$form //= $self->get_form($c);
sub update_item {
my ($self, $c, $item, $old_resource, $resource, $form, $params) = @_;
my ($form_exceptions, $process_extras);
($form, $form_exceptions, $process_extras) = @{$params}{qw/form form_exceptions process_extras/};
$self->validate_form(
c => $c,
resource => $resource,
form => $form,
run => 0,
);
if(!$form){
($form, $form_exceptions) = $self->get_form($c);
}
if($form){
return unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
$form_exceptions ? (exceptions => $form_exceptions) : (),
);
return unless $resource;
}
$old_resource //= $self->resource_from_item($c, $item, $form);
$process_extras //= {};
return unless $self->process_form_resource($c, $item, $old_resource, $resource, $form, $process_extras);
return unless $resource;
return unless $self->check_duplicate($c, $item, $old_resource, $resource, $form, $process_extras);
return unless $self->check_resource($c, $item, $old_resource, $resource, $form, $process_extras);
$item = $self->update_item_model($c, $item, $old_resource, $resource, $form, $process_extras);
return $item, $form;
$resource->{id} = int($item->id);
$hal->resource({%$resource});
return $hal;
}
sub hal_links {
my($self, $c, $item, $resource, $form) = @_;
return [];
#------ dummy & default methods
sub query_params {
return [
];
}
sub item_by_id {
my ($self, $c, $id) = @_;
my $item_rs = $self->item_rs($c);
return $item_rs->find($id);
sub _set_config{
return {};
}
sub update_item {
my ($self, $c, $item, $old_resource, $resource, $form) = @_;
sub check_duplicate{
my($self, $c, $item, $old_resource, $resource, $form) = @_;
return 1;
}
$form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
resource => $resource,
);
last unless $resource;
$resource = $self->process_form_resource($c, $item, $old_resource, $resource, $form);
last unless $resource;
last unless $self->check_duplicate($c, $item, $old_resource, $resource, $form);
$item = $self->update($c, $item, $old_resource, $resource, $form);
return $item;
sub check_resource{
my($self, $c, $item, $old_resource, $resource, $form) = @_;
return 1;
}
#process_form_resource - added as method for custom preparation form data,like:
# my $ft = File::Type->new();
# my $content_type = $ft->mime_type(${$process_extras->{binary_ref}});
# if($type eq 'mac') {
# $resource->{mac_image} = ${$process_extras->{binary_ref}};
# $resource->{mac_image_type} = $content_type;
# } else {
# $resource->{front_image} = ${$process_extras->{binary_ref}};
# $resource->{front_image_type} = $content_type;
# }
#
#etc. Method still can be used as exit point, if form data processing can be performed due to incorrect input data
#used in update_item
sub process_form_resource {
my($self, $c, $item, $old_resource, $resource, $form) = @_;
return $resource;
}
#process_hal_resource is rarely used method, which intnded to transform somehow db resource data to the hal we want
#something like:
#$resource{contract_id} = delete $resource{peering_contract_id};
#used at least in hal_from_item
sub process_hal_resource {
my($self, $c, $item, $resource, $form) = @_;
return $resource;
}
sub update {
my($self, $c, $item, $old_resource, $resource, $form) = @_;
sub hal_links {
my($self, $c, $item, $resource, $form) = @_;
return [];
}
sub get_form {
my($self, $c) = @_;
return ;
}
sub get_item_id{
my($self, $c, $item, $resource, $form) = @_;
return int($item->id);
}
sub item_by_id {
my ($self, $c, $id) = @_;
my $item_rs = $self->item_rs($c);
return $item_rs->find($id);
}
sub resource_from_item{
my($self, $c, $item) = @_;
my $res;
if('HASH' eq ref $item){
$res = $item;
}else{
$res = { $item->get_inflated_columns };
}
return $res;
}
sub update_item_model{
my($self, $c, $item, $old_resource, $resource, $form, $process_extras) = @_;
$item->update($resource);
return $item;
}
#------ accessors ---
#------ accessors ---
sub dispatch_path {
my $self = shift;
return '/api/'.$self->resource_name.'/';
@ -738,8 +860,10 @@ sub relation {
my $self = shift;
return 'http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name;
}
#------ /accessors ---
sub return_csv(){
my($self,$c) = @_;
try{
@ -751,8 +875,9 @@ sub return_csv(){
$self->create_csv($c);
$c->response->body(q());
}catch($e){
$self->error($c, HTTP_BAD_REQUEST, $e);
$self->error($c, HTTP_BAD_REQUEST, $e);
}
}
}
1;
# vim: set tabstop=4 expandtab:

@ -8,6 +8,12 @@ use HTTP::Status qw(:constants);
use Data::HAL qw();
use Data::HAL::Link qw();
#use Path::Tiny qw(path);
#use TryCatch;
#require Catalyst::ActionRole::ACL;
#require Catalyst::ActionRole::CheckTrailingSlash;
#require Catalyst::ActionRole::HTTPMethods;
#require Catalyst::ActionRole::RequireSSL;
sub set_config {
my $self = shift;
@ -20,17 +26,17 @@ sub set_config {
Does => [qw(ACL CheckTrailingSlash RequireSSL)],
Method => $_,
Path => $self->dispatch_path,
%{$self->_set_config($_)},
} } @{ $self->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
%{$self->_set_config()},
);
}
sub auto :Private {
sub get_list{
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
return $self->item_rs($c);
}
sub get {
@ -38,15 +44,16 @@ sub get {
my $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
{
my $items = $self->item_rs($c);
my $items = $self->get_list($c);
(my $total_count, $items) = $self->paginate_order_collection($c, $items);
my (@embedded, @links);
my $form = $self->get_form($c);
for my $item ($items->all) {
my @items = 'ARRAY' eq ref $items ? @$items : $items->all;
for my $item (@items) {
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),
href => sprintf('/%s%s', $c->request->path, $self->get_item_id($c, $item)),
);
}
push @links,
@ -81,25 +88,6 @@ sub get {
return;
}
sub head {
my ($self, $c) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub options {
my ($self, $c) = @_;
my $allowed_methods = $self->allowed_methods_filtered($c);
$c->response->headers(HTTP::Headers->new(
Allow => join(', ', @{ $allowed_methods }),
Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name,
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub post {
my ($self) = shift;
my ($c) = @_;
@ -107,27 +95,55 @@ sub post {
{
my $resource = $self->get_valid_post_data(
c => $c,
media_type => 'application/json',
media_type => $self->config->{action}->{OPTIONS}->{POST}->{ContentType} // 'application/json',
);
last unless $resource;
my $form = $self->get_form($c);
my ($form, $exceptions) = $self->get_form($c);
last unless $self->validate_form(
c => $c,
resource => $resource,
form => $form,
$exceptions ? (exceptions => $exceptions) : (),
);
$resource = $self->process_form_resource($c, undef, undef, $resource, $form);
my $process_extras= {};
last unless $self->process_form_resource($c, undef, undef, $resource, $form, $process_extras);
last unless $resource;
last unless $self->check_duplicate($c, undef, undef, $resource, $form);
last unless $self->check_duplicate($c, undef, undef, $resource, $form, $process_extras);
last unless $self->check_resource($c, undef, undef, $resource, $form, $process_extras);
my $item = $self->create_item($c, $resource, $form);
my $item = $self->create_item($c, $resource, $form, $process_extras);
$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 auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub head {
my ($self, $c) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub options {
my ($self, $c) = @_;
my $allowed_methods = $self->allowed_methods_filtered($c);
$c->response->headers(HTTP::Headers->new(
Allow => join(', ', @{ $allowed_methods }),
Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name,
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}

@ -24,25 +24,20 @@ sub set_config {
Does => [qw(ACL RequireSSL)],
Method => $_,
Path => $self->dispatch_path,
%{$self->_set_config($_)},
} } @{ $self->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
%{$self->_set_config()},
);
}
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub get {
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, $self->item_name => $item);
my $item = $self->item_by_id_valid($c, $id);
last unless $item;
my $hal = $self->hal_from_item($c, $item);
@ -60,25 +55,6 @@ sub get {
return;
}
sub head {
my ($self, $c, $id) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub options {
my ($self, $c, $id) = @_;
my $allowed_methods = $self->allowed_methods_filtered($c);
$c->response->headers(HTTP::Headers->new(
Allow => join(', ', @{ $allowed_methods }),
Accept_Patch => 'application/json-patch+json',
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub patch {
my ($self, $c, $id) = @_;
my $guard = $c->model('DB')->txn_scope_guard;
@ -93,14 +69,14 @@ sub patch {
);
last unless $json;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, $self->item_name => $item);
my $item = $self->item_by_id_valid($c, $id);
last unless $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);
my $form;
($item,$form) = $self->update_item($c, $item, $old_resource, $resource, $form);
last unless $item;
$guard->commit;
@ -129,18 +105,19 @@ sub put {
my $preference = $self->require_preference($c);
last unless $preference;
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, $self->item_name => $item);
my $item = $self->item_by_id_valid($c, $id);
last unless $item;
my $resource = $self->get_valid_put_data(
c => $c,
id => $id,
media_type => 'application/json',
media_type => $self->config->{action}->{OPTIONS}->{PUT}->{ContentType} // '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);
my $form;
($item, $form) = $self->update_item($c, $item, $old_resource, $resource, $form );
last unless $item;
$guard->commit;
@ -162,13 +139,15 @@ sub put {
return;
}
sub delete {
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, $self->item_name => $item);
my $item = $self->item_by_id_valid($c, $id);
last unless $item;
$self->delete_item($c, $item );
$guard->commit;
@ -177,6 +156,30 @@ sub delete {
}
return;
}
sub auto :Private {
my ($self, $c) = @_;
$self->set_body($c);
$self->log_request($c);
}
sub head {
my ($self, $c, $id) = @_;
$c->forward(qw(GET));
$c->response->body(q());
return;
}
sub options {
my ($self, $c, $id) = @_;
my $allowed_methods = $self->allowed_methods_filtered($c);
$c->response->headers(HTTP::Headers->new(
Allow => join(', ', @{ $allowed_methods }),
Accept_Patch => 'application/json-patch+json',
));
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
return;
}
sub end :Private {
my ($self, $c) = @_;

Loading…
Cancel
Save