From: nperez Date: Mon, 1 Mar 2010 02:11:42 +0000 (-0600) Subject: Merge in object_split functionality X-Git-Tag: 2.002001~25 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Controller-DBIC-API.git;a=commitdiff_plain;h=609916e5de6201b2e2212328153657194b41a730 Merge in object_split functionality --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b18583d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.swp +*.tar.gz +Catalyst-Controller-DBIC-API* diff --git a/Changes b/Changes index 2ab8c02..0c16b47 100644 --- a/Changes +++ b/Changes @@ -4,6 +4,8 @@ Revision history for Catalyst-Controller-DBIC-API: {{ $dist->version }} - Remove debugging code from tests - Fixed some typos and code cleanups +- Added possibility to fetch a single object by id + tests using 'item' +- Added item_root attribute which defaults to 'data' and is used as data root for 'item' 2.001003 2010-02-12 19:01:56 America/Chicago diff --git a/lib/Catalyst/Controller/DBIC/API.pm b/lib/Catalyst/Controller/DBIC/API.pm index ad36164..ef19f08 100644 --- a/lib/Catalyst/Controller/DBIC/API.pm +++ b/lib/Catalyst/Controller/DBIC/API.pm @@ -421,6 +421,31 @@ sub row_format_output return $row; # passthrough by default } +=method_protected item + + :Private + +item will return a single object called by identifier in the uri. It will be inflated via each_object_inflate. + +=cut + +sub item :Private +{ + my ($self, $c) = @_; + + if($c->req->count_objects != 1) + { + $c->log->error($_); + $self->push_error($c, { message => 'No objects on which to operate' }); + $c->detach(); + } + else + { + $c->stash->{response}->{$self->item_root} = $self->each_object_inflate($c, $c->req->get_object(0)); + } +} + + =method_protected update_or_create :Private diff --git a/lib/Catalyst/Controller/DBIC/API/REST.pm b/lib/Catalyst/Controller/DBIC/API/REST.pm index a1c47e1..b40b5de 100644 --- a/lib/Catalyst/Controller/DBIC/API/REST.pm +++ b/lib/Catalyst/Controller/DBIC/API/REST.pm @@ -18,8 +18,8 @@ Provides a REST style API interface to the functionality described in L, the chain root of the controller, and the request type will determine the L method to forward. @@ -36,16 +36,16 @@ As described in L, this action is the cha ... ); -=method_protected base +=method_protected no_id -Chained: L +Chained: L PathPart: none CaptureArgs: 0 Forwards to list level methods described in L as follows: -DELETE: forwards to L then L -POST/PUT: forwards to L then L +DELETE: L +POST/PUT: L GET: forwards to L =cut @@ -76,6 +76,20 @@ sub no_id_GET $c->forward('list'); } +=method_protected with_id + +Chained: L +PathPart: none +CaptureArgs: 0 + +Forwards to list level methods described in L as follows: + +DELETE: L +POST/PUT: L +GET: forwards to L + +=cut + sub with_id :Chained('object_with_id') :PathPart('') :ActionClass('REST') :CaptureArgs(0) {} sub with_id_PUT diff --git a/lib/Catalyst/Controller/DBIC/API/RPC.pm b/lib/Catalyst/Controller/DBIC/API/RPC.pm index 4fc764a..9655497 100644 --- a/lib/Catalyst/Controller/DBIC/API/RPC.pm +++ b/lib/Catalyst/Controller/DBIC/API/RPC.pm @@ -22,6 +22,7 @@ By default provides the following endpoints: $base/create $base/list + $base/id/[identifier] $base/id/[identifier]/delete $base/id/[identifier]/update @@ -40,14 +41,6 @@ As described in L, this action is the cha ... ); -=method_protected object - -Chained: L -PathPart: object -CaptureArgs: 1 - -Provides an chain point to the functionality described in L. All object level endpoints should use this as their chain root. - =cut sub index : Chained('setup') PathPart('') Args(0) { @@ -59,7 +52,7 @@ sub index : Chained('setup') PathPart('') Args(0) { =method_protected create -Chained: L +Chained: L PathPart: create CaptureArgs: 0 @@ -67,17 +60,15 @@ Provides an endpoint to the functionality described in Lforward('object'); - return if $self->get_errors($c); $c->forward('update_or_create'); } =method_protected list -Chained: L +Chained: L PathPart: list CaptureArgs: 0 @@ -85,42 +76,90 @@ Provides an endpoint to the functionality described in Lnext::method($c); } +=method_protected item + +Chained: L +PathPart: '' +Args: 0 + +Provides an endpoint to the functionality described in L. + +=cut + +sub item :Chained('object_with_id') :PathPart('') :Args(0) { + my ($self, $c) = @_; + + $c->forward('view'); +} + =method_protected update -Chained: L +Chained: L PathPart: update -CaptureArgs: 0 +Args: 0 Provides an endpoint to the functionality described in L. =cut -sub update :Chained('object') :PathPart('update') :Args(0) { - my ($self, $c) = @_; +sub update :Chained('object_with_id') :PathPart('update') :Args(0) { + my ($self, $c) = @_; $c->forward('update_or_create'); } =method_protected delete -Chained: L +Chained: L PathPart: delete -CaptureArgs: 0 +Args: 0 Provides an endpoint to the functionality described in L. =cut -sub delete :Chained('object') :PathPart('delete') :Args(0) { - my ($self, $c) = @_; +sub delete :Chained('object_with_id') :PathPart('delete') :Args(0) +{ + my ($self, $c) = @_; + $self->next::method($c); +} - $self->next::method($c); +=method_protected update_bulk + +Chained: L +PathPart: update +Args: 0 + +Provides an endpoint to the functionality described in L for multiple objects. + +=cut + +sub update_bulk :Chained('object_no_id') :PathPart('update') :Args(0) +{ + my ($self, $c) = @_; + $c->forward('update_or_create'); +} + +=method_protected delete_bulk + +Chained: L +PathPart: delete +Args: 0 + +Provides an endpoint to the functionality described in L for multiple objects. + +=cut + +sub delete_bulk :Chained('object_no_id') :PathPart('delete') :Args(0) +{ + my ($self, $c) = @_; + $self->next::method($c); } 1; diff --git a/lib/Catalyst/Controller/DBIC/API/Request/Context.pm b/lib/Catalyst/Controller/DBIC/API/Request/Context.pm index 5653031..9f9ea72 100644 --- a/lib/Catalyst/Controller/DBIC/API/Request/Context.pm +++ b/lib/Catalyst/Controller/DBIC/API/Request/Context.pm @@ -32,6 +32,7 @@ has objects => count_objects => 'count', has_objects => 'count', clear_objects => 'clear', + get_object => 'get', }, ); diff --git a/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm b/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm index 063b75f..7b972b3 100644 --- a/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm +++ b/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm @@ -130,6 +130,15 @@ data_root controls how to reference where the data is in the the request_data has 'data_root' => ( is => 'ro', isa => Str, default => 'list'); +=attribute_public item_root is: ro, isa: Str, default: 'data' + +item_root controls how to reference where the data for single object +requests is in the the request_data + +=cut + +has 'item_root' => ( is => 'ro', isa => Str, default => 'data'); + =attribute_public total_entries_arg is: ro, isa: Str, default: 'totalcount' total_entries_arg controls how to reference 'total_entries' in the the request_data diff --git a/t/rest/item.t b/t/rest/item.t new file mode 100644 index 0000000..a376a6b --- /dev/null +++ b/t/rest/item.t @@ -0,0 +1,43 @@ +use 5.6.0; + +use strict; +use warnings; + +use lib 't/lib'; + +my $base = 'http://localhost'; + +use RestTest; +use DBICTest; +use URI; +use Test::More; +use Test::WWW::Mechanize::Catalyst 'RestTest'; +use HTTP::Request::Common; +use JSON::Any; + +my $mech = Test::WWW::Mechanize::Catalyst->new; +ok(my $schema = DBICTest->init_schema(), 'got schema'); + +my $artist_view_url = "$base/api/rest/artist/"; + +{ + my $id = 1; + my $req = GET( $artist_view_url . $id, undef, 'Accept' => 'application/json' ); + $mech->request($req); + cmp_ok( $mech->status, '==', 200, 'open attempt okay' ); + my %expected_response = $schema->resultset('Artist')->find($id)->get_columns; + my $response = JSON::Any->Load( $mech->content); + is_deeply( $response, { data => \%expected_response, success => 'true' }, 'correct data returned' ); +} + +{ + my $id = 5; + my $req = GET( $artist_view_url . $id, undef, 'Accept' => 'application/json' ); + $mech->request($req); + cmp_ok( $mech->status, '==', 400, 'open attempt not ok' ); + my $response = JSON::Any->Load( $mech->content); + is($response->{success}, 'false', 'not existing object fetch failed ok'); + like($response->{messages}->[0], qr/^No object found for id/, 'error message for not existing object fetch ok'); +} + +done_testing(); diff --git a/t/rpc/item.t b/t/rpc/item.t new file mode 100644 index 0000000..538c379 --- /dev/null +++ b/t/rpc/item.t @@ -0,0 +1,43 @@ +use 5.6.0; + +use strict; +use warnings; + +use lib 't/lib'; + +my $base = 'http://localhost'; + +use RestTest; +use DBICTest; +use URI; +use Test::More; +use Test::WWW::Mechanize::Catalyst 'RestTest'; +use HTTP::Request::Common; +use JSON::Any; + +my $mech = Test::WWW::Mechanize::Catalyst->new; +ok(my $schema = DBICTest->init_schema(), 'got schema'); + +my $artist_view_url = "$base/api/rpc/artist/id/"; + +{ + my $id = 1; + my $req = GET( $artist_view_url . $id, undef, 'Accept' => 'application/json' ); + $mech->request($req); + cmp_ok( $mech->status, '==', 200, 'open attempt okay' ); + my %expected_response = $schema->resultset('Artist')->find($id)->get_columns; + my $response = JSON::Any->Load( $mech->content); + is_deeply( $response, { data => \%expected_response, success => 'true' }, 'correct data returned' ); +} + +{ + my $id = 5; + my $req = GET( $artist_view_url . $id, undef, 'Accept' => 'application/json' ); + $mech->request($req); + cmp_ok( $mech->status, '==', 400, 'open attempt not ok' ); + my $response = JSON::Any->Load( $mech->content); + is($response->{success}, 'false', 'not existing object fetch failed ok'); + like($response->{messages}->[0], qr/^No object found for id/, 'error message for not existing object fetch ok'); +} + +done_testing();