Paws the XV (Still Some Way to Go)

In my last post I got nicely snookered by the S3 call 'GetBucketLocation' which was one of the call that had a 'todo' test.

The XML coming back from ASW is correct

<?xml version="1.0" encoding="UTF-8"?> <LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-0301/"> EU </LocationConstraint> but no matter what I tried on 'XML::Simple' I could not get it to parse just the way I want though I did get very close with this

 my $xml = XML::Simple->new(
      ForceArray    => qr/^(?:item|Errors)/i,
      KeyAttr       => '',
      SuppressEmpty => undef,
++    KeepRoot => 1
    );
    return $xml->parse_string($data);

and I did get this in the end


 bless( {
                 'LocationConstraint' => 'eu-west-2',
                 '_request_id' => 'ECDB8CF1EB9699A2'
               }, 'Paws::S3::GetBucketLocationOutput' );

but I then get about 147 fails as that changes all the XML that is being parsed.

Failed 147/9641 subtests (5 TODO tests unexpectedly succeeded)

So I can't just arbitrarily change the parser settings but I might be able to change things is botocore to tell me to use that KeepRoot on the XML Parse but I that is a dead end as well as I am not sending any meta into the call that does the parseing


$self->unserialize_response( $content )

I was thinking I could fix the boto code up to add in an extra 'root' tag to the body of my 'XML' like this


         "requestUri":"/{Bucket}?location"
       },
       "input":{"shape":"GetBucketLocationRequest"},
-      "output":{"shape":"GetBucketLocationOutput"},
+      "output":{"shape":"GetBucketLocationResponse"},
       "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest
...
+       "GetBucketLocationResponse":{
+      "type":"structure",
+      "members":{
+        "LocationConstraint":{
+          "shape":"GetBucketLocationOutput",
+          "location":"content",
+          "documentation":"<p/>"
+        }
+      }
+    },
     "GetBucketLocationOutput":{
       "type":"structure",
       "members":{
         "LocationConstraint":{
           "shape":"BucketLocationConstraint",
+                 "location":"content",
           "documentation":"<p/>"
         }

Not all that did was recreate the same problem but with just a minor change in the error output;


Attribute (LocationConstraint) does not pass the type constraint because: Validation failed for 'Paws::S3::GetBucketLocationOutput' with value eu-west-2 (not isa Paws::S3::GetBucketLocationOutput) at /wwwveh/lib/x86_64-linux-thread-multi/Moose/Object.pm line 24

and then I tried this


+        "LocationConstraint":{
+          "shape":"BucketLocationConstraint",
"location":"content",

but without the root in there I just get this again

can't use string ("eu-west-2") as a HASH ref while "strict refs" in use at /home/scolesj/aws-sdk-perl/lib/Paws/Net/RestXMLResponse.pm line 336.

The problem of course lies in the parser as it is cutting off the 'Root' node and no matter how much I am playing about with boto I am not going to solve the problem of the XML data coming back with only a 'Root' node.

Maybe a new trait in boto to tell me that the expected result for the 'attribure' in the response is the parsed content?

Maybe something like this in Boto;


 "GetBucketLocationOutput":{
      "type":"structure",
      "members":{
        "LocationConstraint":{
           "location":"content",
           "locationName":"LocationConstraint",
          "shape":"BucketLocationConstraint",
          "documentation":"<p/>"
        }
      }
    },

"location":"content", will tell me to just look at the parsed content of the XML and I think I would have to add this new 'Trait' to 'API.pm'


package Paws::API::Attribute::Trait::ParamIsContent;
use Moose::Role;
use Moose::Util;
Moose::Util::meta_attribute_alias('ParamIsContent');

And like before a change to the template.


  [%- ELSIF (shape.members.$param_name.location == 'uri') %], traits => ['ParamInURI'], uri_name => '[% shape.members.$param_name.locationName -%]'
    [%- ELSIF (shape.members.$param_name.location == 'content') %], traits => ['ParamInContent']
    [%- ELSE %], traits => ['NameInRequest'], request_name => '[% shape.members.$param_name.locationName %]'[%- END -%][%- END -%]

and give the build a run again;

carton exec builder-bin/gen_classes.pl --classes botocore/botocore/data/s3/2006-03-01/service-2.json Processing botocore/botocore/data/s3/2006-03-01/service-2.json Complex accessor NextMarker || Contents[-1].Key at builder-lib/Paws/API/Builder.pm line 1086. Complex accessor NextMarker || Contents[-1].Key at builder-lib/Paws/API/Builder.pm line 1086.

and a look in 'auto-lib/Paws/S3/GetBucketLocationOutput.pm'


package Paws::S3::GetBucketLocationOutput;
  use Moose;
  has LocationConstraint => (is => 'ro', isa => 'Str', traits => ['ParamIsContent']);
  has _request_id => (is => 'ro', isa => 'Str');
1;

Which is nice but that gives me a 'trait' on a attribute which I unfortunately I cant really use as I have to work with this line of code;


    if ($ret_class->can('_stream_param')) {
      $unserialized_struct = {}
    } else {

So I need to get my 'content' as part of the 'class' not an attribute of the 'class'.

So more to do for my next post. Darn.

88c11b6f70108cdb4de72020bc908c31.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