Paws XXXXVI (The game she is over)

Well I left off on form my last post with this Moose error;

Attribute (Items) does not pass the type constraint because: Validation failed for 'ArrayRef[Str|Undef]' with value [ { Method: ARRAY(0x5184cf0) } ] at /wwwveh/lib/x86_64-linux-thread-multi/Moose/ line 24

but before I tackle that on I wanted to have a closer look at my changes I did to the template; I wanted to clean that up nicely and then my plan is to see if my changes have Borden anything else.

So in cleaning it up I found what might be a bug.

It seems that this call

[%- IF (stream_param or shape.payload == param_name) %]

is now out of the attributes foreach loop so I change things about a bit and now I just set a var in the template and apply the new trait if the var has a value;

+  [%- IF (shape.payload) %] [% has_payload=param_name %] [% END %]
   [%- IF (c.required_in_shape(shape,param_name)) %], required => 1[% END %]);
 [% END %]
-  [%- IF (stream_param or shape.payload == param_name) %]
-  use MooseX::ClassAttribute;
-  [%- IF (stream_param) %]
-  class_has _stream_param => (is => 'ro', default => '[% c.to_payload_shape_name(stream_param) %]');[% END %]
-  [%- IF (shape.payload == param_name) %]
-  class_has _payload => (is => 'ro', default => '[% param_name %]');[% END %]
-  [%- END %]
   has _request_id => (is => 'ro', isa => 'Str');
+  [%- IF (stream_param or has_payload ) %]
+    use MooseX::ClassAttribute;
+    [%- IF (stream_param) %]
+    class_has _stream_param => (is => 'ro', default => '[% c.to_payload_shape_name(stream_param) %]');[% END %]
+    [%- IF (has_payload) %]
+    class_has _payload => (is => 'ro', default => '[% has_payload %]');[% END %]
[%- END %]

and my class comes out nice and clean;

package Paws::CloudFront::GetDistributionResult;
  use Moose;
  has Distribution => (is => 'ro', isa => 'Paws::CloudFront::Distribution');
  has ETag => (is => 'ro', isa => 'Str', traits => ['ParamInHeader'] , header_name   => 'ETag' );
  has _request_id => (is => 'ro', isa => 'Str');
  use MooseX::ClassAttribute;
  class_has _payload => (is => 'ro', default => 'Distribution');

However this time round I did not get;

Attribute (Items) does not pass the type constraint because: Validation failed for 'ArrayRef[Str|Undef]'

I got;

Attribute (Id) is required at /wwwveh/lib/x86_64-linux-thread-multi/Moose/ line 24 Moose::Object::new('Paws::CloudFront::Origin') called at /home/scolesj/aws-sdk-perl/lib/Paws/Net/ line 300

I ran the test again got the same error but the next time I ran it I got a different error with no code changes in between.

Well I fortunately spotted what was going on here right away. It is a good old perl quirk that I know has caught me in the past and most likely caught many others as well.

'keys on a hash are not in a guaranteed order'

So my input hash is coming in one way but is not processed the same way so it looks like a different random error each time it ran.

Time for a little more debugging and I found that the best place for my debug code was near the very end of the 'new_from_result_struct' sub

++  warn("new args= $class=".Dumper(\%args));

I do not think I have played in the sub yet in my PAWs adventure so I will give the 25c tour of what it does. It acts as a sort of coercion engine taking the passed params, looking at thier class meta attributes and then translates it into a new class.

Well the first thing I am going to do is set the 'sub new_from_result_struct' call up so it will return the same results each time. I can do that by simply adding a 'sort' in that sub;

--foreach my $att ($class->meta->get_attribute_list) {
++foreach my $att (sort($class->meta->get_attribute_list)) {

With that in place I can now have a look at my debug out put;

 new= Paws::CloudFront::CachedMethods=$VAR1 = {
          'Quantity' => '2',
          'Items' => [
                         'Method' => [

with this error;

Attribute (Items) does not pass the type constraint because:
Validation failed for 'ArrayRef[Str|Undef]' with value [ { Method: ARRAY(0x54522f8) } ] at
/wwwveh/lib/x86_64-linux-thread-multi/Moose/ line 24

So lets get rid of that one;

I had a look in the class and my Items attributes is

 has Items => (is => 'ro',
                        isa => 'ArrayRef[Str|Undef]', 
                       request_name => 'Method',
                       list_request_name => 'Items', 
                      traits => ['NameInRequest','ListNameInRequest'],
                      required => 1);

and I think I can work with that.

I checked in the boto and for 'Items' in 'CachedMethods'

I found

"documentation":"<p>A complex type that contains the HTTP methods that you want CloudFront to cache responses to.</p>"

Hmm and a MethodsList in boto is


which justs points to a string.

So what I want is a transform like this

'Items' => [

After adding in an absurdly large amount of debugging code to find the correct spot for the transform, all I needed was to do was add this in

++if ($meta->does("ListNameInRequest")){
++         $result->{$meta->{list_request_name}}= $result->{$meta->
++     }

if ($meta->does("XMLAtribute")){
$args{ $key } = $result->{$meta->xml_attribute_name()};

in my sub but now I get a new error;

new= Paws::CloudFront::LoggingConfig=$VAR1 = {
          'IncludeCookies' => 0,
          'Enabled' => 0
Attribute (Prefix) is required at /wwwveh/lib/x86_64-linux-thread-multi/Moose/ line 24 Moose::Object::new('Paws::CloudFront::LoggingConfig', 'IncludeCookies', 0, 'Enabled', 0)

That leads me back to this generated class;

package Paws::CloudFront::LoggingConfig;
  use Moose;
  has Bucket => (is => 'ro', isa => 'Str'  , required => 1);
  has Enabled => (is => 'ro', isa => 'Bool'  , required => 1);
  has IncludeCookies => (is => 'ro', isa => 'Bool'  , required => 1);
  has Prefix => (is => 'ro', isa => 'Str'  , required => 1);

all the fields are required which is great when I am putting stuff into the AWS API but in this case 'undef' is fine in any of the above as I am getting code back from the API and we can't control what is sent back.

So I think my only choice here is to set up a default value in the response.

That was easy enough as all I need to do was add this

          elsif ($att_is_required){
              $args{ $att } = "";
              $args{ $att } = 0
                if ($att_type eq 'Bool' or $att_type eq 'Int');

and I got rid of that error but ran into yet one more error;

can't locate object method "does" via package "HEAD" (perhaps
you forgot to load "HEAD"?) at /home/scolesj/aws-sdk-perl/auto-lib/ line 155.

ouch that is in some auto generated code.

The head scratch-er here was I was getting all the classes correct such as;

'AllowedMethods' => bless( {
                                              'Quantity' => '2',
                                              'Items' => [
                                              'CachedMethods' => bless( {
                                                                          'Quantity' => '2',
                                                                          'Items' => [
                                                                        }, 'Paws::CloudFront::CachedMethods' )
                                            }, 'Paws::CloudFront::AllowedMethods' ),

So I am a little concerned;

Time I think for my good old debugging buddy 'caller()'

I added this in

sub to_hash {
my (undef, $params) = @_;
my $refHash = {};
++my ($A,$B,$C) = caller();
++use Data::Dumper;
++warn("to_hash $A,$B,$C ".Dumper($params));

That led me back into the same sub and with a few more lines of debugging I found my answer. Seems I inadvertently made a typo in the 'Paws::CloudFront::CachedMethods' class when I was looking at it.

So I regenerated all my classes and presto things are working fine.

Now just get rid off all that debugging and clean up my code so I can move onto something else.


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