File Coverage

blib/lib/WebService/Amazon/Support.pm
Criterion Covered Total %
statement 3 3 100.0
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 4 4 100.0


line stmt bran cond sub pod time code
1             package WebService::Amazon::Support;
2 1     1   15132 use base qw( WebService::Simple );
  1         2  
  1         488  
3              
4             use 5.006;
5             use strict;
6             use warnings;
7              
8             use AWS::Signature4;
9             use Carp;
10             use HTTP::Request::Common;
11             use JSON;
12             use LWP;
13             use Params::Validate qw( :all );
14             use Readonly;
15             #use Smart::Comments '###';
16             #use Smart::Comments '###', '####';
17             #use Smart::Comments '###', '####', '#####';
18              
19             =encoding utf-8
20              
21             =head1 NAME
22              
23             WebService::Amazon::Support - The great new WebService::Amazon::Support!
24              
25             =head1 VERSION
26              
27             Version 0.0.2
28              
29             =cut
30              
31             use version;
32             our $VERSION = version->declare("v0.0.2");
33              
34             =head1 SYNOPSIS
35              
36             This module provides a Perl wrapper around Amazon's Support API
37             ( L ). You will need
38             to be an AWS customer with an ID and Secret which has been provided
39             access to Support.
40              
41             B Some parameter validation is purposely lax. The API will
42             generally fail when invalid params are passed. The errors may not
43             be helpful.
44              
45             use WebService::Amazon::Support;
46              
47             my $sup = WebService::Amazon::Support->new( param => { id => $AWS_ACCESS_KEY_ID,
48             secret => $AWS_ACCESS_KEY_SECRET } );
49             ...
50              
51             =cut
52              
53             # From: http://docs.aws.amazon.com/general/latest/gr/rande.html#awssupport_region
54             # FIXME: use an array and assemble the URL in the constructor?
55             Readonly our %REGIONS => ( 'us-east-1' => 'https://support.us-east-1.amazonaws.com' );
56              
57             # Global API Version and Default Region
58             Readonly our $API_VERSION => '2013-04-15';
59             Readonly our $DEF_REGION => 'us-east-1';
60             Readonly our $DEF_LANG => 'en';
61             Readonly our $TARGET_PRE => 'AWSSupport_20130415';
62              
63             # some defaults
64             __PACKAGE__->config(
65             base_url => $REGIONS{'us-east-1'},
66             );
67              
68             # Global patterns for param validation
69             Readonly our $REGEX_ID => '^[A-Z0-9]{20}$';
70             Readonly our $REGEX_REGION => '^[a-z]{2}-[a-z].*?-\d$';
71             Readonly our $REGEX_SECRET => '^[A-Za-z0-9/+]{40}$';
72              
73             =head1 INTERFACE
74              
75             =head2 new
76              
77             Inherited from L, and takes all the same arguments.
78             You B provide the Amazon required arguments of B, and B
79             in the param hash:
80              
81             my $sup = WebService::Amazon::Support->new( param => { id => $AWS_ACCESS_KEY_ID,
82             secret => $AWS_ACCESS_KEY_SECRET } );
83              
84             =over 4
85              
86             =item B
87              
88             =item id B<(required)>
89              
90             You can find more information in the AWS docs:
91             L
92              
93             =item secret B<(required)>
94              
95             You can find more information in the AWS docs:
96             L
97              
98             =back
99              
100             =cut
101              
102             # HASH to hold API call specifications
103             our( %API_SPEC );
104              
105             # #################################################################################################
106             #
107             # Override WebService::Simple methods
108             #
109              
110             $API_SPEC{'new'} = {
111             id => { type => SCALAR, regex => qr/$REGEX_ID/, },
112             region => { type => SCALAR, regex => qr/$REGEX_REGION/, optional => 1, default => $DEF_REGION, },
113             secret => { type => SCALAR, regex => qr/$REGEX_SECRET/, },
114             };
115              
116             # Override valid options, set API version and create XML parser
117             sub new {
118             ### Enter: (caller(0))[3]
119             my( $class, %args ) = @_;
120             my $self = $class->SUPER::new(%args);
121            
122             # this is silly, but easier for validation
123             my( @temp_params ) = %{ $self->{basic_params} };
124             my %params = validate( @temp_params, $API_SPEC{'new'} );
125             ##### %params
126            
127             # set our API version
128             $self->{api_version} = $API_VERSION;
129            
130             # for parsing the responses
131             $self->{js} = JSON->new->allow_nonref;
132            
133             # store a request signer for later
134             $self->{signer} = AWS::Signature4->new( -access_key => $self->{basic_params}{id},
135             -secret_key => $self->{basic_params}{secret} );
136              
137             # store a user agent for later
138             $self->{ua} = LWP::UserAgent->new( agent => "$class/$VERSION" );
139            
140             # change the endpoint for the requested region
141             if ( $params{region} && $REGIONS{$params{region}} ) {
142             $self->{base_url} = $REGIONS{$params{region}};
143             }
144             elsif ( $params{region} && !$REGIONS{$params{region}} ) {
145             carp( "Unknown region: $params{region}; using $DEF_REGION...")
146             }
147             ### Exit: (caller(0))[3]
148             return bless($self, $class);
149             }
150              
151             # override parent get to perform the required AWS signatures
152             sub _get {
153             ### Enter: (caller(0))[3]
154             my( $self ) = shift;
155             my( %args ) = @_;
156             #my $self = $class->SUPER::new(%args);
157            
158             ##### $self
159             my $signer = AWS::Signature4->new( -access_key => $self->{basic_params}{id},
160             -secret_key => $self->{basic_params}{secret} );
161              
162             my $ua = LWP::UserAgent->new();
163              
164             ### %args
165             if ( !$args{params} ) {
166             carp( "No paramerter provided for request!" );
167             return undef;
168             }
169             else {
170             $args{params}{Version} = $self->{api_version};
171             }
172              
173             my $uri = URI->new( $self->{base_url} );
174             $uri->query_form( $args{params} );
175             #### $uri
176              
177             my $url = $signer->signed_url($uri); # This gives a signed URL that can be fetched by a browser
178             #### $url
179             # This doesn't quite work (it wants path and args onyl)
180             #my $response = $self->SUPER::get( $url );
181             my $response = $ua->get($url);
182             ##### $response
183             if ( $response->is_success ) {
184             ### Exit: (caller(0))[3]
185             return $self->{xs}->XMLin( $response->decoded_content );
186             }
187             else {
188             carp( $response->status_line );
189             ### Exit: (caller(0))[3]
190             return undef;
191             }
192             ### Exit: (caller(0))[3]
193             }
194              
195             # override parent post to perform the required AWS signatures
196             sub _post {
197             my( $self, %args ) = @_;
198              
199             ### %args
200             if ( !$args{params} ) {
201             carp( "No paramerter provided for request!" );
202             return undef;
203             }
204             else {
205             $args{params}{Version} = $self->{api_version};
206             }
207            
208             # JSON encode the params
209             my( $js ) = $self->{js}->encode( $args{params} );
210             ### $js
211            
212             # set custom HTTP request header fields
213             my $req = HTTP::Request->new( POST => $self->{base_url} );
214             #
215             # NOTE: Shenanigans! Reverse engineered from https://github.com/boto/boto/blob/develop/boto/support/layer1.py#L652
216             #
217             $req->header( 'X-Amz-Target' => join( '.', $TARGET_PRE, $args{params}{Action} ) );
218             $req->header( 'content-type' => 'application/x-amz-json-1.1' );
219            
220             $req->content( $js );
221             ### $req
222              
223             $self->{signer}->sign($req);
224             #### $req
225             my $response = $self->{ua}->request( $req );
226             ##### $response
227             my( $jsDecode ) = $self->{js}->decode( $response->decoded_content );
228             if ( $response->is_success ) {
229             return $jsDecode;
230             }
231             else {
232             carp( $self->{js}->pretty->encode( $jsDecode ) . $response->status_line );
233             return undef;
234             }
235             }
236              
237             # implement a general way to configure repeated options to match the API
238             sub _handleRepeatedOptions {
239             ### Enter: (caller(0))[3]
240             my( $self ) = shift;
241             my( $repeat, %params ) = @_;
242             #### Start: %params
243              
244             if ( $params{$repeat} && ref( $params{$repeat} ) eq "ARRAY" ) {
245             my( $i ) = 1;
246             foreach my $t ( @{ $params{$repeat} } ) {
247             $params{"${repeat}.member.${i}"} = $t;
248             $i++;
249             }
250             delete( $params{$repeat} );
251             }
252            
253             #### End: %params
254             ### Exit: (caller(0))[3]
255             return %params;
256             }
257              
258             # most of the calls can do this
259             sub _genericCallHandler {
260             ### Enter: (caller(0))[3]
261             my( $op ) = pop( [ split( /::/, (caller(1))[3] ) ] );
262             ### Operation: $op
263             my( $self ) = shift;
264             my %params = validate( @_, $API_SPEC{$op} );
265             $params{Action} = $op;
266             ### %params
267            
268             # handle ARRAY / repeated options -- this API is JSON: don't do anything
269             # foreach my $opt ( keys( %{ $API_SPEC{$op} } ) ) {
270             # ### Checking opt: $opt
271             # if ( $API_SPEC{$op}->{$opt}->{type} == ARRAYREF ) {
272             # ### Found a repeatable option: $opt
273             # ( %params ) = $self->_handleRepeatedOptions( $opt, %params );
274             # }
275             # }
276            
277             ### %params
278             my( $rez ) = $self->_post( params => \%params );
279             ### Exit: (caller(0))[3]
280             return $rez;
281             }
282              
283             # #################################################################################################
284             #
285             # API Methods Below
286             #
287              
288             # #################################################################################################
289             ## AddAttachmentsToSet
290              
291             =head2 AddAttachmentsToSet( )
292              
293             Unimplimented (for now)
294              
295             =cut
296              
297             # #################################################################################################
298             ## AddCommunicationToCase
299              
300             =head2 AddCommunicationToCase( )
301              
302             Unimplimented (for now)
303              
304             =cut
305              
306             # #################################################################################################
307             ## CreateCase
308              
309             =head2 CreateCase( )
310              
311             Unimplimented (for now)
312              
313             =cut
314              
315             # #################################################################################################
316             ## DescribeAttachment
317              
318             =head2 DescribeAttachment( )
319              
320             Returns the attachment that has the specified ID. Attachment IDs are
321             generated by the case management system when you add an attachment to
322             a case or case communication. Attachment IDs are returned in the
323             AttachmentDetails objects that are returned by the
324             DescribeCommunications operation.
325              
326             Refer to L
327              
328             =over 4
329              
330             =item B
331              
332             =item attachmentId B<(required string)>
333              
334             The ID of the attachment to return. Attachment IDs are returned by the DescribeCommunications operation.
335              
336             =item B
337              
338             =back
339              
340             =cut
341              
342             $API_SPEC{'DescribeAttachment'} = {
343             attachmentId => { type => SCALAR },
344             };
345              
346             sub DescribeAttachment {
347             ### Enter: (caller(0))[3]
348             my( $rez ) = _genericCallHandler( @_ );
349             ### Exit: (caller(0))[3]
350             return $rez;
351             }
352              
353             # #################################################################################################
354             ## DescribeCases
355              
356             =head2 DescribeCases( )
357              
358             Unimplimented (for now)
359              
360             =cut
361              
362             # #################################################################################################
363             ## DescribeCommunications
364              
365             =head2 DescribeCommunications( )
366              
367             Unimplimented (for now)
368              
369             =cut
370              
371             # #################################################################################################
372             ## DescribeServices
373              
374             =head2 DescribeServices( )
375              
376             Returns a list of the available solution stack names.
377              
378             Refer to L
379              
380             =over 4
381              
382             =item B
383              
384             =item Language I<(optional string)>
385              
386             The ISO 639-1 code for the language in which AWS provides support. AWS
387             Support currently supports English ("en") and Japanese ("ja"). Language
388             parameters must be passed explicitly for operations that take them.
389              
390             =item ServiceCodeList I<(optional array)>
391              
392             A JSON-formatted list of service codes available for AWS services.
393              
394             Length constraints: Minimum of 0 item(s) in the list. Maximum of 100 item(s) in the list.
395              
396             Required: No
397              
398             =item B
399              
400             =back
401              
402             =cut
403              
404             $API_SPEC{'DescribeServices'} = {
405             language => { type => SCALAR, regex => qr/^(en|ja)$/i, optional => 1, default => $DEF_LANG, },
406             serviceCodeList => { type => ARRAYREF, optional => 1 },
407             };
408              
409             sub DescribeServices {
410             ### Enter: (caller(0))[3]
411             my( $rez ) = _genericCallHandler( @_ );
412             ### Exit: (caller(0))[3]
413             return $rez;
414             }
415              
416             # #################################################################################################
417             ## DescribeSeverityLevels
418              
419             =head2 DescribeSeverityLevels( )
420              
421             Returns the list of severity levels that you can assign to an AWS
422             Support case. The severity level for a case is also a field in the
423             CaseDetails data type included in any CreateCase request.
424              
425             Refer to L
426              
427             =over 4
428              
429             =item B
430              
431             =item Language I<(optional string)>
432              
433             The ISO 639-1 code for the language in which AWS provides support. AWS
434             Support currently supports English ("en") and Japanese ("ja"). Language
435             parameters must be passed explicitly for operations that take them.
436              
437             =item B
438              
439             =back
440              
441             =cut
442              
443             $API_SPEC{'DescribeSeverityLevels'} = {
444             language => { type => SCALAR, regex => qr/^(en|ja)$/i, optional => 1, default => $DEF_LANG, },
445             };
446              
447             sub DescribeSeverityLevels {
448             ### Enter: (caller(0))[3]
449             my( $rez ) = _genericCallHandler( @_ );
450             ### Exit: (caller(0))[3]
451             return $rez;
452             }
453              
454             # #################################################################################################
455             ## DescribeTrustedAdvisorCheckRefreshStatuses
456              
457             =head2 DescribeTrustedAdvisorCheckRefreshStatuses( )
458              
459             Returns the refresh status of the Trusted Advisor checks that have the
460             specified check IDs. Check IDs can be obtained by calling DescribeTrustedAdvisorChecks.
461              
462             Refer to L
463              
464             =over 4
465              
466             =item B
467              
468             =item checkIds B<(required array)>
469              
470             The IDs of the Trusted Advisor checks.
471              
472             =item B
473              
474             =back
475              
476             =cut
477              
478             $API_SPEC{'DescribeTrustedAdvisorCheckRefreshStatuses'} = {
479             checkIds => { type => ARRAYREF },
480             };
481              
482             sub DescribeTrustedAdvisorCheckRefreshStatuses {
483             ### Enter: (caller(0))[3]
484             my( $rez ) = _genericCallHandler( @_ );
485             ### Exit: (caller(0))[3]
486             return $rez;
487             }
488              
489             # #################################################################################################
490             ## DescribeTrustedAdvisorCheckResult
491              
492             =head2 DescribeTrustedAdvisorCheckResult( )
493              
494             Returns the results of the Trusted Advisor check that has the specified
495             check ID. Check IDs can be obtained by calling DescribeTrustedAdvisorChecks.
496              
497             The response contains a TrustedAdvisorCheckResult object, which contains these three objects:
498              
499             TrustedAdvisorCategorySpecificSummary
500             TrustedAdvisorResourceDetail
501             TrustedAdvisorResourcesSummary
502              
503             In addition, the response contains these fields:
504              
505             Status. The alert status of the check: "ok" (green), "warning" (yellow), "error" (red), or "not_available".
506             Timestamp. The time of the last refresh of the check.
507             CheckId. The unique identifier for the check.
508              
509             Refer to L
510              
511             =over 4
512              
513             =item B
514              
515             =item checkIds B<(required string)>
516              
517             The ID of the Trusted Advisor check.
518              
519             =item Language I<(optional string)>
520              
521             The ISO 639-1 code for the language in which AWS provides support. AWS
522             Support currently supports English ("en") and Japanese ("ja"). Language
523             parameters must be passed explicitly for operations that take them.
524              
525             =item B
526              
527             =back
528              
529             =cut
530              
531             $API_SPEC{'DescribeTrustedAdvisorCheckResult'} = {
532             checkId => { type => SCALAR },
533             language => { type => SCALAR, regex => qr/^(en|ja)$/i, optional => 1, default => $DEF_LANG, },
534             };
535              
536             sub DescribeTrustedAdvisorCheckResult {
537             ### Enter: (caller(0))[3]
538             my( $rez ) = _genericCallHandler( @_ );
539             ### Exit: (caller(0))[3]
540             return $rez;
541             }
542              
543             # #################################################################################################
544             ## DescribeTrustedAdvisorCheckSummaries
545              
546             =head2 DescribeTrustedAdvisorCheckSummaries( )
547              
548             Returns the summaries of the results of the Trusted Advisor checks that
549             have the specified check IDs. Check IDs can be obtained by calling DescribeTrustedAdvisorChecks.
550              
551             The response contains an array of TrustedAdvisorCheckSummary objects.
552              
553             Refer to L
554              
555             =over 4
556              
557             =item B
558              
559             =item checkIds B<(required array)>
560              
561             The IDs of the Trusted Advisor checks.
562              
563             =item B
564              
565             =back
566              
567             =cut
568              
569             $API_SPEC{'DescribeTrustedAdvisorCheckSummaries'} = {
570             checkId => { type => ARRAYREF },
571             };
572              
573             sub DescribeTrustedAdvisorCheckSummaries {
574             ### Enter: (caller(0))[3]
575             my( $rez ) = _genericCallHandler( @_ );
576             ### Exit: (caller(0))[3]
577             return $rez;
578             }
579              
580             # #################################################################################################
581             ## DescribeTrustedAdvisorChecks
582              
583             =head2 DescribeTrustedAdvisorChecks( )
584              
585             Returns information about all available Trusted Advisor checks,
586             including name, ID, category, description, and metadata. You must
587             specify a language code; English ("en") and Japanese ("ja") are
588             currently supported.
589              
590             The response contains a TrustedAdvisorCheckDescription for each check.
591              
592             Refer to L
593              
594             =over 4
595              
596             =item B
597              
598             =item Language I<(optional string)>
599              
600             The ISO 639-1 code for the language in which AWS provides support. AWS
601             Support currently supports English ("en") and Japanese ("ja"). Language
602             parameters must be passed explicitly for operations that take them.
603              
604             =item B
605              
606             =back
607              
608             =cut
609              
610             $API_SPEC{'DescribeTrustedAdvisorChecks'} = {
611             language => { type => SCALAR, regex => qr/^(en|ja)$/i, optional => 1, default => $DEF_LANG, },
612             };
613              
614             sub DescribeTrustedAdvisorChecks {
615             ### Enter: (caller(0))[3]
616             my( $rez ) = _genericCallHandler( @_ );
617             ### Exit: (caller(0))[3]
618             return $rez;
619             }
620              
621             # #################################################################################################
622             ## RefreshTrustedAdvisorCheck
623              
624             =head2 RefreshTrustedAdvisorCheck( )
625              
626             Requests a refresh of the Trusted Advisor check that has the specified
627             check ID. Check IDs can be obtained by calling DescribeTrustedAdvisorChecks.
628              
629             The response contains a TrustedAdvisorCheckRefreshStatus object, which contains these fields:
630              
631             Status. The refresh status of the check: "none", "enqueued", "processing", "success", or "abandoned".
632             MillisUntilNextRefreshable. The amount of time, in milliseconds, until the check is eligible for refresh.
633             CheckId. The unique identifier for the check.
634              
635             Refer to L
636              
637             =over 4
638              
639             =item B
640              
641             =item checkId B<(required string)>
642              
643             The ID of the Trusted Advisor check.
644              
645             =item B
646              
647             =back
648              
649             =cut
650              
651             $API_SPEC{'RefreshTrustedAdvisorCheck'} = {
652             checkId => { type => SCALAR }
653             };
654              
655             sub RefreshTrustedAdvisorCheck {
656             ### Enter: (caller(0))[3]
657             my( $rez ) = _genericCallHandler( @_ );
658             ### Exit: (caller(0))[3]
659             return $rez;
660             }
661              
662             # #################################################################################################
663             # ResolveCase
664              
665             =head2 ResolveCase( )
666              
667             Takes a CaseId and returns the initial state of the case along with
668             the state of the case after the call to ResolveCase completed.
669              
670             Refer to L
671              
672             =over 4
673              
674             =item B
675              
676             =item caseId B<(required string)>
677              
678             CaseId
679             The AWS Support case ID requested or returned in the call. The case ID
680             is an alphanumeric string formatted as shown in this example:
681             case-12345678910-2013-c4c1d2bf33c5cf47
682              
683             =item B
684              
685             =back
686              
687             =cut
688              
689             $API_SPEC{'ResolveCase'} = {
690             checkId => { type => SCALAR }
691             };
692              
693             sub ResolveCase {
694             ### Enter: (caller(0))[3]
695             my( $rez ) = _genericCallHandler( @_ );
696             ### Exit: (caller(0))[3]
697             return $rez;
698             }
699              
700             # #################################################################################################
701              
702             =head1 AUTHOR
703              
704             Matthew Cox, C<< >>
705              
706             =head1 BUGS
707              
708             Please report any bugs or feature requests to C, or through
709             the web interface at L. I will be notified, and then you'll
710             automatically be notified of progress on your bug as I make changes.
711              
712             =head1 SUPPORT
713              
714             You can find documentation for this module with the perldoc command.
715              
716             perldoc WebService::Amazon::Support
717              
718              
719             You can also look for information at:
720              
721             =over 4
722              
723             =item * RT: CPAN's request tracker (report bugs here)
724              
725             L
726              
727             =item * AnnoCPAN: Annotated CPAN documentation
728              
729             L
730              
731             =item * CPAN Ratings
732              
733             L
734              
735             =item * Search CPAN
736              
737             L
738              
739             =back
740              
741              
742             =head1 ACKNOWLEDGEMENTS
743              
744             =head1 SEE ALSO
745              
746             perl(1), L, L, L, L, L, L, L
747              
748             =head1 LICENSE AND COPYRIGHT
749              
750             Copyright 2015 Matthew Cox.
751              
752             This program is free software; you can redistribute it and/or modify it
753             under the terms of the the Artistic License (2.0). You may obtain a
754             copy of the full license at:
755              
756             L
757              
758             Any use, modification, and distribution of the Standard or Modified
759             Versions is governed by this Artistic License. By using, modifying or
760             distributing the Package, you accept this license. Do not use, modify,
761             or distribute the Package, if you do not accept this license.
762              
763             If your Modified Version has been derived from a Modified Version made
764             by someone other than you, you are nevertheless required to ensure that
765             your Modified Version complies with the requirements of this license.
766              
767             This license does not grant you the right to use any trademark, service
768             mark, tradename, or logo of the Copyright Holder.
769              
770             This license includes the non-exclusive, worldwide, free-of-charge
771             patent license to make, have made, use, offer to sell, sell, import and
772             otherwise transfer the Package with respect to any patent claims
773             licensable by the Copyright Holder that are necessarily infringed by the
774             Package. If you institute patent litigation (including a cross-claim or
775             counterclaim) against any party alleging that the Package constitutes
776             direct or contributory patent infringement, then this Artistic License
777             to you shall terminate on the date that such litigation is filed.
778              
779             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
780             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
781             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
782             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
783             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
784             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
785             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
786             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
787              
788              
789             =cut
790              
791             1; # End of WebService::Amazon::Support
792             __END__