[Bps-public-commit] rt-extension-rest2 branch, master, updated. c8581fb576e41372f0d4e6a4ec8827ed8c142f6b

Shawn Moore shawn at bestpractical.com
Tue Dec 13 14:08:41 EST 2016


The branch, master has been updated
       via  c8581fb576e41372f0d4e6a4ec8827ed8c142f6b (commit)
       via  0d514b49bcdf5d86d3f54e36fb57fd5749deaba0 (commit)
       via  607b65fc5717e3447eeef7e7f7ab01f519bc99ed (commit)
       via  9d094915573ac83c2ca8f17e91fde46c7f9d86b2 (commit)
       via  a5b5d60aa2a03288e2670010ed9a705c942461cb (commit)
      from  37d8be7cf9551eef657232e55099c6112ffd8006 (commit)

Summary of changes:
 README                                             |  8 ++--
 lib/RT/Extension/REST2.pm                          |  8 ++--
 lib/RT/Extension/REST2/Resource/Record.pm          | 11 ++++--
 lib/RT/Extension/REST2/Resource/Record/Writable.pm |  8 ++--
 .../REST2/Resource/Role/RequestBodyIsJSON.pm       |  2 +-
 t/lib/RT/Extension/REST2/Test.pm.in                | 44 +++++++++++++++++++---
 t/not_found.t                                      |  6 +--
 t/root.t                                           | 10 +----
 t/tickets.t                                        | 29 +++++++-------
 9 files changed, 77 insertions(+), 49 deletions(-)

- Log -----------------------------------------------------------------
commit a5b5d60aa2a03288e2670010ed9a705c942461cb
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Tue Dec 13 18:11:37 2016 +0000

    Replace TODO comment with TODO test

diff --git a/t/tickets.t b/t/tickets.t
index 7519af1..7bd6750 100644
--- a/t/tickets.t
+++ b/t/tickets.t
@@ -55,8 +55,10 @@ my ($ticket_url, $ticket_id);
         'Content-Type'  => 'application/json; charset=utf-8',
         'Authorization' => $auth,
     );
-    # TODO: This should return 403
-    is($res->code, 400);
+    TODO: {
+        local $TODO = "this should return 403";
+        is($res->code, 403);
+    }
 
     # Rights Test - With CreateTicket
     $user->PrincipalObj->GrantRight( Right => 'CreateTicket' );

commit 9d094915573ac83c2ca8f17e91fde46c7f9d86b2
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Tue Dec 13 18:15:28 2016 +0000

    Test the ticket search result content

diff --git a/t/tickets.t b/t/tickets.t
index 7bd6750..2bb83f3 100644
--- a/t/tickets.t
+++ b/t/tickets.t
@@ -137,6 +137,11 @@ my ($ticket_url, $ticket_id);
     is($content->{per_page}, 20);
     is($content->{total}, 1);
     is(scalar @{$content->{items}}, 1);
+
+    my $ticket = $content->{items}->[0];
+    is($ticket->{type}, 'ticket');
+    is($ticket->{id}, 1);
+    like($ticket->{_url}, qr{$rest_base_path/ticket/1$});
 }
 
 done_testing;

commit 607b65fc5717e3447eeef7e7f7ab01f519bc99ed
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Tue Dec 13 18:49:05 2016 +0000

    Subclass Test::WWW::Mechanize::PSGI

diff --git a/t/lib/RT/Extension/REST2/Test.pm.in b/t/lib/RT/Extension/REST2/Test.pm.in
index 08c5f99..a060375 100644
--- a/t/lib/RT/Extension/REST2/Test.pm.in
+++ b/t/lib/RT/Extension/REST2/Test.pm.in
@@ -12,11 +12,7 @@ use RT::Extension::REST2;
 use Test::WWW::Mechanize::PSGI;
 use RT::User;
 
-sub mech {
-    my $mech = Test::WWW::Mechanize::PSGI->new(
-        app => RT::Extension::REST2->to_app,
-    );
-}
+sub mech { RT::Extension::REST2::Test::Mechanize->new }
 
 {
     my $u;
@@ -42,4 +38,18 @@ sub mech {
     }
 }
 
+{
+    package RT::Extension::REST2::Test::Mechanize;
+    use parent 'Test::WWW::Mechanize::PSGI';
+
+    sub new {
+        my $class = shift;
+        my %args = (
+            app => RT::Extension::REST2->to_app,
+            @_,
+        );
+        return $class->SUPER::new(%args);
+    }
+}
+
 1;

commit 0d514b49bcdf5d86d3f54e36fb57fd5749deaba0
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Tue Dec 13 18:50:32 2016 +0000

    Simplify the common case of processing JSON responses

diff --git a/t/lib/RT/Extension/REST2/Test.pm.in b/t/lib/RT/Extension/REST2/Test.pm.in
index a060375..771e3c9 100644
--- a/t/lib/RT/Extension/REST2/Test.pm.in
+++ b/t/lib/RT/Extension/REST2/Test.pm.in
@@ -42,6 +42,9 @@ sub mech { RT::Extension::REST2::Test::Mechanize->new }
     package RT::Extension::REST2::Test::Mechanize;
     use parent 'Test::WWW::Mechanize::PSGI';
 
+    use JSON;
+    my $json = JSON->new->utf8;
+
     sub new {
         my $class = shift;
         my %args = (
@@ -50,6 +53,19 @@ sub mech { RT::Extension::REST2::Test::Mechanize->new }
         );
         return $class->SUPER::new(%args);
     }
+
+    sub json_response {
+        local $Test::Builder::Level = $Test::Builder::Level + 1;
+        my $self = shift;
+
+        my $res = $self->response;
+
+        local $main::TODO;
+        Test::More::like($res->header('content-type'),
+            qr{^application/json(?:; charset="?utf-8"?)?$});
+
+        return $json->decode($res->content);
+    }
 }
 
 1;
diff --git a/t/not_found.t b/t/not_found.t
index a152b2d..9a68f56 100644
--- a/t/not_found.t
+++ b/t/not_found.t
@@ -2,20 +2,16 @@ use strict;
 use warnings;
 use lib 't/lib';
 use RT::Extension::REST2::Test tests => undef;
-use JSON;
 
 my $mech = RT::Extension::REST2::Test->mech;
 my $auth = RT::Extension::REST2::Test->authorization_header;
 my $rest_base_path = '/REST/2.0';
-my $json = JSON->new->utf8;
 
 sub is_404 {
     local $Test::Builder::Level = $Test::Builder::Level + 1;
     my $res = shift;
     is($res->code, 404);
-    is($res->header('content-type'), 'application/json; charset=utf-8');
-    my $content = $json->decode($res->content);
-    is($content->{message}, 'Not Found');
+    is($mech->json_response->{message}, 'Not Found');
 }
 
 # Proper 404 Response
diff --git a/t/root.t b/t/root.t
index 214f366..ebb43fe 100644
--- a/t/root.t
+++ b/t/root.t
@@ -2,21 +2,17 @@ use strict;
 use warnings;
 use lib 't/lib';
 use RT::Extension::REST2::Test tests => undef;
-use JSON;
 
 my $mech = RT::Extension::REST2::Test->mech;
 
 my $rest_base_path = '/REST/2.0';
-my $json = JSON->new->utf8;
 
 # Unauthorized without Basic Auth
 {
     my $res = $mech->get($rest_base_path);
     is($res->code, 401, 'Unauthorized');
-    is($res->header('content-type'), 'application/json; charset=utf-8');
     is($res->header('www-authenticate'), 'Basic realm="example.com REST API"');
-    my $content = $json->decode($res->content);
-    is($content->{message}, 'Unauthorized');
+    is($mech->json_response->{message}, 'Unauthorized');
 }
 
 my $auth = RT::Extension::REST2::Test->authorization_header;
@@ -50,9 +46,7 @@ my $auth = RT::Extension::REST2::Test->authorization_header;
     );
     is($res->code, 405);
     is($res->header('allow'), 'GET,HEAD,OPTIONS');
-    is($res->header('content-type'), 'application/json; charset=utf-8');
-    my $content = $json->decode($res->content);
-    is($content->{message}, 'Method Not Allowed');
+    is($mech->json_response->{message}, 'Method Not Allowed');
 }
 
 done_testing;
diff --git a/t/tickets.t b/t/tickets.t
index 2bb83f3..8ced0c7 100644
--- a/t/tickets.t
+++ b/t/tickets.t
@@ -2,7 +2,6 @@ use strict;
 use warnings;
 use lib 't/lib';
 use RT::Extension::REST2::Test tests => undef;
-use JSON;
 
 my $mech = RT::Extension::REST2::Test->mech;
 
@@ -16,9 +15,8 @@ my $user = RT::Extension::REST2::Test->user;
     my $res = $mech->get("$rest_base_path/tickets?query=id>0",
         'Authorization' => $auth,
     );
-    is($res->header('content-type'), 'application/json; charset="utf-8"');
-    my $content = $json->decode($res->content);
-    is($content->{count}, 0);
+    is($res->code, 200);
+    is($mech->json_response->{count}, 0);
 }
 
 # Missing Queue
@@ -33,9 +31,7 @@ my $user = RT::Extension::REST2::Test->user;
         'Authorization' => $auth,
     );
     is($res->code, 400);
-    is($res->header('content-type'), 'application/json; charset=utf-8');
-    my $content = $json->decode($res->content);
-    is($content->{message}, 'Could not create ticket. Queue not set');
+    is($mech->json_response->{message}, 'Could not create ticket. Queue not set');
 }
 
 # Ticket Creation
@@ -68,8 +64,6 @@ my ($ticket_url, $ticket_id);
         'Authorization' => $auth,
     );
     is($res->code, 201);
-
-    is($res->header('content-type'), 'application/json; charset="utf-8"');
     ok($ticket_url = $res->header('location'));
     ok($ticket_id = $ticket_url =~ qr[/ticket/(\d+)]);
 }
@@ -92,8 +86,7 @@ my ($ticket_url, $ticket_id);
     );
     is($res->code, 200);
 
-    is($res->header('content-type'), 'application/json; charset="utf-8"');
-    my $content = $json->decode($res->content);
+    my $content = $mech->json_response;
     is($content->{id}, $ticket_id);
     is($content->{Type}, 'ticket');
     is($content->{Status}, 'new');
@@ -130,8 +123,7 @@ my ($ticket_url, $ticket_id);
         'Authorization' => $auth,
     );
     is($res->code, 200);
-    is($res->header('content-type'), 'application/json; charset="utf-8"');
-    my $content = $json->decode($res->content);
+    my $content = $mech->json_response;
     is($content->{count}, 1);
     is($content->{page}, 1);
     is($content->{per_page}, 20);

commit c8581fb576e41372f0d4e6a4ec8827ed8c142f6b
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Tue Dec 13 18:22:17 2016 +0000

    Replace PUT with PATCH
    
    PUT is meant to specify a complete replacement in the content; PATCH is
    for specifying partial modifications. Since RT's Update() routines take
    partial modifications and not complete replacements, PATCH is the more
    correct method.
    
    https://tools.ietf.org/html/rfc5789
    
    "The difference between the PUT and PATCH requests is reflected in the
    way the server processes the enclosed entity to modify the resource
    identified by the Request-URI. In a PUT request, the enclosed entity is
    considered to be a modified version of the resource stored on the origin
    server, and the client is requesting that the stored version be
    replaced. With PATCH, however, the enclosed entity contains a set of
    instructions describing how a resource currently residing on the origin
    server should be modified to produce a new version."

diff --git a/README b/README
index e12bb05..2ba9d30 100644
--- a/README
+++ b/README
@@ -22,17 +22,17 @@ USAGE
     Currently provided endpoints under /REST/2.0/ are:
 
         GET /ticket/:id
-        PUT /ticket/:id <JSON body>
+        PATCH /ticket/:id <JSON body>
         DELETE /ticket/:id
             Sets ticket status to "deleted".
 
         GET /queue/:id
-        PUT /queue/:id <JSON body>
+        PATCH /queue/:id <JSON body>
         DELETE /queue/:id
             Disables the queue.
 
         GET /user/:id
-        PUT /user/:id <JSON body>
+        PATCH /user/:id <JSON body>
         DELETE /user/:id
             Disables the user.
 
@@ -41,7 +41,7 @@ USAGE
     When a GET request is made, each endpoint returns a JSON representation
     of the specified resource, or a 404 if not found.
 
-    When a PUT request is made, the request body should be a modified copy
+    When a PATCH request is made, the request body should be a modified copy
     (or partial copy) of the JSON representation of the specified resource,
     and the record will be updated.
 
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index a3d6591..0a7090c 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -50,17 +50,17 @@ Add this line:
 Currently provided endpoints under C</REST/2.0/> are:
 
     GET /ticket/:id
-    PUT /ticket/:id <JSON body>
+    PATCH /ticket/:id <JSON body>
     DELETE /ticket/:id
         Sets ticket status to "deleted".
 
     GET /queue/:id
-    PUT /queue/:id <JSON body>
+    PATCH /queue/:id <JSON body>
     DELETE /queue/:id
         Disables the queue.
 
     GET /user/:id
-    PUT /user/:id <JSON body>
+    PATCH /user/:id <JSON body>
     DELETE /user/:id
         Disables the user.
 
@@ -69,7 +69,7 @@ For queues and users, C<:id> may be the numeric id or the unique name.
 When a GET request is made, each endpoint returns a JSON representation of the
 specified resource, or a 404 if not found.
 
-When a PUT request is made, the request body should be a modified copy (or
+When a PATCH request is made, the request body should be a modified copy (or
 partial copy) of the JSON representation of the specified resource, and the
 record will be updated.
 
diff --git a/lib/RT/Extension/REST2/Resource/Record.pm b/lib/RT/Extension/REST2/Resource/Record.pm
index 99d7935..9a7e34c 100644
--- a/lib/RT/Extension/REST2/Resource/Record.pm
+++ b/lib/RT/Extension/REST2/Resource/Record.pm
@@ -68,12 +68,17 @@ sub last_modified {
     return create_date($updated);
 }
 
+sub known_methods {
+    my $self = shift;
+    return [@{$self->SUPER::known_methods(@_)}, 'PATCH'];
+}
+
 sub allowed_methods {
     my $self = shift;
     my @ok;
-    push @ok, 'GET', 'HEAD' if $self->DOES("RT::Extension::REST2::Resource::Record::Readable");
-    push @ok, 'DELETE'      if $self->DOES("RT::Extension::REST2::Resource::Record::Deletable");
-    push @ok, 'PUT', 'POST' if $self->DOES("RT::Extension::REST2::Resource::Record::Writable");
+    push @ok, 'GET', 'HEAD'   if $self->DOES("RT::Extension::REST2::Resource::Record::Readable");
+    push @ok, 'DELETE'        if $self->DOES("RT::Extension::REST2::Resource::Record::Deletable");
+    push @ok, 'PATCH', 'POST' if $self->DOES("RT::Extension::REST2::Resource::Record::Writable");
     return \@ok;
 }
 
diff --git a/lib/RT/Extension/REST2/Resource/Record/Writable.pm b/lib/RT/Extension/REST2/Resource/Record/Writable.pm
index 65b5d57..048af88 100644
--- a/lib/RT/Extension/REST2/Resource/Record/Writable.pm
+++ b/lib/RT/Extension/REST2/Resource/Record/Writable.pm
@@ -30,9 +30,9 @@ sub from_json {
     );
 
     my $method = $self->request->method;
-    return $method eq 'PUT'  ? $self->update_resource($data) :
-           $method eq 'POST' ? $self->create_resource($data) :
-                                                        \501 ;
+    return $method eq 'PATCH' ? $self->update_resource($data) :
+           $method eq 'POST'  ? $self->create_resource($data) :
+                                                         \501 ;
 }
 
 sub update_resource {
@@ -69,7 +69,7 @@ sub create_resource {
     if ($self->resource_exists) {
         return error_as_json(
             $self->response,
-            \409, "Resource already exists; use PUT to update");
+            \409, "Resource already exists; use PATCH to update");
     }
 
     my ($ok, $msg) = $self->create_record($data);
diff --git a/lib/RT/Extension/REST2/Resource/Role/RequestBodyIsJSON.pm b/lib/RT/Extension/REST2/Resource/Role/RequestBodyIsJSON.pm
index 2d571cc..dbe38ec 100644
--- a/lib/RT/Extension/REST2/Resource/Role/RequestBodyIsJSON.pm
+++ b/lib/RT/Extension/REST2/Resource/Role/RequestBodyIsJSON.pm
@@ -24,7 +24,7 @@ role {
         return $malformed if $malformed;
 
         my $request = $self->request;
-        return 0 unless $request->method =~ /^(PUT|POST)$/;
+        return 0 unless $request->method =~ /^(PUT|POST|PATCH)$/;
 
         my $json = eval {
             JSON::from_json($request->content)
diff --git a/t/lib/RT/Extension/REST2/Test.pm.in b/t/lib/RT/Extension/REST2/Test.pm.in
index 771e3c9..be15105 100644
--- a/t/lib/RT/Extension/REST2/Test.pm.in
+++ b/t/lib/RT/Extension/REST2/Test.pm.in
@@ -66,6 +66,14 @@ sub mech { RT::Extension::REST2::Test::Mechanize->new }
 
         return $json->decode($res->content);
     }
+
+    # modeled off of LWP::UserAgent::put
+    sub patch {
+        require HTTP::Request::Common;
+        my($self, @parameters) = @_;
+        my @suff = $self->_process_colonic_headers(\@parameters, (ref($parameters[1]) ? 2 : 1));
+        return $self->request( HTTP::Request::Common::_simple_req( 'PATCH', @parameters ), @suff );
+    }
 }
 
 1;

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list