File Coverage

blib/lib/Net/Google/Drive/Simple/Core.pm
Criterion Covered Total %
statement 50 156 32.0
branch 5 52 9.6
condition 0 10 0.0
subroutine 15 25 60.0
pod 11 11 100.0
total 81 254 31.8


line stmt bran cond sub pod time code
1             ###########################################
2             ###########################################
3              
4             use strict;
5 9     9   3389 use warnings;
  9         15  
  9         191  
6 9     9   31  
  9         14  
  9         156  
7             use LWP::UserAgent ();
8 9     9   4545 use HTTP::Request ();
  9         339077  
  9         187  
9 9     9   67  
  9         18  
  9         101  
10             use File::MMagic ();
11 9     9   5265 use IO::File ();
  9         114050  
  9         189  
12 9     9   68  
  9         17  
  9         109  
13             use OAuth::Cmdline::CustomFile ();
14 9     9   3841 use OAuth::Cmdline::GoogleDrive ();
  9         751827  
  9         282  
15 9     9   3533  
  9         2119  
  9         174  
16             use Net::Google::Drive::Simple::Item ();
17 9     9   3363  
  9         18  
  9         176  
18             use JSON qw( from_json to_json );
19 9     9   45 use Log::Log4perl qw(:easy);
  9         17  
  9         57  
20 9     9   904  
  9         17  
  9         68  
21             our $VERSION = '3.00';
22              
23             ###########################################
24             ###########################################
25             my ( $class, %options ) = @_;
26              
27 9     9 1 41 my $oauth;
28              
29 9         19 if ( exists $options{custom_file} ) {
30             $oauth = OAuth::Cmdline::CustomFile->new( custom_file => $options{custom_file} );
31 9 50       32 }
32 0         0 else {
33             $oauth = OAuth::Cmdline::GoogleDrive->new();
34             }
35 9         107  
36             my $self = {
37             init_done => undef,
38 9         13154 oauth => $oauth,
39             error => undef,
40             %options,
41             };
42              
43             return bless $self, $class;
44             }
45 9         40  
46             ###########################################
47             ###########################################
48             my ( $self, $set ) = @_;
49              
50             if ( defined $set ) {
51 0     0 1 0 $self->{error} = $set;
52             }
53 0 0       0  
54 0         0 return $self->{error};
55             }
56              
57 0         0 ###########################################
58             ###########################################
59             my ( $self, $path ) = @_;
60              
61             if ( $self->{init_done} ) {
62             return 1;
63 0     0 1 0 }
64              
65 0 0       0 DEBUG "Testing API";
66 0         0 if ( !$self->api_test() ) {
67             LOGDIE "api_test failed";
68             }
69 0         0  
70 0 0       0 $self->{init_done} = 1;
71 0         0  
72             return 1;
73             }
74 0         0  
75             ###########################################
76 0         0 ###########################################
77             my ($self) = @_;
78              
79             my $url = $self->file_url( { maxResults => 1 } );
80              
81             my $ua = LWP::UserAgent->new();
82 0     0 1 0  
83             my $req = HTTP::Request->new(
84 0         0 GET => $url->as_string,
85             );
86 0         0 $req->header( $self->{oauth}->authorization_headers() );
87             DEBUG "Fetching $url";
88 0         0  
89             my $resp = $ua->request($req);
90              
91 0         0 if ( $resp->is_success() ) {
92 0         0 DEBUG "API tested OK";
93             return 1;
94 0         0 }
95              
96 0 0       0 $self->error( $resp->message() );
97 0         0  
98 0         0 ERROR "API error: ", $resp->message();
99             return 0;
100             }
101 0         0  
102             ###########################################
103 0         0 ###########################################
104 0         0 my ( $self, $data ) = @_;
105              
106             return Net::Google::Drive::Simple::Item->new($data);
107             }
108              
109             ###########################################
110 2     2 1 3 ###########################################
111             my ( $self, $req, $noinit ) = @_;
112 2         7  
113             my $ua = LWP::UserAgent->new();
114             my $resp;
115              
116             my $RETRIES = 3;
117             my $SLEEP_INTERVAL = 10;
118 0     0 1 0  
119             {
120 0         0 # refresh token if necessary
121 0         0 if ( !$noinit ) {
122             $self->init();
123 0         0 }
124 0         0  
125             DEBUG "Fetching ", $req->url->as_string();
126              
127             $resp = $ua->request($req);
128 0 0       0  
  0         0  
129 0         0 if ( !$resp->is_success() ) {
130             $self->error( $resp->message() );
131             warn "Failed with ", $resp->code(), ": ", $resp->message(), "\n";
132 0         0 if ( --$RETRIES >= 0 ) {
133             ERROR "Retrying in $SLEEP_INTERVAL seconds";
134 0         0 sleep $SLEEP_INTERVAL;
135             $self->{oauth}->token_expire();
136 0 0       0 $req->header( $self->{oauth}->authorization_headers() );
137 0         0 redo;
138 0         0 }
139 0 0       0 else {
140 0         0 ERROR "Out of retries.";
141 0         0 return $resp;
142 0         0 }
143 0         0 }
144 0         0  
145             DEBUG "Successfully fetched ", length( $resp->content() ), " bytes.";
146             }
147 0         0  
148 0         0 return $resp;
149             }
150              
151             ###########################################
152 0         0 ###########################################
153             my ( $self, $url, $post_data ) = @_;
154              
155 0         0 my @headers = ( $self->{'oauth'}->authorization_headers() );
156             my $verb = 'GET';
157             my $content;
158             if ($post_data) {
159             if ( ref $post_data eq 'ARRAY' ) {
160             ( $verb, $post_data ) = @{$post_data};
161 0     0 1 0 }
162             else {
163 0         0 $verb = 'POST';
164 0         0 }
165 0         0  
166 0 0       0 if ($post_data) {
167 0 0       0 push @headers, "Content-Type", "application/json";
168 0         0 }
  0         0  
169              
170             defined $post_data
171 0         0 and $content = to_json($post_data);
172             }
173              
174 0 0       0 my $req = HTTP::Request->new(
175 0         0 $verb,
176             $url->as_string(),
177             \@headers,
178 0 0       0 $content,
179             );
180              
181             my $resp = $self->http_loop($req);
182 0         0  
183             if ( $resp->is_error() ) {
184             $self->error( $resp->message() );
185             return;
186             }
187              
188             my $data = from_json( $resp->content() );
189 0         0  
190             return $data;
191 0 0       0 }
192 0         0  
193 0         0 ###########################################
194             ###########################################
195             my ( $self, $file ) = @_;
196 0         0  
197             # There don't seem to be great implementations of mimetype
198 0         0 # detection on CPAN, so just use this one for now.
199              
200             if ( !$self->{magic} ) {
201             $self->{magic} = File::MMagic->new();
202             }
203              
204 0     0 1 0 return $self->{magic}->checktype_filename($file);
205             }
206              
207             ###########################################
208             ###########################################
209 0 0       0 my ( $self, $data ) = @_;
210 0         0  
211             my $idx = 0;
212              
213 0         0 if ( !defined $data ) {
214             die "no data in item_iterator";
215             }
216              
217             return sub {
218             {
219 1     1 1 2 my $next_item = $data->{items}->[ $idx++ ];
220              
221 1         2 return if !defined $next_item;
222              
223 1 50       2 if ( $next_item->{labels}->{trashed} ) {
224 0         0 DEBUG "Skipping $next_item->{ title } (trashed)";
225             redo;
226             }
227              
228             return $next_item;
229 3     3   5 }
  3         6  
230             };
231 3 100       8 }
232              
233 2 50       42 ###########################################
234 0         0 ###########################################
235 0         0 my ( $self, $opts ) = @_;
236              
237             $opts = {} if !defined $opts;
238 2         19  
239             my $default_opts = {
240 1         5 maxResults => 3000,
241             };
242              
243             $opts = {
244             %$default_opts,
245             %$opts,
246 0     0 1   };
247              
248 0 0         my $url = URI->new( $self->{api_file_url} );
249             $url->query_form($opts);
250 0            
251             return $url;
252             }
253              
254 0           ###########################################
255             ###########################################
256             my ( $self, $file_id ) = @_;
257              
258             LOGDIE 'Deletion requires file_id' if ( !defined $file_id );
259 0            
260 0           my $url = URI->new( $self->{api_file_url} . "/$file_id" );
261              
262 0           return $self->http_json($url);
263             }
264              
265             ###########################################
266             ###########################################
267             my $self = shift;
268 0     0 1   my $filename = shift;
269             my @stat = stat $filename;
270 0 0         my $remaining = $stat[7];
271             my $blksize = $stat[11] || 4096;
272 0            
273             die "$filename not a readable file with fixed size"
274 0           unless -r $filename
275             and $remaining;
276              
277             my $fh = IO::File->new( $filename, 'r' )
278             or die "Could not open $filename: $!";
279             $fh->binmode;
280 0     0      
281 0           return sub {
282 0           my $buffer;
283 0            
284 0   0       # upon retries the file is closed and we must reopen it
285             unless ( $fh->opened ) {
286 0 0 0       $fh = IO::File->new( $filename, 'r' )
287             or die "Could not open $filename: $!";
288             $fh->binmode;
289             $remaining = $stat[7];
290 0 0         }
291              
292 0           unless ( my $read = $fh->read( $buffer, $blksize ) ) {
293             die "Error while reading upload content $filename ($remaining remaining) $!"
294             if $! and $remaining;
295 0     0     $fh->close # otherwise, we found EOF
296             or die "close of upload content $filename failed: $!";
297             $buffer ||= ''; # LWP expects an empty string on finish, read returns 0
298 0 0         }
299 0 0         $remaining -= length($buffer);
300             return $buffer;
301 0           };
302 0           }
303              
304             1;
305 0 0          
306 0 0 0        
307             =pod
308 0 0          
309             =head1 DESCRIPTION
310 0   0        
311             This is a baseclass that the V2 and V3 implementations of the module use.
312 0           You shouldn't use this class directly.
313 0            
314 0           =head1 METHODS
315              
316             These are methods that are shared among L<Net::Google::Drive::Simple::V2>
317             and L<Net::Google::Drive::Simple::V3>.
318              
319             You wouldn't normally use these methods.
320              
321             =head2 C<new>
322              
323             Parent method to create one L<Net::Google::Drive::Simple> object.
324              
325             =head2 C<error>
326              
327             Set and retrieve the current error.
328              
329             =head2 C<init>
330              
331             Internal initialization to setup the connection.
332              
333             =head2 C<api_test>
334              
335             Used at init time to check that the connection is correct.
336              
337             =head2 C<data_factory>
338              
339             Set up an object of L<Net::Google::Drive::Simple::Item>.
340              
341             =head2 C<http_json>
342              
343             Make an HTTP request with a body.
344              
345             =head2 C<http_loop>
346              
347             Perform a request.
348              
349             =head2 C<file_metadata>
350              
351             my $metadata_hash_ref = $gd->file_metadata($fileId);
352              
353             Return metadata about the file with the specified ID from Google Drive.
354              
355             =head2 C<file_url>
356              
357             Retrieve a file URL.
358              
359             =head2 C<file_mime_type>
360              
361             Retrieve the mime type of a file.
362              
363             =head2 C<item_iterator>
364              
365             Create an iterator over items.
366              
367             =head2 C<path_resolve>
368              
369             Resolve paths to the folder ID.
370