File Coverage

blib/lib/Net/Google/Drive/Simple/V3.pm
Criterion Covered Total %
statement 106 546 19.4
branch 49 346 14.1
condition 11 255 4.3
subroutine 23 84 27.3
pod 53 53 100.0
total 242 1284 18.8


line stmt bran cond sub pod time code
1             ###########################################
2             ###########################################
3              
4             use strict;
5 10     10   1384795 use warnings;
  10         56  
  10         245  
6 10     10   40 use parent qw< Net::Google::Drive::Simple::Core >;
  10         21  
  10         223  
7 10     10   53  
  10         17  
  10         49  
8             use URI ();
9 10     10   538 use URI::QueryParam ();
  10         22  
  10         152  
10 10     10   3988 use File::Basename ();
  10         6745  
  10         190  
11 10     10   53 use JSON qw< from_json >;
  10         18  
  10         152  
12 10     10   40 use Log::Log4perl qw(:easy);
  10         22  
  10         54  
13 10     10   812  
  10         20  
  10         60  
14             use constant {
15             'HTTP_METHOD_GET' => 'GET',
16 10         2644 'HTTP_METHOD_POST' => 'POST',
17             'HTTP_METHOD_PUT' => 'PUT',
18             'HTTP_METHOD_PATCH' => 'PATCH',
19             'HTTP_METHOD_DELETE' => 'DELETE',
20              
21             'TYPE_STRING' => 'string',
22             'TYPE_INTEGER' => 'integer',
23             'TYPE_LONG' => 'long',
24             'TYPE_BOOLEAN' => 'boolean',
25             'TYPE_OBJECT' => 'object',
26             'TYPE_BYTES' => 'bytes',
27              
28             'SIZE_256K' => 262_144, # default chunk size
29             'SIZE_5MB' => 5_242_880, # low limit
30             'SIZE_5120GB' => 5_497_558_138_880 # high limit
31             };
32 10     10   6990  
  10         39  
33             use constant { 'DEF_CHUNK_SIZE' => SIZE_5MB() * 2 }; # 10 MB
34 10     10   61  
  10         20  
  10         82999  
35             our $VERSION = '3.01';
36              
37             # TODO:
38             # * requestId are random UUIDs that we should probably generate (drive_create)
39             # * Support for Request Body values
40             # * Support fields format: https://developers.google.com/drive/api/guides/fields-parameter
41             # * Add validation for path parameters
42             # * Specific types (like ISO 639-1)
43             # * Extended upload support: https://developers.google.com/drive/api/guides/manage-uploads
44              
45             # These are only the defaults
46             my %default_deprecated_param_names = (
47             'corpus' => 'corpora',
48             'includeTeamDriveItems' => 'includeItemsFromAllDrives',
49             'supportsTeamDrives' => 'supportsAllDrives',
50             'teamDriveId' => 'driveId',
51             );
52              
53             ###########################################
54             ###########################################
55             my ( $class, %options ) = @_;
56             return $class->SUPER::new(
57 8     8 1 707 %options,
58 8         81 'api_base_url' => 'https://www.googleapis.com/drive/v3/',
59             'api_upload_url' => 'https://www.googleapis.com/upload/drive/v3/files',
60              
61             # to make sure api_test() works:
62             'api_file_url' => 'https://www.googleapis.com/drive/v3/files',
63             );
64             }
65              
66             ###########################################
67             ###########################################
68             my ( $self, $method, $param_data, $param_values ) = @_;
69              
70             my %validation_types = (
71 37     37   22105 TYPE_STRING() => sub { defined and length },
72             TYPE_INTEGER() => sub { defined and m{^[0-9]+$}xms },
73             TYPE_LONG() => sub { defined and m{^[0-9]+$}xms },
74 5 100   5   31 TYPE_BOOLEAN() => sub { defined and m{^[01]$}xms },
75 8 100   8   74 TYPE_OBJECT() => sub { ref eq 'HASH' },
76 4 100   4   51 TYPE_BYTES() => sub { defined and length },
77 6 100   6   75 );
78 7     7   33  
79 3 100   3   21 foreach my $param_name ( sort keys %{$param_values} ) {
80 37         270 if ( !exists $param_data->{$param_name} ) {
81             LOGDIE("[$method] Parameter name '$param_name' does not exist");
82 37         59 }
  37         114  
83 35 100       89 }
84 1         8  
85             foreach my $param_name ( sort keys %{$param_data} ) {
86             my ( $param_type, $is_required ) = @{ $param_data->{$param_name} };
87             my $param_value = $param_values->{$param_name};
88 36         68  
  36         77  
89 36         46 # You did not provide a parameter that is needed
  36         83  
90 36         65 if ( !exists $param_values->{$param_name} ) {
91             $is_required
92             and LOGDIE("[$method] Parameter '$param_name' is required");
93 36 100       61  
94 2 100       20 next;
95             }
96              
97 1         3 my $validation_cb = $validation_types{$param_type};
98             if ( !$validation_cb ) {
99             LOGDIE("[$method] Parameter type '$param_type' does not exist");
100 34         50 }
101 34 100       57  
102 1         7 local $_ = $param_value;
103             my $success = $validation_cb->()
104             or LOGDIE("[$method] Parameter type '$param_name' does not validate as '$param_type'");
105 33         59 }
106 33 100       50  
107             return 1;
108             }
109              
110 14         75 ###########################################
111             ###########################################
112             my ( $self, $method, $options ) = @_;
113              
114             if ( my $perm_for_view = $options->{'includePermissionsForView'} ) {
115             $perm_for_view eq 'published'
116 21     21   15590 or LOGDIE("[$method] Parameter 'includePermissionsForView' must be: published");
117             }
118 21 100       47  
119 2 100       11 my $page_size = $options->{'pageSize'};
120             if ( defined $page_size ) {
121             $page_size >= 1 && $page_size <= 1_000
122             or LOGDIE("[$method] Parameter 'pageSize' must be: 1 to 1000");
123 20         30 }
124 20 100       39  
125 8 100 100     37 if ( my $upload_type = $options->{'uploadType'} ) {
126             $upload_type =~ /^( media | multipart | resumable )$/xms
127             or LOGDIE("[$method] Parameter 'uploadType' must be: media|multipart|resumable");
128             }
129 16 100       33  
130 6 100       40 return 1;
131             }
132              
133             ###########################################
134 13         28 ###########################################
135             my ( $self, $path, $options ) = @_;
136             my $uri = URI->new( $path =~ /^http/xms ? $path : $self->{'api_base_url'} . $path );
137              
138             $options && %{$options}
139             and $uri->query_form($options);
140 7     7   6800  
141 7 100       59 return $uri;
142             }
143 7 100 100     12797  
  5         32  
144             ###########################################
145             ###########################################
146 7         378 my ( $self, $method, $info, $options ) = @_;
147              
148             foreach my $dep_name ( sort keys %{$options} ) {
149             my $alt = $info->{'deprecated_param_names'}{$dep_name}
150             || $default_deprecated_param_names{$dep_name};
151              
152 27     27   18909 if ($alt) {
153             WARN("[$method] Parameter name '$dep_name' is deprecated, use '$alt' instead");
154 27         35 }
  27         62  
155             }
156 26   100     82  
157             return;
158 26 100       50 }
159 13         43  
160             ###########################################
161             ###########################################
162             my ( $self, $options, $body_param_names ) = @_;
163 27         215  
164             my $body_options = {
165             map +( exists $options->{$_} ? ( $_ => delete $options->{$_} ) : () ),
166             @{$body_param_names},
167             };
168              
169 6     6   5516 keys %{$body_options} == 0
170             and return;
171              
172             return $body_options;
173 6 50       9 }
  6         25  
174              
175             ###########################################
176 6 100       11 ###########################################
  6         26  
177             my ( $self, $info, $options ) = @_;
178             my $method = $info->{'method_name'};
179 3         38  
180             # We yank out all the body parameters so we don't validate them
181             # We reuse the same key to store the key + value this time (instead of which keys to use in options)
182             # TODO: Support body parameter validation
183             my $body_parameters = delete $info->{'body_parameters'};
184             $info->{'body_parameters'} //= $self->_prepare_body_options( $options, $body_parameters );
185 5     5   20748  
186 5         9 # We validate the options left
187             $self->_validate_param_type( $method, $info->{'query_parameters'}, $options );
188              
189             # We might have a more complicated type checking specified
190             if ( my $param_check = $info->{'parameter_checks'} ) {
191 5         8 foreach my $name ( sort keys %{$param_check} ) {
192 5   66     29 defined $options->{$name}
193             or next;
194              
195 5         18 my $cb = $param_check->{$name};
196             ref $cb eq 'CODE'
197             or LOGDIE("[$method] Parameter '$name' missing validation callback");
198 5 100       12  
199 1         2 local $_ = $options->{$name};
  1         3  
200 3 100       9 my $error_str = $cb->();
201             $error_str
202             and LOGDIE("[$method] Parameter '$name' failed validation: $error_str");
203 2         3 }
204 2 50       5 }
205              
206             # We check for deprecated parameters (from a list of known deprecated parameters)
207 2         4 $self->_handle_deprecated_params( $method, $info, $options );
208 2         4  
209 2 100       860 # We handle some more specific validation rules (from a list of known parameters)
210             $self->_handle_complex_types( $method, $options );
211              
212             $self->init();
213              
214             # We generate the URI path
215 4         12 my $uri = $self->_generate_uri( $info->{'path'}, $options );
216             my $req = $self->_generate_request( $uri, $info );
217              
218 4         9 if ( $info->{'return_http_request'} ) {
219             return $req;
220 4         10 }
221              
222             return $self->_make_request( $req, $info->{'return_http_response'} );
223 4         16 }
224 4         15  
225             # --- about
226 4 50       11  
227 0         0 ###########################################
228             ###########################################
229             my ( $self, $options ) = @_;
230 4         14  
231             ref $options eq 'HASH'
232             or LOGDIE('about() missing parameters');
233              
234             my $info = {
235             'query_parameters' => {
236             'fields' => [ TYPE_STRING(), 1 ],
237             },
238 0     0 1   'path' => 'about',
239             'method_name' => 'about',
240 0 0         'http_method' => HTTP_METHOD_GET(),
241             };
242              
243 0           return $self->_handle_api_method( $info, $options );
244             }
245              
246             # --- changes
247              
248             ###########################################
249             ###########################################
250             my ( $self, $options ) = @_;
251              
252 0           $options //= {};
253              
254             my $info = {
255             'query_parameters' => {
256             'driveId' => [ TYPE_STRING(), 0 ],
257             'fields' => [ TYPE_STRING(), 0 ],
258             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
259             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], # Deprecated
260 0     0 1   'teamDriveId' => [ TYPE_STRING(), 0 ], # Deprecated
261             },
262 0   0        
263             'path' => 'changes/startPageToken',
264 0           'http_method' => HTTP_METHOD_GET(),
265             'method_name' => 'getStartPageToken',
266             };
267              
268             return $self->_handle_api_method( $info, $options );
269             }
270              
271             ###########################################
272             ###########################################
273             my ( $self, $options ) = @_;
274              
275             ref $options eq 'HASH'
276             or LOGDIE('changes() missing parameters');
277              
278 0           my $info = {
279             'query_parameters' => {
280             'pageToken' => [ TYPE_STRING(), 1 ],
281             'driveId' => [ TYPE_STRING(), 0 ],
282             'fields' => [ TYPE_STRING(), 0 ],
283             'includeCorpusRemovals' => [ TYPE_BOOLEAN(), 0 ],
284 0     0 1   'includeItemsFromAllDrives' => [ TYPE_BOOLEAN(), 0 ],
285             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
286 0 0         'includeRemoved' => [ TYPE_BOOLEAN(), 0 ],
287             'includeTeamDriveItems' => [ TYPE_BOOLEAN(), 0 ],
288             'pageSize' => [ TYPE_INTEGER(), 0 ],
289             'restrictToMyDrive' => [ TYPE_BOOLEAN(), 0 ],
290             'spaces' => [ TYPE_STRING(), 0 ],
291             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
292             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
293             'teamDriveId' => [ TYPE_STRING(), 0 ],
294             },
295              
296             'parameter_checks' => {
297             'spaces' => sub {
298             /^( drive | appDataFolder | photos )$/xms
299             or return 'must be: drive|appDataFolder|photos';
300              
301             return 0;
302             },
303             },
304              
305             'path' => 'changes',
306             'http_method' => HTTP_METHOD_GET(),
307             'method_name' => 'changes',
308             };
309 0 0   0      
310             return $self->_handle_api_method( $info, $options );
311             }
312 0            
313             ###########################################
314             ###########################################
315             my ( $self, $options ) = @_;
316 0            
317             $options //= {};
318              
319             my $info = {
320             'query_parameters' => {
321 0           'fields' => [ TYPE_STRING(), 0 ],
322             },
323              
324             'body_parameters' => [
325             qw<
326             kind
327 0     0 1   id
328             resourceId
329 0   0       resourceUri
330             token
331 0           expiration
332             type
333             address
334             payload
335             params
336             >
337             ],
338              
339             'path' => 'changes/watch',
340             'http_method' => HTTP_METHOD_POST(),
341             'method_name' => 'watch_changes',
342             };
343              
344             $options->{'kind'} = 'api#channel';
345              
346             return $self->_handle_api_method( $info, $options );
347             }
348              
349             # --- channels
350              
351             ###########################################
352             ###########################################
353             my ( $self, $options ) = @_;
354              
355             ref $options eq 'HASH'
356 0           or LOGDIE('stop_channels() missing parameters');
357              
358 0           my $info = {
359             'body_parameters' => [
360             qw<
361             kind
362             id
363             resourceId
364             resourceUri
365             token
366 0     0 1   expiration
367             type
368 0 0         address
369             payload
370             params
371             >
372             ],
373              
374             'parameter_checks' => {
375             'type' => sub {
376             m{^web_?hook$}xms
377             or return 'must be: webhook|web_hook';
378              
379             return 0;
380             },
381             },
382              
383             'path' => 'channels/stop',
384             'http_method' => HTTP_METHOD_POST(),
385             'method_name' => 'stop_channels',
386             };
387              
388             $options->{'kind'} = 'api#channel';
389 0 0   0      
390             return $self->_handle_api_method( $info, $options );
391             }
392 0            
393             # --- comments
394              
395             ###########################################
396 0           ###########################################
397             my ( $self, $fileId, $options ) = @_;
398              
399             defined $fileId && length $fileId
400             or LOGDIE('create_comment() missing file ID');
401 0            
402             ref $options eq 'HASH'
403 0           or LOGDIE('create_comment() missing parameters');
404              
405             my $info = {
406             'query_parameters' => {
407             'fields' => [ TYPE_STRING(), 1 ],
408             },
409              
410             'body_parameters' => [
411 0     0 1   qw<
412             content
413 0 0 0       anchor
414             quotedFileContent
415             >
416 0 0         ],
417              
418             'path' => "files/$fileId/comments",
419 0           'method_name' => 'create_comment',
420             'http_method' => HTTP_METHOD_POST(),
421             };
422              
423             return $self->_handle_api_method( $info, $options );
424             }
425              
426             ###########################################
427             ###########################################
428             my ( $self, $fileId, $commentId ) = @_;
429              
430             defined $fileId && length $fileId
431             or LOGDIE('create_comment() missing file ID');
432              
433             defined $commentId && length $commentId
434             or LOGDIE('create_comment() missing comment ID');
435              
436             my $info = {
437 0           'path' => "files/$fileId/comments/$commentId",
438             'method_name' => 'delete_comment',
439             'http_method' => HTTP_METHOD_DELETE(),
440             };
441              
442             return $self->_handle_api_method( $info, {} );
443 0     0 1   }
444              
445 0 0 0       ###########################################
446             ###########################################
447             my ( $self, $fileId, $commentId, $options ) = @_;
448 0 0 0        
449             defined $fileId && length $fileId
450             or LOGDIE('get_comment() missing file ID');
451 0            
452             defined $commentId && length $commentId
453             or LOGDIE('get_comment() missing comment ID');
454              
455             ref $options eq 'HASH'
456             or LOGDIE('get_comment() missing parameters');
457 0            
458             my $info = {
459             'query_parameters' => {
460             'fields' => [ TYPE_STRING(), 1 ],
461             'includeDeleted' => [ TYPE_BOOLEAN(), 0 ],
462             },
463 0     0 1    
464             'path' => "files/$fileId/comments/$commentId",
465 0 0 0       'method_name' => 'get_comment',
466             'http_method' => HTTP_METHOD_GET(),
467             };
468 0 0 0        
469             return $self->_handle_api_method( $info, $options );
470             }
471 0 0          
472             ###########################################
473             ###########################################
474 0           my ( $self, $fileId, $options ) = @_;
475              
476             defined $fileId && length $fileId
477             or LOGDIE('comments() missing file ID');
478              
479             ref $options eq 'HASH'
480             or LOGDIE('comments() missing parameters');
481              
482             my $info = {
483             'query_parameters' => {
484             'fields' => [ TYPE_STRING(), 1 ],
485 0           'includeDeleted' => [ TYPE_BOOLEAN(), 0 ],
486             'pageSize' => [ TYPE_INTEGER(), 0 ],
487             'pageToken' => [ TYPE_STRING(), 0 ],
488             'startModifiedTime' => [ TYPE_STRING(), 0 ],
489             },
490              
491 0     0 1   'path' => "files/$fileId/comments",
492             'method_name' => 'comments',
493 0 0 0       'http_method' => HTTP_METHOD_GET(),
494             };
495              
496 0 0         return $self->_handle_api_method( $info, $options );
497             }
498              
499 0           ###########################################
500             ###########################################
501             my ( $self, $fileId, $commentId, $options ) = @_;
502              
503             defined $fileId && length $fileId
504             or LOGDIE('update_comment() missing file ID');
505              
506             defined $commentId && length $commentId
507             or LOGDIE('update_comment() missing comment ID');
508              
509             ref $options eq 'HASH'
510             or LOGDIE('update_comment() missing parameters');
511              
512             my $info = {
513 0           'query_parameters' => {
514             'fields' => [ TYPE_STRING(), 1 ],
515             },
516              
517             'body_parameters' => [
518             qw<
519 0     0 1   content
520             >
521 0 0 0       ],
522              
523             'path' => "files/$fileId/comments/$commentId",
524 0 0 0       'method_name' => 'update_comment',
525             'http_method' => HTTP_METHOD_PATCH(),
526             };
527 0 0          
528             return $self->_handle_api_method( $info, $options );
529             }
530 0            
531             # --- files
532              
533             ###########################################
534             ###########################################
535             my ( $self, $fileId, $options ) = @_;
536              
537             defined $fileId && length $fileId
538             or LOGDIE('copy_file() missing file ID');
539              
540             ref $options eq 'HASH'
541             or LOGDIE('copy_file() missing parameters');
542              
543             # TODO: ocrLanguage is ISO 639-1 code
544              
545             my $info = {
546 0           'query_parameters' => {
547             'fields' => [ TYPE_STRING(), 0 ],
548             'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ],
549             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
550             'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
551             'ocrLanguage' => [ TYPE_STRING(), 0 ],
552             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
553             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
554 0     0 1   },
555              
556 0 0 0       'body_parameters' => [
557             qw<
558             appProperties
559 0 0         contentHints
560             contentRestrictions
561             copyRequiresWriterPermission
562             description
563             id
564 0           mimeType
565             modifiedTime
566             name
567             parents
568             properties
569             starred
570             viewedByMeTime
571             writersCanShare
572             >
573             ],
574              
575             'path' => "files/$fileId/copy",
576             'method_name' => 'copy_file',
577             'http_method' => HTTP_METHOD_POST(),
578             };
579              
580             return $self->_handle_api_method( $info, $options );
581             }
582              
583             # Metadata only
584             ###########################################
585             ###########################################
586             my ( $self, $options ) = @_;
587              
588             ref $options eq 'HASH'
589             or LOGDIE('create_file() missing parameters');
590              
591             my $info = {
592             'query_parameters' => {
593             'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
594             'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ],
595             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
596             'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
597             'ocrLanguage' => [ TYPE_STRING(), 0 ],
598             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
599 0           'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
600             'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ]
601             },
602              
603             'body_parameters' => [
604             qw<
605             appProperties
606 0     0 1   contentHints
607             contentRestrictions
608 0 0         copyRequiresWriterPermission
609             createdTime
610             description
611 0           folderColorRgb
612             id
613             mimeType
614             modifiedTime
615             name
616             originalFilename
617             parents
618             properties
619             shortcutDetails.targetId
620             starred
621             viewedByMeTime
622             writersCanShare
623             >
624             ],
625              
626             'path' => 'files',
627             'method_name' => 'create_file',
628             'http_method' => HTTP_METHOD_POST(),
629             };
630              
631             if ( defined $options->{'enforceSingleParent'} ) {
632             LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported");
633             }
634              
635             return $self->_handle_api_method( $info, $options );
636             }
637              
638             # Uploading file (uploadType=media)
639             ###########################################
640             ###########################################
641             my ( $self, $file, $options ) = @_;
642              
643             defined $file && length $file
644             or LOGDIE('upload_media_file() missing file');
645              
646             -r $file
647             or LOGDIE("upload_media_file() received non-existent/unreadable file: $file");
648              
649             my $size = -s $file;
650             $size <= SIZE_5MB()
651 0 0         or LOGDIE("upload_media_file() has a limit of 5M, '$file' is bigger");
652 0            
653             $options //= {};
654             $options->{'uploadType'} = 'media';
655 0            
656             my $mimeType = delete $options->{'mimeType'} // $self->file_mime_type($file);
657              
658             my $info = {
659             'query_parameters' => {
660             'uploadType' => [ TYPE_STRING(), 1 ],
661             'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
662 0     0 1   'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ],
663             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
664 0 0 0       'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
665             'ocrLanguage' => [ TYPE_STRING(), 0 ],
666             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
667 0 0         'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
668             'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ]
669             },
670 0            
671 0 0         'path' => $self->{'api_upload_url'},
672             'method_name' => 'upload_media_file',
673             'http_method' => HTTP_METHOD_POST(),
674 0   0       'body_content' => $self->_content_sub($file),
675 0           'extra_headers' => [
676             'Content-Type' => $mimeType,
677 0   0       'Content-Length' => $size,
678             ],
679             };
680              
681             if ( defined $options->{'enforceSingleParent'} ) {
682             LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported");
683             }
684              
685             # Since a file upload can take a long time, refresh the token
686             # just in case.
687             $self->{'oauth'}->token_expire();
688              
689             return $self->_handle_api_method( $info, $options );
690             }
691              
692 0           ###########################################
693             ###########################################
694             my ( $self, $file, $options ) = @_;
695              
696             defined $file && length $file
697             or LOGDIE('upload_multipart_file() missing file');
698              
699             -r $file
700             or LOGDIE("upload_multipart_file() received non-existent/unreadable file: $file");
701              
702 0 0         my $size = -s $file;
703 0           $size <= SIZE_5MB()
704             or LOGDIE("upload_multipart_file() has a limit of 5M, '$file' is bigger");
705              
706             $options //= {};
707              
708 0           $options->{'name'} //= File::Basename::basename($file);
709             $options->{'mimeType'} //= $self->file_mime_type($file);
710 0           $options->{'uploadType'} = 'multipart';
711              
712             # TODO: Wouldn't it be great to support Chunking with a callback?
713             my $file_content;
714             {
715             open my $fh, '<', $file
716 0     0 1   or LOGDIE("File '$file' cannot be open for reading");
717              
718 0 0 0       undef $/;
719             $file_content = <$fh>;
720              
721 0 0         close $fh
722             or LOGDIE("File '$file' cannot be closed after reading");
723             }
724 0            
725 0 0         my $info = {
726             'query_parameters' => {
727             'uploadType' => [ TYPE_STRING(), 1 ],
728 0   0       'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
729             'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ],
730 0   0       'includePermissionsForView' => [ TYPE_STRING(), 0 ],
731 0   0       'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
732 0           'ocrLanguage' => [ TYPE_STRING(), 0 ],
733             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
734             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
735 0           'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ]
736             },
737 0 0          
  0            
738             'body_parameters' => [
739             qw<
740 0           appProperties
741 0           contentHints
742             contentRestrictions
743 0 0         copyRequiresWriterPermission
744             createdTime
745             description
746             folderColorRgb
747             id
748             mimeType
749             modifiedTime
750             name
751             originalFilename
752             parents
753             properties
754             shortcutDetails.targetId
755             starred
756             viewedByMeTime
757             writersCanShare
758             >
759             ],
760              
761             'path' => $self->{'api_upload_url'},
762             'method_name' => 'upload_multipart_file',
763             'http_method' => HTTP_METHOD_POST(),
764             'multipart' => 1,
765             'body_content' => $file_content,
766             };
767              
768             if ( defined $options->{'enforceSingleParent'} ) {
769             LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported");
770             }
771              
772             # Since a file upload can take a long time, refresh the token
773             # just in case.
774             $self->{'oauth'}->token_expire();
775              
776             return $self->_handle_api_method( $info, $options );
777             }
778              
779             ###########################################
780             ###########################################
781             my ( $self, $file, $options ) = @_;
782              
783 0           defined $file && length $file
784             or LOGDIE('create_resumable_upload_for() missing file');
785              
786             -r $file
787             or LOGDIE("create_resumable_upload_for() received non-existent/unreadable file: $file");
788              
789             my $size = -s $file;
790 0 0         $size <= SIZE_5120GB()
791 0           or LOGDIE("create_resumavble_upload_for() has a limit of 5120G, '$file' is bigger");
792              
793             $options //= {};
794              
795             $options->{'name'} //= File::Basename::basename($file);
796 0           $options->{'mimeType'} //= $self->file_mime_type($file);
797             $options->{'uploadType'} = 'resumable';
798 0            
799             my @extra_headers = (
800             'X-Upload-Content-Type' => $options->{'mimeType'},
801             'X-Upload-Content-Length' => $size,
802             );
803              
804 0     0 1   # Do we have metadata other than uploadType?
805             if ( keys %{$options} > 1 ) {
806 0 0 0       push @extra_headers, 'Content-Type' => 'application/json; charset=UTF-8';
807             }
808              
809 0 0         my $info = {
810             'query_parameters' => {
811             'uploadType' => [ TYPE_STRING(), 1 ],
812 0           'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
813 0 0         'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ],
814             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
815             'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
816 0   0       'ocrLanguage' => [ TYPE_STRING(), 0 ],
817             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
818 0   0       'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
819 0   0       'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ]
820 0           },
821              
822             'body_parameters' => [
823 0           qw<
824             appProperties
825             contentHints
826             contentRestrictions
827             copyRequiresWriterPermission
828 0 0         createdTime
  0            
829 0           description
830             folderColorRgb
831             id
832             mimeType
833             modifiedTime
834             name
835             originalFilename
836             parents
837             properties
838             shortcutDetails.targetId
839             starred
840             viewedByMeTime
841             writersCanShare
842             >
843             ],
844              
845             'path' => $self->{'api_upload_url'},
846             'method_name' => 'create_resumable_upload',
847             'http_method' => HTTP_METHOD_POST(),
848             'extra_headers' => \@extra_headers,
849             'resumable' => 1,
850             'return_http_response' => 1,
851             };
852              
853             if ( defined $options->{'enforceSingleParent'} ) {
854             LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported");
855             }
856              
857             # Since a file upload can take a long time, refresh the token
858             # just in case.
859             $self->{'oauth'}->token_expire();
860              
861             my $response = $self->_handle_api_method( $info, $options );
862             $response->is_success()
863             or return;
864              
865             my $location = $response->header('Location');
866             return $location || '';
867             }
868 0            
869             ###########################################
870             ###########################################
871             my ( $self, $options ) = @_;
872              
873             $options //= {};
874             $options->{'uploadType'} = 'resumable';
875              
876 0 0         my @extra_headers;
877 0           if ( my $mimeType = delete $options->{'mediaType'} ) {
878             push @extra_headers, 'X-Upload-Content-Type' => $mimeType;
879             }
880              
881             # Do we have metadata other than uploadType?
882 0           if ( keys %{$options} > 1 ) {
883             push @extra_headers, 'Content-Type' => 'application/json; charset=UTF-8';
884 0           }
885 0 0          
886             my $info = {
887             'query_parameters' => {
888 0           'uploadType' => [ TYPE_STRING(), 1 ],
889 0   0       'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
890             'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ],
891             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
892             'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
893             'ocrLanguage' => [ TYPE_STRING(), 0 ],
894             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
895 0     0 1   'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
896             'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ]
897 0   0       },
898 0            
899             'body_parameters' => [
900 0           qw<
901 0 0         appProperties
902 0           contentHints
903             contentRestrictions
904             copyRequiresWriterPermission
905             createdTime
906 0 0         description
  0            
907 0           folderColorRgb
908             id
909             mimeType
910             modifiedTime
911             name
912             originalFilename
913             parents
914             properties
915             shortcutDetails.targetId
916             starred
917             viewedByMeTime
918             writersCanShare
919             >
920             ],
921              
922             'path' => $self->{'api_upload_url'},
923             'method_name' => 'create_resumable_upload',
924             'http_method' => HTTP_METHOD_POST(),
925             'extra_headers' => \@extra_headers,
926             'resumable' => 1,
927             'return_http_response' => 1,
928             };
929              
930             if ( defined $options->{'enforceSingleParent'} ) {
931             LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported");
932             }
933              
934             # Since a file upload can take a long time, refresh the token
935             # just in case.
936             $self->{'oauth'}->token_expire();
937              
938             my $response = $self->_handle_api_method( $info, $options );
939             $response->is_success()
940             or return;
941              
942             my $location = $response->header('Location');
943             return $location || '';
944             }
945              
946 0           ###########################################
947             ###########################################
948             my ( $self, $upload_uri, $file ) = @_;
949              
950             defined $upload_uri && length $upload_uri
951             or LOGDIE('upload_file_content_single() missing upload_uri');
952              
953             $upload_uri =~ m{^https://.*\bupload_id=.+}xmsi
954 0 0         or LOGDIE('upload_file_content_single() upload_uri seems malformed');
955 0            
956             defined $file && length $file
957             or LOGDIE('upload_file_content_single() missing file');
958              
959             -r $file
960 0           or LOGDIE("upload_file_content_single() received non-existent/unreadable file: $file");
961              
962 0           my $size = -s $file;
963 0 0         $size <= SIZE_5120GB()
964             or LOGDIE("upload_file_content_single() has a limit of 5120G, '$file' is bigger");
965              
966 0           my $info = {
967 0   0       'path' => $upload_uri,
968             'method_name' => 'upload_file_content_single',
969             'http_method' => HTTP_METHOD_PUT(),
970             'body_content' => $self->_content_sub($file),
971             };
972              
973 0     0 1   # Since a file upload can take a long time, refresh the token
974             # just in case.
975 0 0 0       $self->{'oauth'}->token_expire();
976              
977             return $self->_handle_api_method( $info, {} );
978 0 0         }
979              
980             ###########################################
981 0 0 0       ###########################################
982             my ( $self, $upload_uri, $file, $chunk_size ) = @_;
983              
984 0 0         defined $upload_uri && length $upload_uri
985             or LOGDIE('upload_file_content_multiple() missing upload_uri');
986              
987 0           $upload_uri =~ m{^https://.*upload_id=.+}xms
988 0 0         or LOGDIE('upload_file_content_multiple() upload_uri seems malformed');
989              
990             defined $file && length $file
991 0           or LOGDIE('upload_file_content_multiple() missing file');
992              
993             -r $file
994             or LOGDIE("upload_file_content_multiple() received non-existent/unreadable file: $file");
995              
996             my $size = -s $file;
997             $size <= SIZE_5120GB()
998             or LOGDIE("upload_file_content_multiple() has a limit of 5120G, '$file' is bigger");
999              
1000 0           # By upload in 10 MB chunks
1001             # But you can provide the amount of bytes instead
1002 0           $chunk_size //= DEF_CHUNK_SIZE();
1003              
1004             $chunk_size > 0
1005             or LOGDIE('upload_file_content_multiple() must have a chunk size above 0');
1006              
1007             $chunk_size % SIZE_256K() == 0
1008 0     0 1   or LOGDIE('upload_file_content_multiple() chunk size must divide by 256K');
1009              
1010 0 0 0       my $iter = $self->upload_file_content_iterator( $upload_uri, $file, $chunk_size );
1011             while ( my $request = $iter->() ) {
1012             my $response = $self->_make_request( $request, 1 );
1013 0 0          
1014             # 200 - means we're done
1015             # 308 - means continue
1016 0 0 0       # anything else - we're confused, so it's an error no matter what
1017              
1018             if ( $response->code() == Net::Google::Drive::Simple::Core::HTTP_CODE_OK() ) {
1019 0 0         return from_json( $response->decoded_content() );
1020             }
1021              
1022 0           if ( $response->code() != Net::Google::Drive::Simple::Core::HTTP_CODE_RESUME() ) {
1023 0 0         LOGDIE( "Triggered error: " . $response->code() );
1024             }
1025             }
1026              
1027             return;
1028 0   0       }
1029              
1030 0 0         ###########################################
1031             ###########################################
1032             my ( $self, $upload_uri, $file, $chunk_size ) = @_;
1033 0 0          
1034             defined $upload_uri && length $upload_uri
1035             or LOGDIE('upload_file_content_multiple() missing upload_uri');
1036 0            
1037 0           $upload_uri =~ m{^https://.*upload_id=.+}xms
1038 0           or LOGDIE('upload_file_content_multiple() upload_uri seems malformed');
1039              
1040             defined $file && length $file
1041             or LOGDIE('upload_file_content_multiple() missing file');
1042              
1043             -r $file
1044 0 0         or LOGDIE("upload_file_content_multiple() received non-existent/unreadable file: $file");
1045 0            
1046             my $size = -s $file;
1047             $size <= SIZE_5120GB()
1048 0 0         or LOGDIE("upload_file_content_multiple() has a limit of 5120G, '$file' is bigger");
1049 0            
1050             # By upload in 10 MB chunks
1051             # But you can provide the amount of bytes instead
1052             $chunk_size //= DEF_CHUNK_SIZE();
1053 0            
1054             $chunk_size > 0
1055             or LOGDIE('upload_file_content_multiple() must have a chunk size above 0');
1056              
1057             $chunk_size % SIZE_256K() == 0
1058             or LOGDIE('upload_file_content_multiple() chunk size must divide by 256K');
1059 0     0 1    
1060             # How many chunks are we going to iterate over
1061 0 0 0       # size in bytes divided by 256K (chunk size)
1062             my $chunks = ( $size / $chunk_size );
1063              
1064 0 0         # One more if there's a tail
1065             $size % $chunk_size
1066             and $chunks++;
1067 0 0 0        
1068             my $position = 0;
1069             my $iter = sub {
1070 0 0         $chunks-- == 0
1071             and return;
1072              
1073 0           my $start_position = $position;
1074 0 0          
1075             # TODO: Should we take the approach of _content_sub with opening usnig IO::File?
1076             open my $fh, '<', $file
1077             or LOGDIE("File '$file' cannot be open for reading");
1078              
1079 0   0       my $content;
1080             sysseek $fh, $start_position, 0;
1081 0 0         my $bytes_read = sysread $fh, $content, $chunk_size;
1082             $position += $bytes_read;
1083              
1084 0 0         close $fh
1085             or LOGDIE("File '$file' cannot be closed after reading");
1086              
1087             my $info = {
1088             'path' => $upload_uri,
1089 0           'method_name' => 'upload_file_content_multiple',
1090             'http_method' => HTTP_METHOD_PUT(),
1091             'return_http_request' => 1,
1092 0 0         'body_content' => $content,
1093             'extra_headers' => [
1094             'Content-Length' => $bytes_read,
1095 0           'Content-Range' => "bytes $start_position-" . ( $position - 1 ) . "/$size" #. ( $position + $bytes_read ),
1096             ],
1097 0 0   0     };
1098              
1099             # Since a file upload can take a long time, refresh the token
1100 0           # just in case.
1101             $self->{'oauth'}->token_expire();
1102              
1103 0 0         my $request = $self->_handle_api_method( $info, {} );
1104             return $request;
1105             };
1106 0            
1107 0           return $iter;
1108 0           }
1109 0            
1110             ###########################################
1111 0 0         ###########################################
1112             my ( $self, $file, $options ) = @_;
1113             my $upload_uri = $self->create_resumable_upload_for( $file, $options );
1114 0           return $self->upload_file_content_single( $upload_uri, $file );
1115             }
1116              
1117             ###########################################
1118             ###########################################
1119             my ( $self, $fileId, $options ) = @_;
1120              
1121             defined $fileId && length $fileId
1122             or LOGDIE('delete_file() missing file ID');
1123              
1124             $options //= {};
1125              
1126             my $info = {
1127             'query_parameters' => {
1128 0           'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
1129             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1130 0           'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1131 0           },
1132 0            
1133             'path' => "files/$fileId",
1134 0           'method_name' => 'delete_file',
1135             'http_method' => HTTP_METHOD_DELETE(),
1136             };
1137              
1138             if ( defined $options->{'enforceSingleParent'} ) {
1139             LOGDIE("[$info->{'method_name'}] If an item is not in a shared drive and its last parent is deleted but the item itself is not, the item will be placed under its owner's root");
1140 0     0 1   }
1141 0            
1142 0           return $self->_handle_api_method( $info, $options );
1143             }
1144              
1145             ###########################################
1146             ###########################################
1147             my ( $self, $fileId, $options ) = @_;
1148 0     0 1    
1149             defined $fileId && length $fileId
1150 0 0 0       or LOGDIE('export_file() missing file ID');
1151              
1152             ref $options eq 'HASH'
1153 0   0       or LOGDIE('export_file() missing parameters');
1154              
1155 0           my $info = {
1156             'query_parameters' => {
1157             'mimeType' => [ TYPE_STRING(), 1 ],
1158             'fields' => [ TYPE_STRING(), 0 ],
1159             },
1160              
1161             'path' => "files/$fileId/export",
1162             'method_name' => 'export_file',
1163             'http_method' => HTTP_METHOD_GET(),
1164             };
1165              
1166             return $self->_handle_api_method( $info, $options );
1167 0 0         }
1168 0            
1169             ###########################################
1170             ###########################################
1171 0           my ( $self, $options ) = @_;
1172              
1173             $options //= {};
1174              
1175             my $info = {
1176             'query_parameters' => {
1177 0     0 1   'count' => [ TYPE_INTEGER(), 0 ],
1178             'fields' => [ TYPE_STRING(), 0 ],
1179 0 0 0       'space' => [ TYPE_STRING(), 0 ],
1180             'type' => [ TYPE_STRING(), 0 ],
1181             },
1182 0 0          
1183             'parameter_checks' => {
1184             'spaces' => sub {
1185 0           /^( files | shortcuts )$/xms
1186             or return 'must be: files|shortcuts';
1187              
1188             return 0;
1189             }
1190             },
1191              
1192             'path' => 'files/generateIds',
1193             'method_name' => 'generateIds',
1194             'http_method' => HTTP_METHOD_GET(),
1195             };
1196 0            
1197             return $self->_handle_api_method( $info, $options );
1198             }
1199              
1200             ###########################################
1201             ###########################################
1202 0     0 1   my ( $self, $fileId, $options ) = @_;
1203              
1204 0   0       defined $fileId && length $fileId
1205             or LOGDIE('get_file() missing file ID');
1206              
1207             $options //= {};
1208              
1209             my $info = {
1210             'query_parameters' => {
1211             'acknowledgeAbuse' => [ TYPE_BOOLEAN(), 0 ],
1212             'fields' => [ TYPE_STRING(), 0 ],
1213             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
1214             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1215             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1216 0 0   0     },
1217              
1218             'path' => "files/$fileId",
1219 0           'method_name' => 'get_file',
1220             'http_method' => HTTP_METHOD_GET(),
1221             };
1222              
1223 0           return $self->_handle_api_method( $info, $options );
1224             }
1225              
1226             ###########################################
1227             ###########################################
1228 0           my ( $self, $options ) = @_;
1229              
1230             $options //= {};
1231              
1232             # TODO: orderBy
1233             # 'createdTime', 'folder', 'modifiedByMeTime', 'modifiedTime', 'name', 'name_natural', 'quotaBytesUsed', 'recency', 'sharedWithMeTime', 'starred', and 'viewedByMeTime'.
1234 0     0 1   # ?orderBy=folder,modifiedTime desc,name
1235              
1236 0 0 0       # TODO: Steal the struct to query syntax from MetaCPAN::Client
1237             # https://developers.google.com/drive/api/guides/search-files
1238             #if ( my $query_str = $options->{'q'} ) {...}
1239 0   0        
1240             my $info = {
1241 0           'query_parameters' => {
1242             'corpora' => [ TYPE_STRING(), 0 ],
1243             'corpus' => [ TYPE_STRING(), 0 ], # deprecated
1244             'driveId' => [ TYPE_STRING(), 0 ],
1245             'fields' => [ TYPE_STRING(), 0 ],
1246             'includeItemsFromAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1247             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
1248             'includeTeamDriveItems' => [ TYPE_BOOLEAN(), 0 ], # deprecated
1249             'orderBy' => [ TYPE_STRING(), 0 ],
1250             'pageSize' => [ TYPE_INTEGER(), 0 ],
1251             'pageToken' => [ TYPE_STRING(), 0 ],
1252             'q' => [ TYPE_STRING(), 0 ],
1253             'spaces' => [ TYPE_STRING(), 0 ],
1254             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1255 0           'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], # deprecated
1256             'teamDriveId' => [ TYPE_STRING(), 0 ], # deprecated
1257             'trashed' => [ TYPE_BOOLEAN(), 0 ], # not documented
1258             },
1259              
1260             'parameter_checks' => {
1261 0     0 1   'corpora' => sub {
1262             /^( user | drive | domain | allDrives )$/xms
1263 0   0       or return 'must be: user|drive|domain|allDrives';
1264              
1265             if ( $_ eq 'drive' ) {
1266             defined $options->{'driveId'}
1267             or return 'if corpora is drive, parameter driveId must be set';
1268             }
1269              
1270             return 0;
1271             },
1272              
1273             'corpus' => sub {
1274             /^( domain | user )$/xms
1275             or return 'must be: domain|user';
1276              
1277             return 0;
1278             },
1279              
1280             'spaces' => sub {
1281             /^( drive | appDataFolder )$/xms
1282             or return 'must be: drive|appDataFolder';
1283              
1284             return 0;
1285             },
1286             },
1287              
1288             'path' => 'files',
1289             'method_name' => 'files',
1290             'http_method' => HTTP_METHOD_GET(),
1291             };
1292              
1293             return $self->_handle_api_method( $info, $options );
1294             }
1295 0 0   0      
1296             # Update with upload
1297             ###########################################
1298 0 0         ###########################################
1299 0 0         my ( $self, $fileId, $options ) = @_;
1300              
1301             defined $fileId && length $fileId
1302             or LOGDIE('update_file() missing file ID');
1303 0            
1304             ref $options eq 'HASH'
1305             or LOGDIE('update_file() missing parameters');
1306              
1307 0 0   0     my $info = {
1308             'query_parameters' => {
1309             'uploadType' => [ TYPE_STRING(), 1 ],
1310 0           'addParents' => [ TYPE_STRING(), 0 ],
1311             'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
1312             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
1313             'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
1314 0 0   0     'ocrLanguage' => [ TYPE_STRING(), 0 ],
1315             'removeParents' => [ TYPE_STRING(), 0 ],
1316             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1317 0           'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1318             'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ],
1319             },
1320              
1321 0           'body_parameters' => [
1322             qw<
1323             appProperties
1324             contentHints
1325             contentRestrictions
1326 0           copyRequiresWriterPermission
1327             description
1328             folderColorRgb
1329             mimeType
1330             modifiedTime
1331             name
1332             originalFilename
1333 0     0 1   properties
1334             starred
1335 0 0 0       trashed
1336             viewedByMeTime
1337             writersCanShare
1338 0 0         >
1339             ],
1340              
1341             'path' => $self->{'api_upload_file'} . "/$fileId",
1342             'method_name' => 'update_file',
1343             'http_method' => HTTP_METHOD_PATCH(),
1344             };
1345              
1346             if ( defined $options->{'enforceSingleParent'} ) {
1347             LOGDIE("[$info->{'method_name'}] Adding files to multiple folders is no longer supported. Use shortcuts instead");
1348             }
1349              
1350             return $self->_handle_api_method( $info, $options );
1351             }
1352              
1353             # Metadata
1354             ###########################################
1355             ###########################################
1356             my ( $self, $fileId, $options ) = @_;
1357              
1358             defined $fileId && length $fileId
1359             or LOGDIE('update_file_metadata() missing file ID');
1360              
1361             ref $options eq 'HASH'
1362             or LOGDIE('update_file_metadata() missing parameters');
1363              
1364             my $info = {
1365             'query_parameters' => {
1366             'addParents' => [ TYPE_STRING(), 0 ],
1367             'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
1368             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
1369             'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ],
1370             'ocrLanguage' => [ TYPE_STRING(), 0 ],
1371             'removeParents' => [ TYPE_STRING(), 0 ],
1372             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1373             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1374             'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ],
1375 0           },
1376              
1377             'body_parameters' => [
1378             qw<
1379             appProperties
1380 0 0         contentHints
1381 0           contentRestrictions
1382             copyRequiresWriterPermission
1383             description
1384 0           folderColorRgb
1385             mimeType
1386             modifiedTime
1387             name
1388             originalFilename
1389             properties
1390             starred
1391 0     0 1   trashed
1392             viewedByMeTime
1393 0 0 0       writersCanShare
1394             >
1395             ],
1396 0 0          
1397             'path' => "files/$fileId",
1398             'method_name' => 'update_file_metadata',
1399 0           'http_method' => HTTP_METHOD_PATCH(),
1400             };
1401              
1402             if ( defined $options->{'enforceSingleParent'} ) {
1403             LOGDIE("[$info->{'method_name'}] Adding files to multiple folders is no longer supported. Use shortcuts instead");
1404             }
1405              
1406             return $self->_handle_api_method( $info, $options );
1407             }
1408              
1409             ###########################################
1410             ###########################################
1411             my ( $self, $fileId, $options ) = @_;
1412              
1413             defined $fileId && length $fileId
1414             or LOGDIE('watch_file() missing file ID');
1415              
1416             ref $options eq 'HASH'
1417             or LOGDIE('watch_file() missing parameters');
1418              
1419             my $info = {
1420             'query_parameters' => {
1421             'acknowledgeAbuse' => [ TYPE_BOOLEAN(), 0 ],
1422             'fields' => [ TYPE_STRING(), 0 ],
1423             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1424             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1425             },
1426              
1427             'body_parameters' => [
1428             qw<
1429             kind
1430             id
1431             resourceId
1432             resourceUri
1433             token
1434             expiration
1435             type
1436             address
1437 0 0         payload
1438 0           params
1439             >
1440             ],
1441 0            
1442             'path' => "files/$fileId/watch",
1443             'method_name' => 'watch_file',
1444             'http_method' => HTTP_METHOD_POST(),
1445             };
1446              
1447 0     0 1   $options->{'kind'} = 'api#channel';
1448              
1449 0 0 0       return $self->_handle_api_method( $info, $options );
1450             }
1451              
1452 0 0         ###########################################
1453             ###########################################
1454             my ( $self, $options ) = @_;
1455 0            
1456             $options //= {};
1457              
1458             my $info = {
1459             'query_parameters' => {
1460             'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
1461             },
1462              
1463             'path' => 'files/trash',
1464             'method' => 'empty_trash',
1465             'http_method' => HTTP_METHOD_DELETE(),
1466             };
1467              
1468             if ( defined $options->{'enforceSingleParent'} ) {
1469             LOGDIE("[$info->{'method_name'}] If an item is not in a shared drive and its last parent is deleted but the item itself is not, the item will be placed under its owner's root");
1470             }
1471              
1472             return $self->_handle_api_method( $info, {} );
1473             }
1474              
1475             # --- permissions
1476              
1477             ###########################################
1478             ###########################################
1479             my ( $self, $fileId, $options ) = @_;
1480              
1481             defined $fileId && length $fileId
1482             or LOGDIE('create_permission() missing file ID');
1483 0            
1484             ref $options eq 'HASH'
1485 0           or LOGDIE('create_permission() missing parameters');
1486              
1487             my $info = {
1488             'query_parameters' => {
1489             'emailMessage' => [ TYPE_STRING(), 0 ],
1490             'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ],
1491 0     0 1   'fields' => [ TYPE_STRING(), 0 ],
1492             'moveToNewOwnersRoot' => [ TYPE_BOOLEAN(), 0 ],
1493 0   0       'sendNotificationEmail' => [ TYPE_BOOLEAN(), 0 ],
1494             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1495 0           'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1496             'transferOwnership' => [ TYPE_BOOLEAN(), 0 ],
1497             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
1498             },
1499              
1500             'body_parameters' => [
1501             qw<
1502             role
1503             type
1504             allowFileDiscovery
1505 0 0         domain
1506 0           emailAddress
1507             pendingOwner
1508             view
1509 0           >
1510             ],
1511              
1512             'deprecated_param_names' => {
1513             'enforceSingleParent' => 'moveToNewOwnersRoot',
1514             },
1515              
1516             'path' => "files/$fileId/permissions",
1517 0     0 1   'method_name' => 'create_permission',
1518             'http_method' => HTTP_METHOD_POST(),
1519 0 0 0       };
1520              
1521             return $self->_handle_api_method( $info, $options );
1522 0 0         }
1523              
1524             ###########################################
1525 0           ###########################################
1526             my ( $self, $fileId, $permissionId, $options ) = @_;
1527              
1528             defined $fileId && length $fileId
1529             or LOGDIE('delete_permission() missing file ID');
1530              
1531             defined $permissionId && length $permissionId
1532             or LOGDIE('delete_permission() missing permission ID');
1533              
1534             $options //= {};
1535              
1536             my $info = {
1537             'query_parameters' => {
1538             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1539             'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1540             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
1541             },
1542              
1543             'path' => "files/$fileId/permissions/$permissionId",
1544             'method_name' => 'delete_permission',
1545             'http_method' => HTTP_METHOD_DELETE(),
1546             };
1547              
1548             return $self->_handle_api_method( $info, $options );
1549             }
1550              
1551             ###########################################
1552             ###########################################
1553             my ( $self, $fileId, $permissionId, $options ) = @_;
1554              
1555             defined $fileId && length $fileId
1556             or LOGDIE('get_permission() missing file ID');
1557              
1558             defined $permissionId && length $permissionId
1559 0           or LOGDIE('get_permission() missing permission ID');
1560              
1561             $options //= {};
1562              
1563             my $info = {
1564             'query_parameters' => {
1565 0     0 1   'fields' => [ TYPE_STRING(), 0 ],
1566             'supportsAllDrivee' => [ TYPE_BOOLEAN(), 0 ],
1567 0 0 0       'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1568             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
1569             },
1570 0 0 0        
1571             'path' => "files/$fileId/permissions/$permissionId",
1572             'method_name' => 'get_permission',
1573 0   0       'http_method' => HTTP_METHOD_GET(),
1574             };
1575 0            
1576             return $self->_handle_api_method( $info, $options );
1577             }
1578              
1579             ###########################################
1580             ###########################################
1581             my ( $self, $fileId, $options ) = @_;
1582              
1583             defined $fileId && length $fileId
1584             or LOGDIE('permissions() missing file ID');
1585              
1586             $options //= {};
1587 0            
1588             my $info = {
1589             'query_parameters' => {
1590             'fields' => [ TYPE_STRING(), 0 ],
1591             'includePermissionsForView' => [ TYPE_STRING(), 0 ],
1592             'pageSize' => [ TYPE_INTEGER(), 0 ],
1593 0     0 1   'pageToken' => [ TYPE_STRING(), 0 ],
1594             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1595 0 0 0       'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1596             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
1597             },
1598 0 0 0        
1599             'path' => "files/$fileId/permissions",
1600             'method_name' => 'permissions',
1601 0   0       'http_method' => HTTP_METHOD_GET(),
1602             };
1603 0            
1604             return $self->_handle_api_method( $info, $options );
1605             }
1606              
1607             ###########################################
1608             ###########################################
1609             my ( $self, $fileId, $permissionId, $options ) = @_;
1610              
1611             defined $fileId && length $fileId
1612             or LOGDIE('update_permission() missing file ID');
1613              
1614             defined $permissionId && length $permissionId
1615             or LOGDIE('update_permission() missing permission ID');
1616 0            
1617             $options //= {};
1618              
1619             my $info = {
1620             'query_parameters' => {
1621             'fields' => [ TYPE_STRING(), 0 ],
1622 0     0 1   'removeExpiration' => [ TYPE_BOOLEAN(), 0 ],
1623             'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ],
1624 0 0 0       'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ],
1625             'transferOwnership' => [ TYPE_BOOLEAN(), 0 ],
1626             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
1627 0   0       },
1628              
1629 0           'path' => "files/$fileId/permissions/$permissionId",
1630             'method_name' => 'update_permission',
1631             'http_method' => HTTP_METHOD_PATCH(),
1632             };
1633              
1634             return $self->_handle_api_method( $info, $options );
1635             }
1636              
1637             # --- replies
1638              
1639             ###########################################
1640             ###########################################
1641             my ( $self, $fileId, $commentId, $options ) = @_;
1642              
1643             defined $fileId && length $fileId
1644             or LOGDIE('create_reply() missing file ID');
1645 0            
1646             defined $commentId && length $commentId
1647             or LOGDIE('create_reply() missing comment ID');
1648              
1649             ref $options eq 'HASH'
1650             or LOGDIE('create_reply() missing parameters');
1651 0     0 1    
1652             my $info = {
1653 0 0 0       'query_parameters' => {
1654             'fields' => [ TYPE_STRING(), 1 ],
1655             },
1656 0 0 0        
1657             'body_parameters' => [
1658             qw<
1659 0   0       action
1660             content
1661 0           >
1662             ],
1663              
1664             'parameter_checks' => {
1665             'action' => sub {
1666             m{^( resolve | reopen )$}xms
1667             or return 'must be: resolve|reopen';
1668              
1669             return 0;
1670             },
1671             },
1672              
1673             'path' => "files/$fileId/comments/$commentId/replies",
1674             'method_name' => 'create_reply',
1675             'http_method' => HTTP_METHOD_POST(),
1676 0           };
1677              
1678             return $self->_handle_api_method( $info, $options );
1679             }
1680              
1681             ###########################################
1682             ###########################################
1683             my ( $self, $fileId, $commentId, $replyId ) = @_;
1684 0     0 1    
1685             defined $fileId && length $fileId
1686 0 0 0       or LOGDIE('delete_reply() missing file ID');
1687              
1688             defined $commentId && length $commentId
1689 0 0 0       or LOGDIE('delete_reply() missing comment ID');
1690              
1691             my $info = {
1692 0 0         'path' => "files/$fileId/comments/$commentId/replies/$replyId",
1693             'method_name' => 'delete_reply',
1694             'http_method' => HTTP_METHOD_DELETE(),
1695             };
1696              
1697             return $self->_handle_api_method( $info, {} );
1698             }
1699              
1700             ###########################################
1701             ###########################################
1702             my ( $self, $fileId, $commentId, $replyId, $options ) = @_;
1703              
1704             defined $fileId && length $fileId
1705             or LOGDIE('get_reply() missing file ID');
1706              
1707             defined $commentId && length $commentId
1708             or LOGDIE('get_reply() missing comment ID');
1709 0 0   0      
1710             defined $replyId && length $replyId
1711             or LOGDIE('get_reply() missing reply ID');
1712 0            
1713             ref $options eq 'HASH'
1714             or LOGDIE('get_reply() missing parameters');
1715              
1716 0           my $info = {
1717             'query_parameters' => {
1718             'fields' => [ TYPE_STRING(), 1 ],
1719             'includeDeleted' => [ TYPE_BOOLEAN(), 0 ],
1720             },
1721 0            
1722             'path' => "files/$fileId/comments/$commentId/replies/$replyId",
1723             'method_name' => 'get_reply',
1724             'http_method' => HTTP_METHOD_GET(),
1725             };
1726              
1727 0     0 1   return $self->_handle_api_method( $info, $options );
1728             }
1729 0 0 0        
1730             ###########################################
1731             ###########################################
1732 0 0 0       my ( $self, $fileId, $commentId, $options ) = @_;
1733              
1734             defined $fileId && length $fileId
1735 0           or LOGDIE('replies() missing file ID');
1736              
1737             defined $commentId && length $commentId
1738             or LOGDIE('replies() missing comment ID');
1739              
1740             ref $options eq 'HASH'
1741 0           or LOGDIE('replies() missing parameters');
1742              
1743             my $info = {
1744             'query_parameters' => {
1745             'fields' => [ TYPE_STRING(), 1 ],
1746             'includeDeleted' => [ TYPE_BOOLEAN(), 0 ],
1747 0     0 1   'pageSize' => [ TYPE_INTEGER(), 0 ],
1748             'pageToken' => [ TYPE_STRING(), 0 ],
1749 0 0 0       },
1750              
1751             'path' => "files/$fileId/comments/$commentId/replies",
1752 0 0 0       'method_name' => 'replies',
1753             'http_method' => HTTP_METHOD_GET(),
1754             };
1755 0 0 0        
1756             return $self->_handle_api_method( $info, $options );
1757             }
1758 0 0          
1759             ###########################################
1760             ###########################################
1761 0           my ( $self, $fileId, $commentId, $replyId, $options ) = @_;
1762              
1763             defined $fileId && length $fileId
1764             or LOGDIE('update_reply() missing file ID');
1765              
1766             defined $commentId && length $commentId
1767             or LOGDIE('update_reply() missing comment ID');
1768              
1769             defined $replyId && length $replyId
1770             or LOGDIE('update_reply() missing reply ID');
1771              
1772 0           ref $options eq 'HASH'
1773             or LOGDIE('update_reply() missing parameters');
1774              
1775             my $info = {
1776             'query_parameters' => {
1777             'fields' => [ TYPE_STRING(), 1 ],
1778 0     0 1   },
1779              
1780 0 0 0       'body_parameters' => {
1781             'content' => [ TYPE_STRING(), 1 ],
1782             },
1783 0 0 0        
1784             'path' => "files/$fileId/comments/$commentId/replies/$replyId",
1785             'method_name' => 'update_reply',
1786 0 0         'http_method' => HTTP_METHOD_PATCH(),
1787             };
1788              
1789 0           return $self->_handle_api_method( $info, $options );
1790             }
1791              
1792             # --- revisions
1793              
1794             ###########################################
1795             ###########################################
1796             my ( $self, $fileId, $revisionId ) = @_;
1797              
1798             defined $fileId && length $fileId
1799             or LOGDIE('delete_revision() missing file ID');
1800              
1801             my $info = {
1802 0           'path' => "files/$fileId/revisions/$revisionId",
1803             'method_name' => 'delete_revision',
1804             'http_method' => HTTP_METHOD_DELETE(),
1805             };
1806              
1807             return $self->_handle_api_method( $info, {} );
1808 0     0 1   }
1809              
1810 0 0 0       ###########################################
1811             ###########################################
1812             my ( $self, $fileId, $revisionId, $options ) = @_;
1813 0 0 0        
1814             defined $fileId && length $fileId
1815             or LOGDIE('get_revision() missing file ID');
1816 0 0 0        
1817             defined $revisionId && length $revisionId
1818             or LOGDIE('get_revision() missing revision ID');
1819 0 0          
1820             $options //= {};
1821              
1822 0           my $info = {
1823             'query_parameters' => {
1824             'acknowledgeAbuse' => [ TYPE_BOOLEAN(), 0 ],
1825             'fields' => [ TYPE_STRING(), 0 ],
1826             },
1827              
1828             'path' => "files/$fileId/revisions/$revisionId",
1829             'method_name' => 'get_revision',
1830             'http_method' => HTTP_METHOD_GET(),
1831             };
1832              
1833             return $self->_handle_api_method( $info, $options );
1834             }
1835              
1836 0           ###########################################
1837             ###########################################
1838             my ( $self, $fileId, $options ) = @_;
1839              
1840             defined $fileId && length $fileId
1841             or LOGDIE('revisions() missing file ID');
1842              
1843             $options //= {};
1844 0     0 1    
1845             my $info = {
1846 0 0 0       'query_parameters' => {
1847             'fields' => [ TYPE_STRING(), 0 ],
1848             'pageSize' => [ TYPE_INTEGER(), 0 ],
1849 0           'pageToken' => [ TYPE_STRING(), 0 ],
1850             },
1851              
1852             'path' => "files/$fileId/revisions",
1853             'method_name' => 'revisions',
1854             'http_method' => HTTP_METHOD_GET(),
1855 0           };
1856              
1857             return $self->_handle_api_method( $info, $options );
1858             }
1859              
1860             ###########################################
1861 0     0 1   ###########################################
1862             my ( $self, $fileId, $revisionId, $options ) = @_;
1863 0 0 0        
1864             defined $fileId && length $fileId
1865             or LOGDIE('update_revision() missing file ID');
1866 0 0 0        
1867             defined $revisionId && length $revisionId
1868             or LOGDIE('update_revision() missing revision ID');
1869 0   0        
1870             ref $options eq 'HASH'
1871 0           or LOGDIE('update_revision() missing parameters');
1872              
1873             my $info = {
1874             'query_parameters' => {
1875             'fields' => [ TYPE_STRING(), 0 ],
1876             },
1877              
1878             'body_parameter' => [
1879             qw<
1880             keepForever
1881             publishAuto
1882 0           published
1883             publishedOutsideDomain
1884             >
1885             ],
1886              
1887             'path' => "files/$fileId/revisions/$revisionId",
1888 0     0 1   'method_name' => 'update_revision',
1889             'http_method' => HTTP_METHOD_PATCH(),
1890 0 0 0       };
1891              
1892             return $self->_handle_api_method( $info, $options );
1893 0   0       }
1894              
1895 0           # --- drives
1896              
1897             ###########################################
1898             ###########################################
1899             my ( $self, $options ) = @_;
1900              
1901             ref $options eq 'HASH'
1902             or LOGDIE('create_drive() missing parameters');
1903              
1904             my $info = {
1905             'query_parameters' => {
1906             'requestId' => [ TYPE_STRING(), 1 ],
1907 0           },
1908              
1909             'body_parameters' => {
1910             'name' => [ TYPE_STRING(), 1 ],
1911             'themeId' => [ TYPE_STRING(), 0 ],
1912             },
1913 0     0 1    
1914             'path' => 'drives',
1915 0 0 0       'method_name' => 'create_drive',
1916             'http_method' => HTTP_METHOD_POST(),
1917             };
1918 0 0 0        
1919             return $self->_handle_api_method( $info, $options );
1920             }
1921 0 0          
1922             ###########################################
1923             ###########################################
1924 0           my ( $self, $driveId ) = @_;
1925              
1926             defined $driveId && length $driveId
1927             or LOGDIE('delete_drive() missing drive ID');
1928              
1929             my $info = {
1930             'path' => "drives/$driveId",
1931             'method_name' => 'delete_drive',
1932             'http_method' => HTTP_METHOD_DELETE(),
1933             };
1934              
1935             return $self->_handle_api_method( $info, {} );
1936             }
1937              
1938             ###########################################
1939             ###########################################
1940             my ( $self, $driveId, $options ) = @_;
1941              
1942             defined $driveId && length $driveId
1943 0           or LOGDIE('get_drive() missing drive ID');
1944              
1945             $options //= {};
1946              
1947             my $info = {
1948             'query_parameters' => {
1949             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
1950             },
1951 0     0 1    
1952             'path' => "drives/$driveId",
1953 0 0         'method_name' => 'get_drive',
1954             'http_method' => HTTP_METHOD_GET(),
1955             };
1956 0            
1957             return $self->_handle_api_method( $info, $options );
1958             }
1959              
1960             ###########################################
1961             ###########################################
1962             my ( $self, $driveId ) = @_;
1963              
1964             defined $driveId && length $driveId
1965             or LOGDIE('hide_drive() missing drive ID');
1966              
1967             my $info = {
1968             'path' => "drives/$driveId/hide",
1969             'method_name' => 'hide_drive',
1970             'http_method' => HTTP_METHOD_POST(),
1971 0           };
1972              
1973             return $self->_handle_api_method( $info, {} );
1974             }
1975              
1976             ###########################################
1977 0     0 1   ###########################################
1978             my ( $self, $options ) = @_;
1979 0 0 0        
1980             $options //= {};
1981              
1982 0           my $info = {
1983             'query_parameters' => {
1984             'pageSize' => [ TYPE_INTEGER(), 0 ],
1985             'pageToken' => [ TYPE_STRING(), 0 ],
1986             'q' => [ TYPE_STRING(), 0 ],
1987             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
1988 0           },
1989              
1990             'path' => 'drives',
1991             'method_name' => 'drives',
1992             'http_method' => HTTP_METHOD_GET(),
1993             };
1994 0     0 1    
1995             return $self->_handle_api_method( $info, $options );
1996 0 0 0       }
1997              
1998             ###########################################
1999 0   0       ###########################################
2000             my ( $self, $driveId ) = @_;
2001 0            
2002             defined $driveId && length $driveId
2003             or LOGDIE('unhide_drive() missing drive ID');
2004              
2005             my $info = {
2006             'path' => "drives/$driveId/unhide",
2007             'method_name' => 'unhide_drive',
2008             'http_method' => HTTP_METHOD_POST(),
2009             };
2010              
2011 0           return $self->_handle_api_method( $info, {} );
2012             }
2013              
2014             ###########################################
2015             ###########################################
2016             my ( $self, $driveId, $options ) = @_;
2017 0     0 1    
2018             defined $driveId && length $driveId
2019 0 0 0       or LOGDIE('update_drive() missing drive ID');
2020              
2021             ref $options eq 'HASH'
2022 0           or LOGDIE('update_drive() missing parameters');
2023              
2024             my $info = {
2025             'query_parameters' => {
2026             'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ],
2027             },
2028 0            
2029             'body_parameters' => [
2030             qw<
2031             backgroundImageFile
2032             colorRgb
2033             name
2034 0     0 1   restrictions
2035             themeId
2036 0   0       >
2037             ],
2038 0            
2039             'path' => "drives/$driveId",
2040             'method_name' => 'update_drive',
2041             'http_method' => HTTP_METHOD_PATCH(),
2042             };
2043              
2044             return $self->_handle_api_method( $info, $options );
2045             }
2046              
2047             # Helper methods
2048              
2049             ###########################################
2050             ###########################################
2051 0           my ( $self, $path, $opts, $search_opts ) = @_;
2052              
2053             DEBUG("Determine children of $path");
2054             LOGDIE("No $path given") unless defined $path;
2055              
2056             $opts //= {};
2057 0     0 1   $search_opts //= {};
2058              
2059 0 0 0       $opts->{'maxResults'}
2060             and LOGDIE("'maxResults' not supported, use 'pagesize' instead");
2061              
2062 0           my ( $folder_id, $parent ) = $self->_path_resolve( $path, $search_opts );
2063              
2064             return unless defined $folder_id;
2065              
2066             DEBUG("Getting content of folder $folder_id");
2067             my $children = $self->children_by_folder_id( $folder_id, $opts, $search_opts ) or return;
2068 0            
2069             return $children;
2070             }
2071              
2072             ###########################################
2073             ###########################################
2074 0     0 1   my ( $self, $folder_id, $opts, $search_opts ) = @_;
2075              
2076 0 0 0       $folder_id
2077             or LOGDIE('Must provide a folder id');
2078              
2079 0 0         $self->init();
2080              
2081             $opts = {} unless defined $opts;
2082 0           $search_opts = {} unless defined $search_opts;
2083              
2084             exists $search_opts->{'page'}
2085             and LOGDIE("Search option 'page' is deprecated, use 'auto_paging'");
2086              
2087             exists $search_opts->{'title'}
2088             and LOGDIE("Search option 'title' is deprecated, set 'q' parameter with 'name' accordingly");
2089              
2090             $search_opts->{'auto_paging'} //= 1;
2091              
2092             # Append or create a search in folder
2093             if ( defined $opts->{'q'} && length $opts->{'q'} ) {
2094             $opts->{'q'} .= ' AND ';
2095             }
2096             else {
2097             $opts->{'q'} = '';
2098             }
2099              
2100             $opts->{'q'} .= "'$folder_id' in parents";
2101              
2102 0           if ( my $name = $search_opts->{'name'} ) {
2103             $name =~ s{\'}{\\\'}xmsg;
2104             $opts->{'q'} .= " AND name = '$name'";
2105             }
2106              
2107             $opts->{'fields'} //= '';
2108             if ( $opts->{'fields'} ) {
2109             $opts->{'fields'} .= ',';
2110 0     0 1   }
2111             $opts->{'fields'} .= 'files(id,kind,name,mimeType,originalFilename,trashed)';
2112 0            
2113 0 0         # Find only those not in the trash
2114             # possibly go through all paged results
2115 0   0       my @children;
2116 0   0       while (1) {
2117             my $data = $self->files($opts)
2118 0 0         or return;
2119              
2120             my @items = @{ $data->{'files'} || [] };
2121 0            
2122             while ( my $item = shift @items ) {
2123 0 0         if ( $item->{'trashed'} ) {
2124             DEBUG("Skipping $item->{'name'} (item in trash)");
2125 0           next;
2126 0 0         }
2127              
2128 0           # use the Item.pm object with AUTOLOAD, is_folder, and is_file
2129             # TODO: I dislke the AUTOLOAD format...
2130             push @children, $self->data_factory($item);
2131             }
2132              
2133             if ( $search_opts->{'auto_paging'} && $data->{'nextPageToken'} ) {
2134 0     0 1   $opts->{'pageToken'} = $data->{'nextPageToken'};
2135             }
2136 0 0         else {
2137             last;
2138             }
2139 0           }
2140              
2141 0 0         return \@children;
2142 0 0         }
2143              
2144 0 0         ###########################################
2145             ###########################################
2146             my ( $self, $path, $search_opts ) = @_;
2147 0 0          
2148             $search_opts = {} if !defined $search_opts;
2149              
2150 0   0       my @parts = grep length, split '/', $path;
2151              
2152             my @ids = qw(root);
2153 0 0 0       my $folder_id = my $parent = 'root';
2154 0           DEBUG("Parent: $parent");
2155              
2156             PART:
2157 0           foreach my $part (@parts) {
2158             DEBUG("Looking up part $part (folder_id=$folder_id)");
2159              
2160 0           # We append to 'q' parameter in case the user provided it
2161             my $name = $part =~ s{\'}{\\\'}xmsgr;
2162 0 0         if ( defined $search_opts->{'q'} && length $search_opts->{'q'} ) {
2163 0           $search_opts->{'q'} .= ' AND ';
2164 0           }
2165             else {
2166             $search_opts->{'q'} = '';
2167 0   0       }
2168 0 0         $search_opts->{'q'} .= "name = '$name'";
2169 0            
2170             my $children = $self->children_by_folder_id( $folder_id, {}, $search_opts )
2171 0           or return;
2172              
2173             for my $child (@$children) {
2174             DEBUG( "Found child: " . $child->name() );
2175 0            
2176 0           if ( $child->name() eq $part ) {
2177 0 0         $folder_id = $child->{'id'};
2178             unshift @ids, $folder_id;
2179             $parent = $folder_id;
2180 0 0         DEBUG("Parent: $parent");
  0            
2181             next PART;
2182 0           }
2183 0 0         }
2184 0            
2185 0           my $msg = "Child $part not found";
2186             $self->error($msg);
2187             ERROR($msg);
2188             return;
2189             }
2190 0            
2191             # parent of root is root
2192             if ( @ids == 1 ) {
2193 0 0 0       push @ids, $ids[0];
2194 0           }
2195              
2196             return @ids;
2197 0           }
2198              
2199             # TODO: Placed here until I have a use for it
2200             #my %IMPORT_FORMATS = (
2201 0           # 'application/x-vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation',
2202             # 'text/tab-separated-values' => 'application/vnd.google-apps.spreadsheet',
2203             # 'image/jpeg' => 'application/vnd.google-apps.document',
2204             # 'image/bmp' => 'application/vnd.google-apps.document',
2205             # 'image/gif' => 'application/vnd.google-apps.document',
2206             # 'application/vnd.ms-excel.sheet.macroenabled.12' => 'application/vnd.google-apps.spreadsheet',
2207 0     0     # 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'application/vnd.google-apps.document',
2208             # 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'application/vnd.google-apps.presentation',
2209 0 0         # 'application/vnd.ms-word.template.macroenabled.12' => 'application/vnd.google-apps.document',
2210             # 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'application/vnd.google-apps.document',
2211 0           # 'image/pjpeg' => 'application/vnd.google-apps.document',
2212             # 'application/vnd.google-apps.script+text/plain' => 'application/vnd.google-apps.script',
2213 0           # 'application/vnd.ms-excel' => 'application/vnd.google-apps.spreadsheet',
2214 0           # 'application/vnd.sun.xml.writer' => 'application/vnd.google-apps.document',
2215 0           # 'application/vnd.ms-word.document.macroenabled.12' => 'application/vnd.google-apps.document',
2216             # 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'application/vnd.google-apps.presentation',
2217             # 'text/rtf' => 'application/vnd.google-apps.document',
2218 0           # 'application/vnd.oasis.opendocument.spreadsheet' => 'application/vnd.google-apps.spreadsheet',
2219 0           # 'text/plain' => 'application/vnd.google-apps.document',
2220             # 'application/x-vnd.oasis.opendocument.spreadsheet' => 'application/vnd.google-apps.spreadsheet',
2221             # 'application/x-vnd.oasis.opendocument.text' => 'application/vnd.google-apps.document',
2222 0           # 'image/png' => 'application/vnd.google-apps.document',
2223 0 0 0       # 'application/msword' => 'application/vnd.google-apps.document',
2224 0           # 'application/pdf' => 'application/vnd.google-apps.document',
2225             # 'application/x-msmetafile' => 'application/vnd.google-apps.drawing',
2226             # 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'application/vnd.google-apps.spreadsheet',
2227 0           # 'application/vnd.ms-powerpoint' => 'application/vnd.google-apps.presentation',
2228             # 'application/vnd.ms-excel.template.macroenabled.12' => 'application/vnd.google-apps.spreadsheet',
2229 0           # 'image/x-bmp' => 'application/vnd.google-apps.document',
2230             # 'application/rtf' => 'application/vnd.google-apps.document',
2231 0 0         # 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'application/vnd.google-apps.presentation',
2232             # 'image/x-png' => 'application/vnd.google-apps.document',
2233             # 'text/html' => 'application/vnd.google-apps.document',
2234 0           # 'application/vnd.oasis.opendocument.text' => 'application/vnd.google-apps.document',
2235 0           # 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'application/vnd.google-apps.presentation',
2236             # 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'application/vnd.google-apps.spreadsheet',
2237 0 0         # 'application/vnd.google-apps.script+json' => 'application/vnd.google-apps.script',
2238 0           # 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'application/vnd.google-apps.presentation',
2239 0           # 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'application/vnd.google-apps.presentation',
2240 0           # 'text/csv' => 'application/vnd.google-apps.spreadsheet',
2241 0           # 'application/vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation',
2242 0           # 'image/jpg' => 'application/vnd.google-apps.document',
2243             # 'text/richtext' => 'application/vnd.google-apps.document']
2244             #);
2245              
2246 0           1;
2247 0            
2248 0            
2249 0           =head1 SYNOPSIS
2250              
2251             my $gd_v3 = Net::Google::Drive::Simple->new( 'version' => 3 );
2252             # same as:
2253 0 0         my $gd_v3 = Net::Google::Drive::Simple::V3->new();
2254 0            
2255             $gd->changes(...);
2256             $gd->create_comment(...);
2257 0           $gd->delete_file(...);
2258              
2259             =head1 DESCRIPTION
2260              
2261             This is a complete implementation of the Google Drive API V3. You can
2262             use all the documented methods below.
2263              
2264             Uploading files over 5 MB require reading the B<UPLOADING> section below.
2265              
2266             =head1 METHODS
2267              
2268             =head2 C<new>
2269              
2270             my $gd = Net::Google::Drive::Simple::V3->new();
2271              
2272             Create a new instance.
2273              
2274             You can also create an instance using L<Net::Google::Drive::Simple>:
2275              
2276             my $gd = Net::Google::Drive::Simple->new( 'version' => 3 );
2277              
2278             =head2 C<about>
2279              
2280             my $about = $gd->about({%params});
2281              
2282             This serves the path to C</about>.
2283              
2284             It's also referred to as C<about.get>.
2285              
2286             You can read about the parameters on the Google Drive
2287             L<API page|https://developers.google.com/drive/api/v3/reference/about/get>.
2288              
2289             =head2 C<getStartPageToken>
2290              
2291             my $data = $gd->getStartPageToken({%params});
2292              
2293             Parameters are optional.
2294              
2295             This serves the path to C</changes/startPageToken>.
2296              
2297             This is also known as C<changes_getStartPageToken>.
2298              
2299             You can read about the parameters on the Google Drive
2300             L<API page|https://developers.google.com/drive/api/v3/reference/changes/getStartPageToken>.
2301              
2302             =head2 C<changes>
2303              
2304             my $changes_list = $gd->changes({%params});
2305              
2306             This serves the path to C</changes>.
2307              
2308             This is also known as C<changes.list>.
2309              
2310             You can read about the parameters on the Google Drive
2311             L<API page|https://developers.google.com/drive/api/v3/reference/changes/list>.
2312              
2313             =head2 C<watch_changes>
2314              
2315             my $data = $gd->watch_changes({%params});
2316              
2317             Parameters are optional.
2318              
2319             This serves the path to C</changes/watch>.
2320              
2321             This is also known as C<changes.watch>.
2322              
2323             You can read about the parameters on the Google Drive
2324             L<API page|https://developers.google.com/drive/api/v3/reference/changes/watch>.
2325              
2326             =head2 C<stop_channels>
2327              
2328             $gd->stop_channels({%params});
2329              
2330             This serves the path to C</channels/stop>.
2331              
2332             This is also known as C<channels.stop>.
2333              
2334             You can read about the parameters on the Google Drive
2335             L<API page|https://developers.google.com/drive/api/v3/reference/channels/stop>.
2336              
2337             =head2 C<create_comment>
2338              
2339             my $comment = $gd->create_comment( $fileId, {%params} );
2340              
2341             This serves the path to C</files/$fileId/comments>.
2342              
2343             This is also known as C<comments.create>.
2344              
2345             You can read about the parameters on the Google Drive
2346             L<API page|https://developers.google.com/drive/api/v3/reference/comments/create>.
2347              
2348             =head2 C<delete_comment( $fileId, $commentId )>
2349              
2350             $gd->delete_comment( $fileId, $commentId );
2351              
2352             This serves the path to C</files/$fileId/comments/$commentId>.
2353              
2354             This is also known as C<comments.delete>.
2355              
2356             You can read about the parameters on the Google Drive
2357             L<API page|https://developers.google.com/drive/api/v3/reference/comments/delete>.
2358              
2359             =head2 C<get_comment( $fileId, $commentId, $params )>
2360              
2361             my $comment = $gd->get_comment( $fileId, $commentId, {%params} );
2362              
2363             This serves the path to C</files/$fileId/comments/$commentId>.
2364              
2365             This is also known as C<comments.get>.
2366              
2367             You can read about the parameters on the Google Drive
2368             L<API page|https://developers.google.com/drive/api/v3/reference/comments/get>.
2369              
2370             =head2 C<comments>
2371              
2372             my $comments = $gd->comments( $fileId, {%params} );
2373              
2374             This serves the path to C</files/$fileId/comments>.
2375              
2376             This is also known as C<comments.list>.
2377              
2378             You can read about the parameters on the Google Drive
2379             L<API page|https://developers.google.com/drive/api/v3/reference/comments/list>.
2380              
2381             =head2 C<update_comment>
2382              
2383             my $comment = $gd->update_comment( $fileId, $commentId, {%params} );
2384              
2385             This serves the path to C</files/$fileId/comments/$commentId>.
2386              
2387             This is also known as C<comments.update>.
2388              
2389             You can read about the parameters on the Google Drive
2390             L<API page|https://developers.google.com/drive/api/v3/reference/comments/update>.
2391              
2392             =head2 C<copy_file>
2393              
2394             my $file = $gd->copy_file( $fileId, {%params} );
2395              
2396             This serves the path to C</files/$fileId/copy>.
2397              
2398             This is also known as C<files.copy>.
2399              
2400             You can read about the parameters on the Google Drive
2401             L<API page|https://developers.google.com/drive/api/v3/reference/files/copy>.
2402              
2403             =head2 C<create_file>
2404              
2405             my $file = $gd->create_file({%params});
2406              
2407             This serves the path to C</files>.
2408              
2409             This is also known as C<files.create>.
2410              
2411             You can read about the parameters on the Google Drive
2412             L<API page|https://developers.google.com/drive/api/v3/reference/files/create>.
2413              
2414             This one is B<ONLY for creating metadata for a file>. It will not upload
2415             any content. If you want to set content, use the following methods below:
2416             C<upload_media_file()>, C<upload_multipart_file>,
2417             C<create_resumable_upload()>, C<upload_file_content_single()>,
2418             C<upload_file_content_multiple()>, and C<upload_file_content_iterator>.
2419              
2420             Read more about uploading under B<UPLOADING> below. If you want a simple way
2421             to upload files, check C<upload_file()>.
2422              
2423             =head2 C<upload_file>
2424              
2425             my $result = $gd->upload_file( $filename, {%params} );
2426              
2427             Parameters are optional.
2428              
2429             This method combines the different mechanisms for uploading a file and makes
2430             it easy to upload large files. There are disadvantages, but for most cases,
2431             this method should serve you well.
2432              
2433             Read more about uploading under B<UPLOADING> below.
2434              
2435             =head2 C<upload_media_file>
2436              
2437             my $file = $gd->upload_media_file( $filename, {%params} );
2438              
2439             Parameters are optional.
2440              
2441             This serves the path to C</files>.
2442              
2443             This is also known as C<files.create>.
2444              
2445             You can read about the parameters on the Google Drive
2446             L<API page|https://developers.google.com/drive/api/v3/reference/files/create>.
2447              
2448             This one is for uploading a file B<without metadata>. File size limitation
2449             is 5 MB and we read it 4K at a time.
2450              
2451             Read more about uploading under B<UPLOADING> below.
2452              
2453             =head2 C<upload_multipart_file>
2454              
2455             my $file = $gd->upload_multipart_file( $filename, {%params} );
2456              
2457             Parameters are optional.
2458              
2459             This serves the path to C</files>.
2460              
2461             This is also known as C<files.create>.
2462              
2463             You can read about the parameters on the Google Drive
2464             L<API page|https://developers.google.com/drive/api/v3/reference/files/create>.
2465              
2466             This one is for uploading B<both a file and its metadata>. File size limitation
2467             is 5 MB and we read the entire file into memory at once before uploading.
2468              
2469             Read more about uploading under B<UPLOADING> below.
2470              
2471             =head2 C<create_resumable_upload_for>
2472              
2473             my $upload_id = $gd->upload_multimedia_file( $filename, {%params} );
2474              
2475             Parameters are optional.
2476              
2477             This serves the path to C</files>.
2478              
2479             This is also known as C<files.create>.
2480              
2481             You can read about the parameters on the Google Drive
2482             L<API page|https://developers.google.com/drive/api/v3/reference/files/create>.
2483              
2484             This method starts a resumable upload for a specific file on disk. It provides
2485             you with an ID that you can then feed to the following methods:
2486             C<upload_file_content_single()>, C<upload_file_content_multiple()>, and
2487             C<upload_file_content_iterator()>.
2488              
2489             File size limitation for any resumable upload is 5120 GB and
2490             C<create_resumable_upload_for()> checks for this limit.
2491              
2492             Read more about uploading under B<UPLOADING> below.
2493              
2494             =head2 C<create_resumable_upload>
2495              
2496             my $upload_id = $gd->create_resumable_upload( {%params} );
2497              
2498             Parameters are optional.
2499              
2500             This serves the path to C</files>.
2501              
2502             This is also known as C<files.create>.
2503              
2504             You can read about the parameters on the Google Drive
2505             L<API page|https://developers.google.com/drive/api/v3/reference/files/create>.
2506              
2507             This method starts a resumable upload. It provides you with an ID that you can
2508             then feed to the following methods: C<upload_file_content_single()>,
2509             C<upload_file_content_multiple()>, and C<upload_file_content_iterator()>.
2510              
2511             File size limitation for any resumable upload is 5120 GB and
2512             C<create_resumable_upload_for()> checks for this limit.
2513              
2514             Read more about uploading under B<UPLOADING> below.
2515              
2516             =head2 C<upload_file_content_single>
2517              
2518             my $result = $gd->upload_file_content_single( $upload_uri, $filename );
2519              
2520             This serves the path to the upload URI you provide it.
2521              
2522             There is a limitation of 5120 GB for the file. With this method, we feed the
2523             file 4K at a time.
2524              
2525             Read more about uploading under B<UPLOADING>.
2526              
2527             =head2 C<upload_file_content_multiple>
2528              
2529             my $result = $gd->upload_file_content_multiple( $upload_uri, $file, $chunk_size );
2530              
2531             Chunk size is optional, defaults to 10 MB.
2532              
2533             This serves the path to the upload URI you provide it.
2534              
2535             There is a limitation of 5120 GB for the file. With this method, we feed the
2536             file in chunks defined by the chunk size you provide or the default 10 MB.
2537              
2538             Read more about uploading under B<UPLOADING>.
2539              
2540             The difference between C<upload_file_content_single()> and
2541             C<upload_file_content_multiple()> is that this variation will work better
2542             for long files, in theory. More importantly, it uses
2543             C<upload_file_content_iterator()> which really allows you to control matters.
2544              
2545             =head2 C<upload_file_content_iterator>
2546              
2547             use JSON qw< from_json >;
2548              
2549             my $iter_cb = $gd->upload_file_content_iterator( $filename, $file, $chunk_size );
2550              
2551             while ( my $request = $iter->() ) {
2552             my $response = handle_http_request($request);
2553             # anything else - we're confused, so it's an error no matter what
2554              
2555             # 200 - Done!
2556             if ( $response->code() == 200 ) {
2557             return from_json( $response->decoded_content() );
2558             }
2559              
2560             # 308 - Continue uploading!
2561             # anything else - not what we're expecting
2562             if ( $response->code() != 308 ) {
2563             die "Error: " . $response->code();
2564             }
2565             }
2566              
2567             Chunk size is optional, defaults to 10 MB.
2568              
2569             This returns a callback that allows you to control how to handle the
2570             uploading. It's especially valuable when you want to connect the uploading
2571             to an event loop like L<IO::Async>, L<POE>, L<AnyEvent>, or others.
2572              
2573             The callback returns an object of L<HTTP::Request> which represent the
2574             request that needs to be done. Once requested, if you receive a 308, it
2575             means you can continue, and if you receive a 200, it means you are done
2576             uploading.
2577              
2578             There is a limitation of 5120 GB for the file. With this method, we feed the
2579             file in chunks defined by the chunk size you provide or the default 10 MB.
2580              
2581             Read more about uploading under B<UPLOADING>.
2582              
2583             =head2 C<delete_file>
2584              
2585             $gd->delete_file( $fileId, {%params} );
2586              
2587             Parameters are optional.
2588              
2589             This serves the path to C</files/$fileId>.
2590              
2591             This is also known as C<files.delete>.
2592              
2593             You can read about the parameters on the Google Drive
2594             L<API page|https://developers.google.com/drive/api/v3/reference/files/delete>.
2595              
2596             =head2 C<export_file>
2597              
2598             my $file_content_in_bytes = $gd->export_file( $fileId, {%params} );
2599              
2600             This serves the path to C</files/$fileId/export>.
2601              
2602             This is also known as C<files.export>.
2603              
2604             You can read about the parameters on the Google Drive
2605             L<API page|https://developers.google.com/drive/api/v3/reference/files/export>.
2606              
2607             =head2 C<generateIds>
2608              
2609             my $data = $gd->generateIds({%params});
2610              
2611             Parameters are optional.
2612              
2613             This serves the path to C</files/generateIds>.
2614              
2615             This is also known as C<files.generateIds>.
2616              
2617             You can read about the parameters on the Google Drive
2618             L<API page|https://developers.google.com/drive/api/v3/reference/files/generateIds>.
2619              
2620             =head2 C<get_file>
2621              
2622             my $file = $gd->get_file( $fileId, {%params} );
2623              
2624             Parameters are optional.
2625              
2626             This serves the path to C</files/$fileId>.
2627              
2628             This is also known as C<files.get>.
2629              
2630             You can read about the parameters on the Google Drive
2631             L<API page|https://developers.google.com/drive/api/v3/reference/files/get>.
2632              
2633             =head2 C<files>
2634              
2635             my $files = $gd->files({%params});
2636              
2637             Parameters are optional.
2638              
2639             This serves the path to C</files>.
2640              
2641             This is also known as C<files.list>.
2642              
2643             You can read about the parameters on the Google Drive
2644             L<API page|https://developers.google.com/drive/api/v3/reference/files/list>.
2645              
2646             =head2 C<update_file>
2647              
2648             my $file = $gd->update_file( $fileId, {%params} );
2649              
2650             This serves the path to C</files/$fileId>.
2651              
2652             This is also known as C<files.update>.
2653              
2654             This is for updating a file's metadata and content.
2655              
2656             You can read about the parameters on the Google Drive
2657             L<API page|https://developers.google.com/drive/api/v3/reference/files/update>.
2658              
2659             =head2 C<update_file_metadata>
2660              
2661             my $file = $gd->update_file_metadata( $fileId, {%params} );
2662              
2663             This serves the path to C</files/$fileId>.
2664              
2665             This is also known as C<files.update>.
2666              
2667             This one is only for updating a file's metadata, even though it shares
2668             a moniker with the C<update_file()> method in the Google Drive API.
2669              
2670             You can read about the parameters on the Google Drive
2671             L<API page|https://developers.google.com/drive/api/v3/reference/files/update>.
2672              
2673             =head2 C<watch_file>
2674              
2675             my $data = $gd->watch_file( $fileId, {%params} );
2676              
2677             This serves the path to C</files/$fileId/watch>.
2678              
2679             This is also known as C<files.watch>.
2680              
2681             You can read about the parameters on the Google Drive
2682             L<API page|https://developers.google.com/drive/api/v3/reference/files/watch>.
2683              
2684             =head2 C<empty_trash>
2685              
2686             $gd->empty_trash({%params})
2687              
2688             Parameters are optional.
2689              
2690             This serves the path to C</files/trash>.
2691              
2692             This is also known as C<files.emptyTrash>.
2693              
2694             You can read about the parameters on the Google Drive
2695             L<API page|https://developers.google.com/drive/api/v3/reference/files/emptyTrash>.
2696              
2697             =head2 C<create_permission>
2698              
2699             my $permission = $gd-><create_permission( $fileId, {%params} );
2700              
2701             This serves the path to C</files/$fileId/permissions>.
2702              
2703             This is also known as C<permissions.create>.
2704              
2705             You can read about the parameters on the Google Drive
2706             L<API page|https://developers.google.com/drive/api/v3/reference/permissions/create>.
2707              
2708             =head2 C<delete_permission>
2709              
2710             $gd->delete_permission( $fileId, $permissionId, {%params} );
2711              
2712             Parameters are optional.
2713              
2714             This serves the path to C</files/$fileId/permissions/$permissionId>.
2715              
2716             This is also known as C<permissions.delete>.
2717              
2718             You can read about the parameters on the Google Drive
2719             L<API page|https://developers.google.com/drive/api/v3/reference/permissions/delete>.
2720              
2721             =head2 C<get_permission>
2722              
2723             my $permission = $gd->get_permission( $fileId, $permissionId, {%params} );
2724              
2725             Parameters are optional.
2726              
2727             This serves the path to C</files/$fileId/permissions/$permissionId>.
2728              
2729             This is also known as C<permissions.get>.
2730              
2731             You can read about the parameters on the Google Drive
2732             L<API page|https://developers.google.com/drive/api/v3/reference/permissions/get>.
2733              
2734             =head2 C<permissions>
2735              
2736             my $permissions = $gd->permissions( $fileId, {%params} );
2737              
2738             Parameters are optional.
2739              
2740             This serves the path to C</files/$fileId/permissions>.
2741              
2742             This is also known as C<permissions.list>.
2743              
2744             You can read about the parameters on the Google Drive
2745             L<API page|https://developers.google.com/drive/api/v3/reference/permissions/list>.
2746              
2747             =head2 C<update_permission>
2748              
2749             my $permission = $gd->update_permission( $fileId, $permissionId, {%params} );
2750              
2751             Parameters are optional.
2752              
2753             This serves the path to C</files/$fileId/permissions/$permissionId>.
2754              
2755             This is also known as C<permissions.update>.
2756              
2757             You can read about the parameters on the Google Drive
2758             L<API page|https://developers.google.com/drive/api/v3/reference/permissions/update>.
2759              
2760             =head2 C<create_reply>
2761              
2762             my $reply = $gd->create_reply( $fileId, $commentId, {%params} );
2763              
2764             This serves the path to C</files/$fileId/comments/$commentId/replies>.
2765              
2766             This is also known as C<replies.create>.
2767              
2768             You can read about the parameters on the Google Drive
2769             L<API page|https://developers.google.com/drive/api/v3/reference/replies/create>.
2770              
2771             =head2 C<delete_reply( $fileId, $commentId, $replyId )>
2772              
2773             $gd->delete_reply( $fileId, $commentId, $replyId );
2774              
2775             This serves the path to C</files/$fileId/comments/$commentId/replies/$replyId>.
2776              
2777             This is also known as C<replies.delete>.
2778              
2779             You can read about the parameters on the Google Drive
2780             L<API page|https://developers.google.com/drive/api/v3/reference/replies/delete>.
2781              
2782             =head2 C<get_reply>
2783              
2784             my $reply = %gd->get_reply( $fileId, $commentId, $replyId, {%params} );
2785              
2786             This serves the path to C</files/$fileId/comments/$commentId/replies/$replyId>.
2787              
2788             This is also known as C<replies.get>.
2789              
2790             You can read about the parameters on the Google Drive
2791             L<API page|https://developers.google.com/drive/api/v3/reference/replies/get>.
2792              
2793             =head2 C<replies>
2794              
2795             my $replies = $gd->replies( $fileId, $commentId, {%params} );
2796              
2797             This serves the path to C</files/$fileId/comments/$commentId/replies>.
2798              
2799             This is also known as C<replies.list>.
2800              
2801             You can read about the parameters on the Google Drive
2802             L<API page|https://developers.google.com/drive/api/v3/reference/replies/list>.
2803              
2804             =head2 C<update_reply>
2805              
2806             my $reply = $gd->update_reply( $fileId, $commentId, $replyId, {%params} );
2807              
2808             This serves the path to C</files/$fileId/comments/$commentId/replies/$replyId>.
2809              
2810             This is also known as C<replies.update>.
2811              
2812             You can read about the parameters on the Google Drive
2813             L<API page|https://developers.google.com/drive/api/v3/reference/replies/update>.
2814              
2815             =head2 C<delete_revision>
2816              
2817             $gd->delete_revision( $fileId, {%params} );
2818              
2819             This serves the path to C</files/$fileId/revisions/$revisionId>.
2820              
2821             This is also known as C<revisions.delete>.
2822              
2823             You can read about the parameters on the Google Drive
2824             L<API page|https://developers.google.com/drive/api/v3/reference/revisions/delete>.
2825              
2826             =head2 C<get_revision( $fileId, $revisionId, $params )>
2827              
2828             my $revision = $gd->get_revision( $fileId, $revisionId, {%params} );
2829              
2830             Parameters are optional.
2831              
2832             This serves the path to C</files/$fileId/revisions/$revisionId>.
2833              
2834             This is also known as C<revisions.get>.
2835              
2836             You can read about the parameters on the Google Drive
2837             L<API page|https://developers.google.com/drive/api/v3/reference/revisions/get>.
2838              
2839             =head2 C<revisions>
2840              
2841             my $revisions = $gd->revisions( $fileId, {%params} );
2842              
2843             Parameters are optional.
2844              
2845             This serves the path to C</files/$fileId/revisions>.
2846              
2847             This is also known as C<revisions.list>.
2848              
2849             You can read about the parameters on the Google Drive
2850             L<API page|https://developers.google.com/drive/api/v3/reference/revisions/list>.
2851              
2852             =head2 C<update_revision>
2853              
2854             my $revision = $gd->update_revision( $fileId, $revisionId, {%params} );
2855              
2856             This serves the path to C</files/$fileId/revisions/$revisionId>.
2857              
2858             This is also known as C<revisions.update>.
2859              
2860             You can read about the parameters on the Google Drive
2861             L<API page|https://developers.google.com/drive/api/v3/reference/revisions/update>.
2862              
2863             =head2 C<create_drive>
2864              
2865             my $drive = $gd->create_drive({%params});
2866              
2867             This serves the path to C</drives>.
2868              
2869             This is also known as C<drives.create>.
2870              
2871             You can read about the parameters on the Google Drive
2872             L<API page|https://developers.google.com/drive/api/v3/reference/drives/create>.
2873              
2874             =head2 C<delete_drive>
2875              
2876             $gd->delete_drive($driveId);
2877              
2878             This serves the path to C</drives/$driveId>.
2879              
2880             This is also known as C<drives.delete>.
2881              
2882             You can read about the parameters on the Google Drive
2883             L<API page|https://developers.google.com/drive/api/v3/reference/drives/delete>.
2884              
2885             =head2 C<get_drive>
2886              
2887             my $drive = $gd->get_drive( $driveId, {%params} );
2888              
2889             Parameters are optional.
2890              
2891             This serves the path to C</drives/$driveId>.
2892              
2893             This is also known as C<drives.get>.
2894              
2895             You can read about the parameters on the Google Drive
2896             L<API page|https://developers.google.com/drive/api/v3/reference/drives/get>.
2897              
2898             =head2 C<hide_drive>
2899              
2900             my $drive = $gd->hide_drive($driveId);
2901              
2902             This serves the path to C</drives/$driveId/hide>.
2903              
2904             This is also known as C<drives.hide>.
2905              
2906             You can read about the parameters on the Google Drive
2907             L<API page|https://developers.google.com/drive/api/v3/reference/drives/hide>.
2908              
2909             =head2 C<drives>
2910              
2911             my $drives = $gd->drives({%params});
2912              
2913             Parameters are optional.
2914              
2915             This serves the path to C</drives>.
2916              
2917             This is also known as C<drives.list>.
2918              
2919             You can read about the parameters on the Google Drive
2920             L<API page|https://developers.google.com/drive/api/v3/reference/drives/list>.
2921              
2922             =head2 C<unhide_drive>
2923              
2924             my $drive = $gd->unhide_drive($driveId);
2925              
2926             This serves the path to C</drives/$driveId/unhide>.
2927              
2928             This is also known as C<drives.unhide>.
2929              
2930             You can read about the parameters on the Google Drive
2931             L<API page|https://developers.google.com/drive/api/v3/reference/drives/unhide>.
2932              
2933             =head2 C<update_drive>
2934              
2935             my $drive = $gd->update_drive( $driveId, {%params} );
2936              
2937             This serves the path to C</drives/$driveId>.
2938              
2939             This is also known as C<drives.update>.
2940              
2941             You can read about the parameters on the Google Drive
2942             L<API page|https://developers.google.com/drive/api/v3/reference/drives/update>.
2943              
2944             =head2 C<children>
2945              
2946             my $children = $gd->children( '/path/to', {%params}, {%extra_params} )>
2947              
2948             The parameters and extra parameters are both optional.
2949              
2950             Return the entries under a given path on the Google Drive as a reference
2951             to an array. Each entry
2952             is an object composed of the JSON data returned by the Google Drive API.
2953             Each object offers methods named like the fields in the JSON data, e.g.
2954             C<originalFilename()>, C<downloadUrl>, etc.
2955              
2956             Will return all entries found unless C<pageSize> is set:
2957              
2958             my $children = $gd->children( "/path/to", { pageSize => 3 } )
2959              
2960             Due to the somewhat capricious ways Google Drive handles its directory
2961             structures, the method needs to traverse the path component by component
2962             and determine the ID of each directory to get to the next level. To speed
2963             up subsequent lookups, it also returns the ID of the last component to the
2964             caller:
2965              
2966             my( $children, $parent ) = $gd->children( "/path/to" );
2967              
2968             If the caller now wants to e.g. insert a file into the directory, its
2969             ID is available in $parent.
2970              
2971             Each child comes back as a files#resource type and gets mapped into
2972             an object that offers access to the various fields via methods:
2973              
2974             for my $child ( @$children ) {
2975             print $child->kind(), " ", $child->name(), "\n";
2976             }
2977              
2978             Please refer to
2979              
2980             https://developers.google.com/drive/v3/reference/files#resource
2981              
2982             for details on which fields are available.
2983              
2984             =head2 C<children_by_folder_id>
2985              
2986             my $children = $gd->children_by_folder_id($folderId);
2987              
2988             # Search with a particular query and stop at the first page
2989             my $children = $gd->children_by_folder_id(
2990             $folderId,
2991             { 'q' => q{name contains 'hello'} },
2992             { 'auto_paging' => 0 },
2993             );
2994              
2995             Similar to C<children()> but uses a folder ID.
2996              
2997             The second parameter is the parameters the C<files()> method receives.
2998              
2999             The third parameter is for additional options on how to conduct this
3000             search. Only one option is supported: C<auto_paging>.
3001              
3002             When C<auto_paging> is turned on (which is the default), the search
3003             will be done on every page of the results instead of stopping at the
3004             first page.
3005              
3006             =head1 UPLOADING
3007              
3008             Uploading of a 5 MB file or lower is simple, but uploading a larger
3009             file is more difficult. This module supports every possible option,
3010             including connecting uploads to a different systems.
3011              
3012             =head2 SIMPLE AND EASY UPLOADING
3013              
3014             If you are not interested in all the details and finer controls of
3015             uploading files, you can just use C<upload_file>.
3016              
3017             my $data = $gd->upload_file( $filename, {%params} );
3018              
3019             The parameters are optional and you can read more about them in
3020             the appropriate
3021             L<API page|https://developers.google.com/drive/api/v3/reference/files/create>.
3022              
3023             The disadvantages are that you cannot control how much is uploaded
3024             at a time, it's not resumable, nor coudl you connect it with an
3025             event loop.
3026              
3027             However, this has no size limitations other than 5120 GB.
3028              
3029             =head2 UPLOADING FILES 5 MB OR SMALLER
3030              
3031             When uploading 5 MB and under, you can either use C<create_file()>
3032             for metadata or C<upload_media_file()> for both metadata and content.
3033              
3034             Despite the name, you may upload any form of file, not just media
3035             files. (This is the name Google provides this form of upload.)
3036              
3037             # Create only the metadata
3038             my $data = $gd->create_file({
3039             'name' => 'foo.txt',
3040             'mediaType' => 'text/plain',
3041             })
3042              
3043             Once you call C<create_file()>, you can use the ID of the response
3044             in subsequent calls to C<upload_media_file()> or
3045             C<upload_multipart_file()>. (This might work for resumable uploads
3046             too, but it's not tested.)
3047              
3048             # Upload just the file
3049             $data = $gd->upload_media_file('foo.txt');
3050             $data = $gd->upload_media_file( 'foo.txt', { 'name' => 'bar.txt' } );
3051              
3052             The only supported parameters are the query parameters.
3053              
3054             # Upload the file and all the metadata
3055             $data = $gd->upload_multipart_file( 'foo.txt', {...} );
3056              
3057             The difference between C<upload_media_file()> and
3058             C<upload_multipart_file()> is that the former method allows the query
3059             parameters, but the latter method allows all options.
3060              
3061             You can read more about the available options for either of these
3062             file uploads
3063             L<here|https://developers.google.com/drive/api/v3/reference/files/create>.
3064              
3065             =head2 UPLOADING FILES ABOVE 5 MB
3066              
3067             When uploading files above 5 MB, you must first create a URI for the
3068             file upload and then upload to that URI with another method.
3069              
3070             There are two ways to create it, depending on whether you have a file
3071             available on disk or not.
3072              
3073             # Creating the URI using a file
3074             my $upload_uri = $gd->create_resumable_upload_for( $file, {...} );
3075              
3076             # Creating the URI without a file
3077             my $upload_uri = $gd->create_resumable_upload({
3078             'name' => 'foo.txt',
3079             'mimeType' => 'text/plain',
3080             });
3081              
3082             The benefit of using C<create_resumable_upload_for()> is that it allows
3083             you to use an existing file's mime type and filename. Otherwise, if you
3084             use the low-level C<create_resumable_upload()>, you will need to provide
3085             the C<name> and C<mediaType> parameters yourself.
3086              
3087             There are three different methods for uploading - choose the one that
3088             suits you best.
3089              
3090             # Just upload the entire file
3091             my $data = upload_file_content_single( $upload_uri, $file );
3092              
3093             This upload will still try to chunk it by 4K so it doesn't load the
3094             entire file into memory. The biggest downside of this method is that
3095             if the file fails, it fully fails and you have to start from scratch.
3096              
3097             # Upload the file in resumable chunks
3098             my $data = upload_file_content_multiple(
3099             $upload_uri, $file, $optional_chunk_size,
3100             );
3101              
3102             This method will attempt to upload the file in chunks of 10 MB (or a
3103             whatever chunk size you ask for - in bytes) until it successfully
3104             finishes. If it fails, you might be able to resume. However, resuming
3105             is not yet supported within the API, sorry.
3106              
3107             =head2 UPLOADING FILES WITH EVENT LOOPS
3108              
3109             If you want to connect file uploading to an event loop, you can use
3110             C<upload_file_content_iterator()> to receive an iterator which will
3111             generate a proper L<HTTP::Request> object you can then use in the
3112             event loop request.
3113              
3114             my $iterator = upload_file_content_iterator(
3115             $upload_uir, $file, $optional_chunk_size,
3116             );
3117              
3118             while ( my $request = $iter->() ) {
3119             my $response = do_something_with_request($request);
3120              
3121             # $response->code() == 200 - Done
3122             # $response->code() == 308 - Keep going
3123             # anything else - Probably some form of error
3124             }
3125              
3126             There is currently no sample code for any particular event loop.
3127              
3128             =head1 LEGALESE
3129              
3130             Copyright 2012-2019 by Mike Schilli, all rights reserved.
3131             This program is free software, you can redistribute it and/or
3132             modify it under the same terms as Perl itself.
3133              
3134             =head1 AUTHOR
3135              
3136             Sawyer X <xsawyerx@cpan.org>