diff --git a/lib/NGCP/Panel/Controller/API/Contacts.pm b/lib/NGCP/Panel/Controller/API/Contacts.pm index a2832bb718..f7d9db9dee 100644 --- a/lib/NGCP/Panel/Controller/API/Contacts.pm +++ b/lib/NGCP/Panel/Controller/API/Contacts.pm @@ -5,9 +5,6 @@ use boolean qw(true); use Data::HAL qw(); use Data::HAL::Link qw(); use Data::Record qw(); -use DateTime::Format::HTTP qw(); -use DateTime::Format::RFC3339 qw(); -use Digest::SHA3 qw(sha3_256_base64); use HTTP::Headers qw(); use HTTP::Headers::Util qw(split_header_words); use HTTP::Status qw(:constants); @@ -15,11 +12,8 @@ use HTTP::Status qw(:constants); use MooseX::ClassAttribute qw(class_has); use NGCP::Panel::Form::Contact::Admin qw(); use NGCP::Panel::Form::Contact::Reseller qw(); -use NGCP::Panel::ValidateJSON qw(); use Path::Tiny qw(path); -use Regexp::Common qw(delimited); # $RE{delimited} use Safe::Isa qw($_isa); -use Types::Standard qw(InstanceOf); BEGIN { extends 'Catalyst::Controller::ActionRole'; } require Catalyst::ActionRole::ACL; require Catalyst::ActionRole::CheckTrailingSlash; @@ -32,7 +26,6 @@ with 'NGCP::Panel::Role::API'; class_has('dispatch_path', is => 'ro', default => '/api/contacts/'); class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-contacts'); -has('last_modified', is => 'rw', isa => InstanceOf['DateTime']); __PACKAGE__->config( action => { @@ -170,42 +163,6 @@ sub allowed_methods : Private { return [sort @allow]; } -sub cached : Private { - my ($self, $c) = @_; - my $response = $c->cache->get($c->request->uri->canonical->as_string); - unless ($response) { - $c->log->info('not cached'); - return; - } - my $matched_tag = $c->request->header('If-None-Match') && ('*' eq $c->request->header('If-None-Match')) - || (grep {$response->header('ETag') eq $_} Data::Record->new({ - split => qr/\s*,\s*/, unless => $RE{delimited}{-delim => q(")}, - })->records($c->request->header('If-None-Match'))); - my $not_modified = $c->request->header('If-Modified-Since') - && !($self->last_modified < DateTime::Format::HTTP->parse_datetime($c->request->header('If-Modified-Since'))); - if ( - $matched_tag && $not_modified - || $matched_tag - || $not_modified - ) { - $c->response->status(HTTP_NOT_MODIFIED); - $c->response->headers($response->headers); - $c->log->info('cached'); - return 1; - } - $c->log->info('stale'); - return; -} - -sub etag : Private { - my ($self, $octets) = @_; - return sprintf '"ni:/sha3-256;%s"', sha3_256_base64($octets); -} - -sub expires : Private { - my ($self) = @_; - return DateTime->now->clone->add(years => 1); # XXX insert product end-of-life -} sub hal_from_contact : Private { my ($self, $contact) = @_; diff --git a/lib/NGCP/Panel/Controller/API/ContactsItem.pm b/lib/NGCP/Panel/Controller/API/ContactsItem.pm index 48d60c976a..32e0cd5f07 100644 --- a/lib/NGCP/Panel/Controller/API/ContactsItem.pm +++ b/lib/NGCP/Panel/Controller/API/ContactsItem.pm @@ -26,7 +26,7 @@ use JSON qw(); use JSON::Pointer qw(); use MooseX::ClassAttribute qw(class_has); use NGCP::Panel::Form::Contact::Reseller qw(); -use NGCP::Panel::ValidateJSON qw(); +use NGCP::Panel::Utils::ValidateJSON qw(); use Path::Tiny qw(path); use Regexp::Common qw(delimited); # $RE{delimited} use Safe::Isa qw($_isa); @@ -128,7 +128,7 @@ sub PATCH : Allow { }; last unless $self->valid_precondition($c, $cached->header('ETag'), 'contact'); try { - NGCP::Panel::ValidateJSON->new($cached->content); + NGCP::Panel::Utils::ValidateJSON->new($cached->content); $entity = JSON::decode_json($cached->content); } catch($e) { die "cache poisoned: $e"; @@ -256,7 +256,7 @@ sub PUT : Allow { }; last unless $self->valid_precondition($c, $cached->header('ETag'), 'contact'); try { - NGCP::Panel::ValidateJSON->new($cached->content); + NGCP::Panel::Utils::ValidateJSON->new($cached->content); $entity = JSON::decode_json($cached->content); } catch($e) { die "cache poisoned: $e"; @@ -550,7 +550,7 @@ sub require_valid_patch : Private { sub require_wellformed_json : Private { my ($self, $c, $media_type, $patch) = @_; try { - NGCP::Panel::ValidateJSON->new($patch); + NGCP::Panel::Utils::ValidateJSON->new($patch); } catch($e) { $c->response->status(HTTP_BAD_REQUEST); $c->response->header('Content-Language' => 'en'); diff --git a/lib/NGCP/Panel/Controller/API/Contracts.pm b/lib/NGCP/Panel/Controller/API/Contracts.pm index baa1972938..588c0891b3 100644 --- a/lib/NGCP/Panel/Controller/API/Contracts.pm +++ b/lib/NGCP/Panel/Controller/API/Contracts.pm @@ -22,7 +22,7 @@ use JE qw(); use JSON qw(); use MooseX::ClassAttribute qw(class_has); use NGCP::Panel::Form::Contact::Reseller qw(); -use NGCP::Panel::ValidateJSON qw(); +use NGCP::Panel::Utils::ValidateJSON qw(); use Path::Tiny qw(path); use Regexp::Common qw(delimited); # $RE{delimited} use Safe::Isa qw($_isa); @@ -286,7 +286,7 @@ sub require_body : Private { sub require_wellformed_json : Private { my ($self, $c, $media_type, $patch) = @_; try { - NGCP::Panel::ValidateJSON->new($patch); + NGCP::Panel::Utils::ValidateJSON->new($patch); } catch($e) { $c->response->status(HTTP_BAD_REQUEST); $c->response->header('Content-Language' => 'en'); diff --git a/lib/NGCP/Panel/Controller/API/ContractsItem.pm b/lib/NGCP/Panel/Controller/API/ContractsItem.pm index 0ac3ea45dd..f8ed7667b3 100644 --- a/lib/NGCP/Panel/Controller/API/ContractsItem.pm +++ b/lib/NGCP/Panel/Controller/API/ContractsItem.pm @@ -25,7 +25,7 @@ use JE qw(); use JSON qw(); use JSON::Pointer qw(); use MooseX::ClassAttribute qw(class_has); -use NGCP::Panel::ValidateJSON qw(); +use NGCP::Panel::Utils::ValidateJSON qw(); use Path::Tiny qw(path); use Regexp::Common qw(delimited); # $RE{delimited} use Safe::Isa qw($_isa); @@ -127,7 +127,7 @@ sub PATCH : Allow { }; last unless $self->valid_precondition($c, $cached->header('ETag'), 'contract'); try { - NGCP::Panel::ValidateJSON->new($cached->content); + NGCP::Panel::Utils::ValidateJSON->new($cached->content); $entity = JSON::decode_json($cached->content); } catch($e) { die "cache poisoned: $e"; @@ -240,7 +240,7 @@ sub PUT : Allow { }; last unless $self->valid_precondition($c, $cached->header('ETag'), 'contract'); try { - NGCP::Panel::ValidateJSON->new($cached->content); + NGCP::Panel::Utils::ValidateJSON->new($cached->content); $entity = JSON::decode_json($cached->content); } catch($e) { die "cache poisoned: $e"; @@ -518,7 +518,7 @@ sub require_valid_patch : Private { sub require_wellformed_json : Private { my ($self, $c, $media_type, $patch) = @_; try { - NGCP::Panel::ValidateJSON->new($patch); + NGCP::Panel::Utils::ValidateJSON->new($patch); } catch($e) { $c->response->status(HTTP_BAD_REQUEST); $c->response->header('Content-Language' => 'en'); diff --git a/lib/NGCP/Panel/Role/API.pm b/lib/NGCP/Panel/Role/API.pm index 351ffc8a27..a68aaa13e0 100644 --- a/lib/NGCP/Panel/Role/API.pm +++ b/lib/NGCP/Panel/Role/API.pm @@ -6,6 +6,14 @@ use JSON qw(); use HTTP::Status qw(:constants); use Safe::Isa qw($_isa); use Try::Tiny; +use Digest::SHA3 qw(sha3_256_base64); +use DateTime::Format::HTTP qw(); +use DateTime::Format::RFC3339 qw(); +use Types::Standard qw(InstanceOf); +use Regexp::Common qw(delimited); # $RE{delimited} +use NGCP::Panel::Utils::ValidateJSON qw(); + +has('last_modified', is => 'rw', isa => InstanceOf['DateTime']); sub get_valid_post_data { my ($self, %params) = @_; @@ -108,7 +116,7 @@ sub require_body { sub require_wellformed_json { my ($self, $c, $media_type, $patch) = @_; try { - NGCP::Panel::ValidateJSON->new($patch); + NGCP::Panel::Utils::ValidateJSON->new($patch); } catch { $self->error($c, HTTP_BAD_REQUEST, "The entity is not a well-formed '$media_type' document. $_"); return; @@ -116,7 +124,42 @@ sub require_wellformed_json { return 1; } +sub cached { + my ($self, $c) = @_; + my $response = $c->cache->get($c->request->uri->canonical->as_string); + unless ($response) { + $c->log->info('not cached'); + return; + } + my $matched_tag = $c->request->header('If-None-Match') && ('*' eq $c->request->header('If-None-Match')) + || (grep {$response->header('ETag') eq $_} Data::Record->new({ + split => qr/\s*,\s*/, unless => $RE{delimited}{-delim => q(")}, + })->records($c->request->header('If-None-Match'))); + my $not_modified = $c->request->header('If-Modified-Since') + && !($self->last_modified < DateTime::Format::HTTP->parse_datetime($c->request->header('If-Modified-Since'))); + if ( + $matched_tag && $not_modified + || $matched_tag + || $not_modified + ) { + $c->response->status(HTTP_NOT_MODIFIED); + $c->response->headers($response->headers); + $c->log->info('cached'); + return 1; + } + $c->log->info('stale'); + return; +} + +sub etag { + my ($self, $octets) = @_; + return sprintf '"ni:/sha3-256;%s"', sha3_256_base64($octets); +} +sub expires { + my ($self) = @_; + return DateTime->now->clone->add(years => 1); # XXX insert product end-of-life +} 1; # vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/ValidateJSON.pm b/lib/NGCP/Panel/Utils/ValidateJSON.pm similarity index 95% rename from lib/NGCP/Panel/ValidateJSON.pm rename to lib/NGCP/Panel/Utils/ValidateJSON.pm index 8328c58b11..3883d0856c 100644 --- a/lib/NGCP/Panel/ValidateJSON.pm +++ b/lib/NGCP/Panel/Utils/ValidateJSON.pm @@ -1,4 +1,4 @@ -package NGCP::Panel::ValidateJSON; +package NGCP::Panel::Utils::ValidateJSON; use Sipwise::Base; extends 'JSON::Tiny::Subclassable';