File Coverage

blib/lib/AWS/S3.pm
Criterion Covered Total %
statement 65 65 100.0
branch 7 8 87.5
condition n/a
subroutine 16 16 100.0
pod 4 5 80.0
total 92 94 97.8


line stmt bran cond sub pod time code
1              
2             package AWS::S3;
3              
4 6     6   4702460 use Moose;
  6         1201199  
  6         59  
5 6     6   50536 use Carp 'confess';
  6         15  
  6         560  
6 6     6   4020 use LWP::UserAgent::Determined;
  6         268635  
  6         237  
7 6     6   56 use HTTP::Response;
  6         20  
  6         203  
8 6     6   3869 use HTTP::Request::Common;
  6         19558  
  6         652  
9 6     6   3294 use IO::Socket::INET;
  6         156207  
  6         55  
10 6     6   3503 use Class::Load 'load_class';
  6         15  
  6         472  
11 6     6   3739 use Log::Any qw( $LOG );
  6         66945  
  6         35  
12              
13 6     6   19002 use AWS::S3::ResponseParser;
  6         118273  
  6         360  
14 6     6   4173 use AWS::S3::Owner;
  6         2619  
  6         339  
15 6     6   4316 use AWS::S3::Bucket;
  6         3499  
  6         58  
16              
17             our $VERSION = '2.00';
18              
19             has [qw/access_key_id secret_access_key/] => ( is => 'ro', isa => 'Str' );
20              
21             has 'session_token' => (
22             is => 'ro',
23             isa => 'Maybe[Str]',
24             lazy => 1,
25             default => sub { $ENV{AWS_SESSION_TOKEN} },
26             );
27              
28             has 'region' => (
29             is => 'ro',
30             isa => 'Maybe[Str]',
31             lazy => 1,
32             default => sub { $ENV{AWS_REGION} },
33             );
34              
35             has 'secure' => (
36             is => 'ro',
37             isa => 'Bool',
38             lazy => 1,
39             default => 0
40             );
41              
42             has 'endpoint' => (
43             is => 'ro',
44             isa => 'Str',
45             lazy => 1,
46             default => sub {
47             my ( $s ) = @_;
48              
49             if ( my $region = $s->region ) {
50             return "s3.$region.amazonaws.com"
51             } else {
52             return "s3.amazonaws.com"
53             }
54             },
55             );
56              
57             has 'ua' => (
58             is => 'ro',
59             isa => 'LWP::UserAgent',
60             default => sub { LWP::UserAgent::Determined->new }
61             );
62              
63             has 'honor_leading_slashes' => (
64             is => 'ro',
65             isa => 'Bool',
66             default => sub { 0 },
67             );
68              
69             sub request {
70 34     34 0 2155 my ( $s, $type, %args ) = @_;
71              
72 34         113 my $class = "AWS::S3::Request::$type";
73              
74 34         262 load_class( $class );
75 34         9778 return $class->new( %args, s3 => $s, type => $type );
76             } # end request()
77              
78             sub owner {
79 2     2 1 6496 my $s = shift;
80              
81 2         8 my $type = 'ListAllMyBuckets';
82 2         8 my $request = $s->request( $type );
83 2         12 my $response = $request->request();
84              
85             # Linode/Akamai E3 endpoints do not include the `xmlns` in
86             # ListAllMyBuckets, so use the localname to work with or
87             # without a declared XML namespace.
88 2         438 my $xml = $response->xml;
89 2         15 my ($node) = $xml->getElementsByLocalName('Owner');
90 2         139 return AWS::S3::Owner->new(
91             id => $node->getElementsByLocalName('ID')->string_value,
92             display_name => $node->getElementsByLocalName('DisplayName')->string_value,
93             );
94             } # end owner()
95              
96             sub buckets {
97 7     7 1 21 my ( $s ) = @_;
98              
99 7         35 my $type = 'ListAllMyBuckets';
100 7         35 my $request = $s->request( $type );
101 7         37 my $response = $request->request();
102              
103             # Linode/Akamai E3 endpoints do not include the `xmlns` in
104             # ListAllMyBuckets, so use the localname to work with or
105             # without a declared XML namespace.
106 7         1417 my $xml = $response->xml;
107 7         15 my @buckets = ();
108 7         118 foreach my $node ( $xml->getElementsByLocalName( 'Bucket' ) ) {
109 14         983 push @buckets,
110             AWS::S3::Bucket->new(
111             name => $node->getElementsByLocalName('Name')->string_value,
112             creation_date => $node->getElementsByLocalName('CreationDate')->string_value,
113             s3 => $s,
114             );
115             } # end foreach()
116              
117 7         190 $LOG->debug('Listed AWS buckets', { buckets => [map $_->name, @buckets] });
118 7         390 return @buckets;
119             } # end buckets()
120              
121             sub bucket {
122 5     5 1 17992 my ( $s, $name ) = @_;
123              
124 5 100       22 my ( $bucket ) = grep { $_->name eq $name } $s->buckets
  10         340  
125             or return;
126 3         121 $bucket;
127             } # end bucket()
128              
129             sub add_bucket {
130 3     3 1 8052 my ( $s, %args ) = @_;
131              
132 3         10 my $type = 'CreateBucket';
133             my $request = $s->request(
134             $type,
135             bucket => $args{name},
136             (
137             $args{location} ? ( location => $args{location} )
138 3 50       118 : $s->region ? ( location => $s->region )
    100          
139             : ()
140             ),
141             );
142 3         33 my $response = $request->request();
143              
144 3 100       795 if ( my $msg = $response->friendly_error() ) {
145 2         152 die $msg;
146             } # end if()
147              
148 1         9 return $s->bucket( $args{name} );
149             } # end add_bucket()
150              
151             __PACKAGE__->meta->make_immutable;
152              
153             __END__
154              
155             =pod
156              
157             =head1 NAME
158              
159             AWS::S3 - Lightweight interface to Amazon S3 (Simple Storage Service)
160              
161             =for html
162             <a href='https://travis-ci.org/leejo/AWS-S3?branch=master'><img src='https://travis-ci.org/leejo/AWS-S3.svg?branch=master' /></a>
163             <a href='https://coveralls.io/r/leejo/AWS-S3?branch=master'><img src='https://coveralls.io/repos/leejo/AWS-S3/badge.png?branch=master' alt='Coverage Status' /></a>
164              
165             =head1 SYNOPSIS
166              
167             use AWS::S3;
168              
169             my $s3 = AWS::S3->new(
170             access_key_id => 'E654SAKIASDD64ERAF0O',
171             secret_access_key => 'LgTZ25nCD+9LiCV6ujofudY1D6e2vfK0R4GLsI4H',
172             session_token => 'IQob3JpJZ2luXJ2VjJEL7//////////wE...',
173             region => 'eu-west-1', # set to relevant AWS region
174             honor_leading_slashes => 0, # set to allow leading slashes in bucket names, defaults to 0
175             );
176              
177             # Add a bucket:
178             my $bucket = $s3->add_bucket(
179             name => 'foo-bucket',
180             );
181              
182             # Set the acl:
183             $bucket->acl( 'private' );
184              
185             # Add a file:
186             my $new_file = $bucket->add_file(
187             key => 'foo/bar.txt',
188             contents => \'This is the contents of the file',
189             );
190              
191             # You can also set the contents with a coderef:
192             # Coderef should eturn a reference, not the actual string of content:
193             $new_file = $bucket->add_file(
194             key => 'foo/bar.txt',
195             contents => sub { return \"This is the contents" }
196             );
197              
198             # Get the file:
199             my $same_file = $bucket->file( 'foo/bar.txt' );
200              
201             # Get the contents:
202             my $scalar_ref = $same_file->contents;
203             print $$scalar_ref;
204              
205             # Update the contents with a scalar ref:
206             $same_file->contents( \"New file contents" );
207              
208             # Update the contents with a code ref:
209             $same_file->contents( sub { return \"New file contents" } );
210              
211             # Delete the file:
212             $same_file->delete();
213              
214             # Iterate through lots of files:
215             my $iterator = $bucket->files(
216             page_size => 100,
217             page_number => 1,
218             );
219             while( my @files = $iterator->next_page )
220             {
221             warn "Page number: ", $iterator->page_number, "\n";
222             foreach my $file ( @files )
223             {
224             warn "\tFilename (key): ", $file->key, "\n";
225             warn "\tSize: ", $file->size, "\n";
226             warn "\tETag: ", $file->etag, "\n";
227             warn "\tContents: ", ${ $file->contents }, "\n";
228             }# end foreach()
229             }# end while()
230              
231             # You can't delete a bucket until it's empty.
232             # Empty a bucket like this:
233             while( my @files = $iterator->next_page )
234             {
235             map { $_->delete } @files;
236              
237             # Return to page 1:
238             $iterator->page_number( 1 );
239             }# end while()
240              
241             # Now you can delete the bucket:
242             $bucket->delete();
243              
244             =head1 DESCRIPTION
245              
246             AWS::S3 attempts to provide an alternate interface to the Amazon S3 Simple Storage Service.
247              
248             B<Disclaimer:> Several portions of AWS::S3 have been adopted from L<Net::Amazon::S3>.
249              
250             B<NOTE:> AWS::S3 is NOT a drop-in replacement for L<Net::Amazon::S3>.
251              
252             B<TODO:> CloudFront integration.
253              
254             =head1 CONSTRUCTOR
255              
256             Call C<new()> with the following parameters.
257              
258             =head2 access_key_id
259              
260             Required. String.
261              
262             Provided by Amazon, this is your access key id.
263              
264             =head2 secret_access_key
265              
266             Required. String.
267              
268             Provided by Amazon, this is your secret access key.
269              
270             =head2 secure
271              
272             Optional. Boolean.
273              
274             Default is C<0>
275              
276             =head2 endpoint
277              
278             Optional. String.
279              
280             Default is C<s3.amazonaws.com>
281              
282             =head2 ua
283              
284             Optional. Should be an instance of L<LWP::UserAgent> or a subclass of it.
285              
286             Defaults to creating a new instance of L<LWP::UserAgent::Determined>
287              
288             =head2 honor_leading_slashes
289              
290             Optional. Boolean to set if bucket names should include any leading slashes
291             when sent to S3 - defaults to zero
292              
293             =head1 PUBLIC PROPERTIES
294              
295             =head2 access_key_id
296              
297             String. Read-only
298              
299             =head2 secret_access_key
300              
301             String. Read-only.
302              
303             =head2 secure
304              
305             Boolean. Read-only.
306              
307             =head2 endpoint
308              
309             String. Read-only.
310              
311             =head2 ua
312              
313             L<LWP::UserAgent> object. Read-only.
314              
315             =head2 owner
316              
317             L<AWS::S3::Owner> object. Read-only.
318              
319             =head1 PUBLIC METHODS
320              
321             =head2 buckets
322              
323             Returns an array of L<AWS::S3::Bucket> objects.
324              
325             =head2 bucket( $name )
326              
327             Returns the L<AWS::S3::Bucket> object matching C<$name> if found.
328              
329             Returns nothing otherwise.
330              
331             =head2 add_bucket( name => $name, location => 'us-west-1' )
332              
333             Attempts to create a new bucket with the name provided. The location parameter is optional
334             and, as per the AWS docs, will default to "us-east-1".
335              
336             On success, returns the new L<AWS::S3::Bucket>
337              
338             On failure, dies with the error message.
339              
340             See L<AWS::S3::Bucket> for details on how to use buckets (and access their files).
341              
342             =head1 ENVIRONMENT VARIABLES
343              
344             =head2 AWS_S3_DEBUG
345              
346             If set, will print out debugging information to C<STDERR>.
347              
348             =head1 SEE ALSO
349              
350             L<The Amazon S3 API Documentation|http://docs.amazonwebservices.com/AmazonS3/latest/API/>
351              
352             L<AWS::S3::Bucket>
353              
354             L<AWS::S3::File>
355              
356             L<AWS::S3::FileIterator>
357              
358             L<AWS::S3::Owner>
359              
360             =head1 AUTHOR
361              
362             Originally John Drago C<jdrago_999@yahoo.com>, currently maintained by Lee Johnson (LEEJO) C<leejo@cpan.org>
363             with contributions from Evan Carroll, Robin Clarke, Ulrich Kautz, simbabque, Dave Rolsky
364              
365             =head1 LICENSE
366              
367             This software is Free software and may be used and redistributed under the same
368             terms as any version of perl itself.
369              
370             Copyright John Drago 2011 all rights reserved.
371              
372             =cut
373