Paws XI - III (back we go)

I finally had a response to my question from this post about adding a new attribute '_status' to the auto-generated classes that Paws creates;

I'd try to avoid leaking HTTP details to the user by default. It's true that for this API call the HTTP Status is relevant to the caller, so exposing it seems legit. I'd try to limit the scope of exposing the HTTP return status by adding an attribute role like https://github.com/pplu/aws-sdk-perl/blob/master/lib/Paws/API.pm#L1 that signal the response to object routines to copy the HTTP Status over to an attribute.

Ok now to translate that to something that the non-Paw dev can understand.

Don't do that! It is not a good idea to expose too much of the response from AWS. However, this is a legitimate use case so an 'attribute role' to expose the status.

The fist thing I should do it role back all my changes (sigh) and start over. At lease with git I can stash my current change to hide them and then apply them as I need them.

The code in 'lib/Paws/API.pm' defines a number of small Moose roles like this one;


package Paws::API::Attribute::Trait::ParamInHeader;
  use Moose::Role;
  use Moose::Util;
  Moose::Util::meta_attribute_alias('ParamInHeader');
  has header_name => (is => 'ro', isa => 'Str');

and these are used, for example, in 'lib/Paws/Net/RestXMLResponse.pm' like this


      my $key = $meta->does('NameInRequest') ? $meta->request_name :
                $meta->does('ParamInHeader') ? lc($meta->header_name) : $att;

It then uses that $key value later in the code. The meta data comes from the generated classes and the above is telling the us to look in the response header for the key value.
Now I traced this back into the Botocore s3 service-2.json code and here is an example of the link back. for my "RestoreObjectOutput" boto;


  "type":"structure",
      "members":{
        "RequestCharged":{
          "shape":"RequestCharged",
          "location":"header",
          "locationName":"x-amz-request-charged"
        },
…

So now I know that ' RequestCharged' attribute is found in the 'header' and it is name ' x-amz-request-charged'

Great now I know what to do to set this up but how to do it???

I took another guess and did a quick

ack '"location":"

and got a large number of line like this

... 360: "location":"header", 8366: "location":"uri", 8372: "location":"querystring", …

and scanning thouh this I found a few like this;


...
glacier/2012-06-01/service-2.json
1155: "location":"statusCode"
...

and into 'glacier/2012-06-01/service-2.json' I go;



"status":{
"shape":"httpstatus",
"documentation":"<p>The HTTP response code for a job output request. The value depends on whether a range was
specified in the request.</p>",
"location":"statusCode"
},

a start at least.

I will add that into s3 json in the


…
         "locationName":"x-amz-restore-output-path"
        },
        "Status":{
          "shape":"httpstatus",
          "documentation":"<P>A successful operation returns either the 200 OK or 202 Accepted status code.</p><p>If the object copy is not previously restored, then Amazon S3 returns 202 Accepted in the response.</p><p>If the object copy is previously restored, Amazon S3 returns 200 OK in the response.</p>",
          "location":"statusCode"
        },
      },

with just a change to the documentation section and to keep the API consistent I upper cased the 's'.

I then looked in the json for the '"httpstatus" shape and that was not there but I did find it in the glacier json so I added that in


    "Message":{"type":"string"},
++  "httpstatus":{"type":"integer"},
    "Metadata":{

Time for a rebuild

make gen-classes

and after a good 20mins of re-compileing I had a look in the 'Paws::S3::RestoreObjectOutput' class and there is my new code


  has RestoreOutputPath => (is => 'ro', isa => 'Str', traits => ['ParamInHeader'], header_name => 'x-amz-restore-output
-path');
++  has Status => (is => 'ro', isa => 'Int');

but I think a few things are missing. I do not have traits in there which should be I am missing much more than just a change to the s3 json and a new entry in the that 'lib/Paws/Net/RestXMLResponse.pm' class;

I then went out and had a snoop in the python code that come as part of boto that is used there to autogenerate classes. In that code base I found this in 'parsers.py';


 if location is None:
                continue
            elif location == 'statusCode':
                final_parsed[name] = self._parse_shape(
                    member_shape, response['status_code'])

Hmm I am missing something when generating my classes.

In Paws the classes are generated via templates and poking about there I found where I think I is should go in this template '/templates/restxml/callresult_class.tt' as this patch


   ...
-    [%- ELSE %], traits => ['NameInRequest'], request_name => '[% shape.members.$param_name.locationName %]'[%- END -%][%- END -%]
+    [%- ELSIF (shape.members.$param_name.location == 'statusCode') %], traits => ['NameInResponse'], reponse_name => '[% shape.members.$param_name.locationName %]'
+       [%- ELSE %], traits => ['NameInRequest'], request_name => '[% shape.members.$param_name.locationName %]'[%- END -%][%- END -%]
…

What I did above was copy the ' NameInRequest' ELSE snippet and then added in as a new ELSIF with a ' NameInResponse' in it place.

Now when I generate the boto code

carton exec builder-bin/gen_classes.pl --classes botocore/botocore/data/s3/2006-03-01/service-2.json

I get the same results. Again it took me a while to sort this one one but in the template there is


 [%- IF (shape.members.$param_name.locationName) %]

and in my s3 json I did not add in a 'locationName' so lets see what this;


"Status":{
          "shape":"httpstatus",
          "documentation":"<P>A successful operation returns either the 200 OK or 202 Accepted status code.</p><p>If the object copy is not previously restored, then Amazon S3 returns 202 Accepted in the response.</p><p>If the object copy is previously restored, Amazon S3 returns 200 OK in the response.</p>",
          "location":"statusCode",
++          "locationName":"status"
        }
change does;

has Status => (is => 'ro', isa => 'Int', traits => ['NameInResponse'], reponse_name => 'status');

so great that goes in.

Now how about getting that trait to actually work, but that is another story.

4eba6898517fb89d7d2722bb86e37b4e--pomeranian-dogs-pomeranians.jpg


Leave a comment

About byterock

user-pic Long time Perl guy, a few CPAN mods allot of work on DBD::Oracle and a few YAPC presentations