File manager - Edit - /usr/local/cpanel/3rdparty/perl/542/cpanel-lib/Net/Google/Drive/Simple/V3.pm
Back
########################################### package Net::Google::Drive::Simple::V3; ########################################### use strict; use warnings; use parent qw< Net::Google::Drive::Simple::Core >; use URI (); use URI::QueryParam (); use File::Basename (); use JSON qw< from_json >; use Log::Log4perl qw(:easy); use constant { 'HTTP_METHOD_GET' => 'GET', 'HTTP_METHOD_POST' => 'POST', 'HTTP_METHOD_PUT' => 'PUT', 'HTTP_METHOD_PATCH' => 'PATCH', 'HTTP_METHOD_DELETE' => 'DELETE', 'TYPE_STRING' => 'string', 'TYPE_INTEGER' => 'integer', 'TYPE_LONG' => 'long', 'TYPE_BOOLEAN' => 'boolean', 'TYPE_OBJECT' => 'object', 'TYPE_BYTES' => 'bytes', 'SIZE_256K' => 262_144, # default chunk size 'SIZE_5MB' => 5_242_880, # low limit 'SIZE_5120GB' => 5_497_558_138_880 # high limit }; use constant { 'DEF_CHUNK_SIZE' => SIZE_5MB() * 2 }; # 10 MB our $VERSION = '3.02'; # TODO: # * requestId are random UUIDs that we should probably generate (drive_create) # * Support for Request Body values # * Support fields format: https://developers.google.com/drive/api/guides/fields-parameter # * Add validation for path parameters # * Specific types (like ISO 639-1) # * Extended upload support: https://developers.google.com/drive/api/guides/manage-uploads # These are only the defaults my %default_deprecated_param_names = ( 'corpus' => 'corpora', 'includeTeamDriveItems' => 'includeItemsFromAllDrives', 'supportsTeamDrives' => 'supportsAllDrives', 'teamDriveId' => 'driveId', ); ########################################### sub new { ########################################### my ( $class, %options ) = @_; return $class->SUPER::new( %options, 'api_base_url' => 'https://www.googleapis.com/drive/v3/', 'api_upload_url' => 'https://www.googleapis.com/upload/drive/v3/files', # to make sure api_test() works: 'api_file_url' => 'https://www.googleapis.com/drive/v3/files', ); } ########################################### sub _validate_param_type { ########################################### my ( $self, $method, $param_data, $param_values ) = @_; my %validation_types = ( TYPE_STRING() => sub { defined and length }, TYPE_INTEGER() => sub { defined and m{^[0-9]+$}xms }, TYPE_LONG() => sub { defined and m{^[0-9]+$}xms }, TYPE_BOOLEAN() => sub { defined and m{^[01]$}xms }, TYPE_OBJECT() => sub { ref eq 'HASH' }, TYPE_BYTES() => sub { defined and length }, ); foreach my $param_name ( sort keys %{$param_values} ) { if ( !exists $param_data->{$param_name} ) { LOGDIE("[$method] Parameter name '$param_name' does not exist"); } } foreach my $param_name ( sort keys %{$param_data} ) { my ( $param_type, $is_required ) = @{ $param_data->{$param_name} }; my $param_value = $param_values->{$param_name}; # You did not provide a parameter that is needed if ( !exists $param_values->{$param_name} ) { $is_required and LOGDIE("[$method] Parameter '$param_name' is required"); next; } my $validation_cb = $validation_types{$param_type}; if ( !$validation_cb ) { LOGDIE("[$method] Parameter type '$param_type' does not exist"); } local $_ = $param_value; my $success = $validation_cb->() or LOGDIE("[$method] Parameter type '$param_name' does not validate as '$param_type'"); } return 1; } ########################################### sub _handle_complex_types { ########################################### my ( $self, $method, $options ) = @_; if ( my $perm_for_view = $options->{'includePermissionsForView'} ) { $perm_for_view eq 'published' or LOGDIE("[$method] Parameter 'includePermissionsForView' must be: published"); } my $page_size = $options->{'pageSize'}; if ( defined $page_size ) { $page_size >= 1 && $page_size <= 1_000 or LOGDIE("[$method] Parameter 'pageSize' must be: 1 to 1000"); } if ( my $upload_type = $options->{'uploadType'} ) { $upload_type =~ /^( media | multipart | resumable )$/xms or LOGDIE("[$method] Parameter 'uploadType' must be: media|multipart|resumable"); } return 1; } ########################################### sub _generate_uri { ########################################### my ( $self, $path, $options ) = @_; my $uri = URI->new( $path =~ /^http/xms ? $path : $self->{'api_base_url'} . $path ); $options && %{$options} and $uri->query_form($options); return $uri; } ########################################### sub _handle_deprecated_params { ########################################### my ( $self, $method, $info, $options ) = @_; foreach my $dep_name ( sort keys %{$options} ) { my $alt = $info->{'deprecated_param_names'}{$dep_name} || $default_deprecated_param_names{$dep_name}; if ($alt) { WARN("[$method] Parameter name '$dep_name' is deprecated, use '$alt' instead"); } } return; } ########################################### sub _prepare_body_options { ########################################### my ( $self, $options, $body_param_names ) = @_; my $body_options = { map +( exists $options->{$_} ? ( $_ => delete $options->{$_} ) : () ), @{$body_param_names}, }; keys %{$body_options} == 0 and return; return $body_options; } ########################################### sub _handle_api_method { ########################################### my ( $self, $info, $options ) = @_; my $method = $info->{'method_name'}; # We yank out all the body parameters so we don't validate them # We reuse the same key to store the key + value this time (instead of which keys to use in options) # TODO: Support body parameter validation my $body_parameters = delete $info->{'body_parameters'}; $info->{'body_parameters'} //= $self->_prepare_body_options( $options, $body_parameters ); # We validate the options left $self->_validate_param_type( $method, $info->{'query_parameters'}, $options ); # We might have a more complicated type checking specified if ( my $param_check = $info->{'parameter_checks'} ) { foreach my $name ( sort keys %{$param_check} ) { defined $options->{$name} or next; my $cb = $param_check->{$name}; ref $cb eq 'CODE' or LOGDIE("[$method] Parameter '$name' missing validation callback"); local $_ = $options->{$name}; my $error_str = $cb->(); $error_str and LOGDIE("[$method] Parameter '$name' failed validation: $error_str"); } } # We check for deprecated parameters (from a list of known deprecated parameters) $self->_handle_deprecated_params( $method, $info, $options ); # We handle some more specific validation rules (from a list of known parameters) $self->_handle_complex_types( $method, $options ); $self->init(); # We generate the URI path my $uri = $self->_generate_uri( $info->{'path'}, $options ); my $req = $self->_generate_request( $uri, $info ); if ( $info->{'return_http_request'} ) { return $req; } return $self->_make_request( $req, $info->{'return_http_response'} ); } # --- about ########################################### sub about { ########################################### my ( $self, $options ) = @_; ref $options eq 'HASH' or LOGDIE('about() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], }, 'path' => 'about', 'method_name' => 'about', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } # --- changes ########################################### sub getStartPageToken { ########################################### my ( $self, $options ) = @_; $options //= {}; my $info = { 'query_parameters' => { 'driveId' => [ TYPE_STRING(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], # Deprecated 'teamDriveId' => [ TYPE_STRING(), 0 ], # Deprecated }, 'path' => 'changes/startPageToken', 'http_method' => HTTP_METHOD_GET(), 'method_name' => 'getStartPageToken', }; return $self->_handle_api_method( $info, $options ); } ########################################### sub changes { ########################################### my ( $self, $options ) = @_; ref $options eq 'HASH' or LOGDIE('changes() missing parameters'); my $info = { 'query_parameters' => { 'pageToken' => [ TYPE_STRING(), 1 ], 'driveId' => [ TYPE_STRING(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], 'includeCorpusRemovals' => [ TYPE_BOOLEAN(), 0 ], 'includeItemsFromAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'includeRemoved' => [ TYPE_BOOLEAN(), 0 ], 'includeTeamDriveItems' => [ TYPE_BOOLEAN(), 0 ], 'pageSize' => [ TYPE_INTEGER(), 0 ], 'restrictToMyDrive' => [ TYPE_BOOLEAN(), 0 ], 'spaces' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'teamDriveId' => [ TYPE_STRING(), 0 ], }, 'parameter_checks' => { 'spaces' => sub { /^( drive | appDataFolder | photos )$/xms or return 'must be: drive|appDataFolder|photos'; return 0; }, }, 'path' => 'changes', 'http_method' => HTTP_METHOD_GET(), 'method_name' => 'changes', }; return $self->_handle_api_method( $info, $options ); } ########################################### sub watch_changes { ########################################### my ( $self, $options ) = @_; $options //= {}; my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 0 ], }, 'body_parameters' => [ qw< kind id resourceId resourceUri token expiration type address payload params > ], 'path' => 'changes/watch', 'http_method' => HTTP_METHOD_POST(), 'method_name' => 'watch_changes', }; $options->{'kind'} = 'api#channel'; return $self->_handle_api_method( $info, $options ); } # --- channels ########################################### sub stop_channels { ########################################### my ( $self, $options ) = @_; ref $options eq 'HASH' or LOGDIE('stop_channels() missing parameters'); my $info = { 'body_parameters' => [ qw< kind id resourceId resourceUri token expiration type address payload params > ], 'parameter_checks' => { 'type' => sub { m{^web_?hook$}xms or return 'must be: webhook|web_hook'; return 0; }, }, 'path' => 'channels/stop', 'http_method' => HTTP_METHOD_POST(), 'method_name' => 'stop_channels', }; $options->{'kind'} = 'api#channel'; return $self->_handle_api_method( $info, $options ); } # --- comments ########################################### sub create_comment { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('create_comment() missing file ID'); ref $options eq 'HASH' or LOGDIE('create_comment() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], }, 'body_parameters' => [ qw< content anchor quotedFileContent > ], 'path' => "files/$fileId/comments", 'method_name' => 'create_comment', 'http_method' => HTTP_METHOD_POST(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub delete_comment { ########################################### my ( $self, $fileId, $commentId ) = @_; defined $fileId && length $fileId or LOGDIE('create_comment() missing file ID'); defined $commentId && length $commentId or LOGDIE('create_comment() missing comment ID'); my $info = { 'path' => "files/$fileId/comments/$commentId", 'method_name' => 'delete_comment', 'http_method' => HTTP_METHOD_DELETE(), }; return $self->_handle_api_method( $info, {} ); } ########################################### sub get_comment { ########################################### my ( $self, $fileId, $commentId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('get_comment() missing file ID'); defined $commentId && length $commentId or LOGDIE('get_comment() missing comment ID'); ref $options eq 'HASH' or LOGDIE('get_comment() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], 'includeDeleted' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "files/$fileId/comments/$commentId", 'method_name' => 'get_comment', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub comments { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('comments() missing file ID'); ref $options eq 'HASH' or LOGDIE('comments() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], 'includeDeleted' => [ TYPE_BOOLEAN(), 0 ], 'pageSize' => [ TYPE_INTEGER(), 0 ], 'pageToken' => [ TYPE_STRING(), 0 ], 'startModifiedTime' => [ TYPE_STRING(), 0 ], }, 'path' => "files/$fileId/comments", 'method_name' => 'comments', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub update_comment { ########################################### my ( $self, $fileId, $commentId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('update_comment() missing file ID'); defined $commentId && length $commentId or LOGDIE('update_comment() missing comment ID'); ref $options eq 'HASH' or LOGDIE('update_comment() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], }, 'body_parameters' => [ qw< content > ], 'path' => "files/$fileId/comments/$commentId", 'method_name' => 'update_comment', 'http_method' => HTTP_METHOD_PATCH(), }; return $self->_handle_api_method( $info, $options ); } # --- files ########################################### sub copy_file { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('copy_file() missing file ID'); ref $options eq 'HASH' or LOGDIE('copy_file() missing parameters'); # TODO: ocrLanguage is ISO 639-1 code my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 0 ], 'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], }, 'body_parameters' => [ qw< appProperties contentHints contentRestrictions copyRequiresWriterPermission description id mimeType modifiedTime name parents properties starred viewedByMeTime writersCanShare > ], 'path' => "files/$fileId/copy", 'method_name' => 'copy_file', 'http_method' => HTTP_METHOD_POST(), }; return $self->_handle_api_method( $info, $options ); } # Helper ########################################### sub create_folder { ########################################### my ( $self, $name, $parent_id ) = @_; defined $name && length $name or LOGDIE('create_folder() missing name'); defined $parent_id && length $parent_id or LOGDIE('create_folder() missing parent_id'); return $self->create_file( { 'name' => $name, 'mimeType' => "application/vnd.google-apps.folder", 'parents' => [$parent_id], } ); } # Metadata only ########################################### sub create_file { ########################################### my ( $self, $options ) = @_; ref $options eq 'HASH' or LOGDIE('create_file() missing parameters'); my $info = { 'query_parameters' => { 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ] }, 'body_parameters' => [ qw< appProperties contentHints contentRestrictions copyRequiresWriterPermission createdTime description folderColorRgb id mimeType modifiedTime name originalFilename parents properties shortcutDetails.targetId starred viewedByMeTime writersCanShare > ], 'path' => 'files', 'method_name' => 'create_file', 'http_method' => HTTP_METHOD_POST(), }; if ( defined $options->{'enforceSingleParent'} ) { LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported"); } return $self->_handle_api_method( $info, $options ); } # Uploading file (uploadType=media) ########################################### sub upload_media_file { ########################################### my ( $self, $file, $options ) = @_; defined $file && length $file or LOGDIE('upload_media_file() missing file'); -r $file or LOGDIE("upload_media_file() received non-existent/unreadable file: $file"); my $size = -s $file; $size <= SIZE_5MB() or LOGDIE("upload_media_file() has a limit of 5M, '$file' is bigger"); $options //= {}; $options->{'uploadType'} = 'media'; my $mimeType = delete $options->{'mimeType'} // $self->file_mime_type($file); my $info = { 'query_parameters' => { 'uploadType' => [ TYPE_STRING(), 1 ], 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ] }, 'path' => $self->{'api_upload_url'}, 'method_name' => 'upload_media_file', 'http_method' => HTTP_METHOD_POST(), 'body_content' => $self->_content_sub($file), 'extra_headers' => [ 'Content-Type' => $mimeType, 'Content-Length' => $size, ], }; if ( defined $options->{'enforceSingleParent'} ) { LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported"); } # Since a file upload can take a long time, refresh the token # just in case. $self->{'oauth'}->token_expire(); return $self->_handle_api_method( $info, $options ); } ########################################### sub upload_multipart_file { ########################################### my ( $self, $file, $options ) = @_; defined $file && length $file or LOGDIE('upload_multipart_file() missing file'); -r $file or LOGDIE("upload_multipart_file() received non-existent/unreadable file: $file"); my $size = -s $file; $size <= SIZE_5MB() or LOGDIE("upload_multipart_file() has a limit of 5M, '$file' is bigger"); $options //= {}; $options->{'name'} //= File::Basename::basename($file); $options->{'mimeType'} //= $self->file_mime_type($file); $options->{'uploadType'} = 'multipart'; # TODO: Wouldn't it be great to support Chunking with a callback? my $file_content; { open my $fh, '<', $file or LOGDIE("File '$file' cannot be open for reading"); undef $/; $file_content = <$fh>; close $fh or LOGDIE("File '$file' cannot be closed after reading"); } my $info = { 'query_parameters' => { 'uploadType' => [ TYPE_STRING(), 1 ], 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ] }, 'body_parameters' => [ qw< appProperties contentHints contentRestrictions copyRequiresWriterPermission createdTime description folderColorRgb id mimeType modifiedTime name originalFilename parents properties shortcutDetails.targetId starred viewedByMeTime writersCanShare > ], 'path' => $self->{'api_upload_url'}, 'method_name' => 'upload_multipart_file', 'http_method' => HTTP_METHOD_POST(), 'multipart' => 1, 'body_content' => $file_content, }; if ( defined $options->{'enforceSingleParent'} ) { LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported"); } # Since a file upload can take a long time, refresh the token # just in case. $self->{'oauth'}->token_expire(); return $self->_handle_api_method( $info, $options ); } ########################################### sub create_resumable_upload_for { ########################################### my ( $self, $file, $options ) = @_; defined $file && length $file or LOGDIE('create_resumable_upload_for() missing file'); -r $file or LOGDIE("create_resumable_upload_for() received non-existent/unreadable file: $file"); my $size = -s $file; $size <= SIZE_5120GB() or LOGDIE("create_resumavble_upload_for() has a limit of 5120G, '$file' is bigger"); $options //= {}; $options->{'name'} //= File::Basename::basename($file); $options->{'mimeType'} //= $self->file_mime_type($file); $options->{'uploadType'} = 'resumable'; my @extra_headers = ( 'X-Upload-Content-Type' => $options->{'mimeType'}, 'X-Upload-Content-Length' => $size, ); # Do we have metadata other than uploadType? if ( keys %{$options} > 1 ) { push @extra_headers, 'Content-Type' => 'application/json; charset=UTF-8'; } my $info = { 'query_parameters' => { 'uploadType' => [ TYPE_STRING(), 1 ], 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ] }, 'body_parameters' => [ qw< appProperties contentHints contentRestrictions copyRequiresWriterPermission createdTime description folderColorRgb id mimeType modifiedTime name originalFilename parents properties shortcutDetails.targetId starred viewedByMeTime writersCanShare > ], 'path' => $self->{'api_upload_url'}, 'method_name' => 'create_resumable_upload', 'http_method' => HTTP_METHOD_POST(), 'extra_headers' => \@extra_headers, 'resumable' => 1, 'return_http_response' => 1, }; if ( defined $options->{'enforceSingleParent'} ) { LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported"); } # Since a file upload can take a long time, refresh the token # just in case. $self->{'oauth'}->token_expire(); my $response = $self->_handle_api_method( $info, $options ); $response->is_success() or return; my $location = $response->header('Location'); return $location || ''; } ########################################### sub create_resumable_upload { ########################################### my ( $self, $options ) = @_; $options //= {}; $options->{'uploadType'} = 'resumable'; my @extra_headers; if ( my $mimeType = delete $options->{'mediaType'} ) { push @extra_headers, 'X-Upload-Content-Type' => $mimeType; } # Do we have metadata other than uploadType? if ( keys %{$options} > 1 ) { push @extra_headers, 'Content-Type' => 'application/json; charset=UTF-8'; } my $info = { 'query_parameters' => { 'uploadType' => [ TYPE_STRING(), 1 ], 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'ignoreDefaultVisibility' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ] }, 'body_parameters' => [ qw< appProperties contentHints contentRestrictions copyRequiresWriterPermission createdTime description folderColorRgb id mimeType modifiedTime name originalFilename parents properties shortcutDetails.targetId starred viewedByMeTime writersCanShare > ], 'path' => $self->{'api_upload_url'}, 'method_name' => 'create_resumable_upload', 'http_method' => HTTP_METHOD_POST(), 'extra_headers' => \@extra_headers, 'resumable' => 1, 'return_http_response' => 1, }; if ( defined $options->{'enforceSingleParent'} ) { LOGDIE("[$info->{'method_name'}] Creating files in multiple folders is no longer supported"); } # Since a file upload can take a long time, refresh the token # just in case. $self->{'oauth'}->token_expire(); my $response = $self->_handle_api_method( $info, $options ); $response->is_success() or return; my $location = $response->header('Location'); return $location || ''; } ########################################### sub upload_file_content_single { ########################################### my ( $self, $upload_uri, $file ) = @_; defined $upload_uri && length $upload_uri or LOGDIE('upload_file_content_single() missing upload_uri'); $upload_uri =~ m{^https://.*\bupload_id=.+}xmsi or LOGDIE('upload_file_content_single() upload_uri seems malformed'); defined $file && length $file or LOGDIE('upload_file_content_single() missing file'); -r $file or LOGDIE("upload_file_content_single() received non-existent/unreadable file: $file"); my $size = -s $file; $size <= SIZE_5120GB() or LOGDIE("upload_file_content_single() has a limit of 5120G, '$file' is bigger"); my $info = { 'path' => $upload_uri, 'method_name' => 'upload_file_content_single', 'http_method' => HTTP_METHOD_PUT(), 'body_content' => $self->_content_sub($file), }; # Since a file upload can take a long time, refresh the token # just in case. $self->{'oauth'}->token_expire(); return $self->_handle_api_method( $info, {} ); } ########################################### sub upload_file_content_multiple { ########################################### my ( $self, $upload_uri, $file, $chunk_size ) = @_; defined $upload_uri && length $upload_uri or LOGDIE('upload_file_content_multiple() missing upload_uri'); $upload_uri =~ m{^https://.*upload_id=.+}xms or LOGDIE('upload_file_content_multiple() upload_uri seems malformed'); defined $file && length $file or LOGDIE('upload_file_content_multiple() missing file'); -r $file or LOGDIE("upload_file_content_multiple() received non-existent/unreadable file: $file"); my $size = -s $file; $size <= SIZE_5120GB() or LOGDIE("upload_file_content_multiple() has a limit of 5120G, '$file' is bigger"); # By upload in 10 MB chunks # But you can provide the amount of bytes instead $chunk_size //= DEF_CHUNK_SIZE(); $chunk_size > 0 or LOGDIE('upload_file_content_multiple() must have a chunk size above 0'); $chunk_size % SIZE_256K() == 0 or LOGDIE('upload_file_content_multiple() chunk size must divide by 256K'); my $iter = $self->upload_file_content_iterator( $upload_uri, $file, $chunk_size ); while ( my $request = $iter->() ) { my $response = $self->_make_request( $request, 1 ); # 200 - means we're done # 308 - means continue # anything else - we're confused, so it's an error no matter what if ( $response->code() == Net::Google::Drive::Simple::Core::HTTP_CODE_OK() ) { return from_json( $response->decoded_content() ); } if ( $response->code() != Net::Google::Drive::Simple::Core::HTTP_CODE_RESUME() ) { LOGDIE( "Triggered error: " . $response->code() ); } } return; } ########################################### sub upload_file_content_iterator { ########################################### my ( $self, $upload_uri, $file, $chunk_size ) = @_; defined $upload_uri && length $upload_uri or LOGDIE('upload_file_content_multiple() missing upload_uri'); $upload_uri =~ m{^https://.*upload_id=.+}xms or LOGDIE('upload_file_content_multiple() upload_uri seems malformed'); defined $file && length $file or LOGDIE('upload_file_content_multiple() missing file'); -r $file or LOGDIE("upload_file_content_multiple() received non-existent/unreadable file: $file"); my $size = -s $file; $size <= SIZE_5120GB() or LOGDIE("upload_file_content_multiple() has a limit of 5120G, '$file' is bigger"); # By upload in 10 MB chunks # But you can provide the amount of bytes instead $chunk_size //= DEF_CHUNK_SIZE(); $chunk_size > 0 or LOGDIE('upload_file_content_multiple() must have a chunk size above 0'); $chunk_size % SIZE_256K() == 0 or LOGDIE('upload_file_content_multiple() chunk size must divide by 256K'); # How many chunks are we going to iterate over # size in bytes divided by 256K (chunk size) my $chunks = ( $size / $chunk_size ); # One more if there's a tail $size % $chunk_size and $chunks++; my $position = 0; my $iter = sub { $chunks-- == 0 and return; my $start_position = $position; # TODO: Should we take the approach of _content_sub with opening usnig IO::File? open my $fh, '<', $file or LOGDIE("File '$file' cannot be open for reading"); my $content; sysseek $fh, $start_position, 0; my $bytes_read = sysread $fh, $content, $chunk_size; $position += $bytes_read; close $fh or LOGDIE("File '$file' cannot be closed after reading"); my $info = { 'path' => $upload_uri, 'method_name' => 'upload_file_content_multiple', 'http_method' => HTTP_METHOD_PUT(), 'return_http_request' => 1, 'body_content' => $content, 'extra_headers' => [ 'Content-Length' => $bytes_read, 'Content-Range' => "bytes $start_position-" . ( $position - 1 ) . "/$size" #. ( $position + $bytes_read ), ], }; # Since a file upload can take a long time, refresh the token # just in case. $self->{'oauth'}->token_expire(); my $request = $self->_handle_api_method( $info, {} ); return $request; }; return $iter; } ########################################### sub upload_file { ########################################### my ( $self, $file, $options ) = @_; my $upload_uri = $self->create_resumable_upload_for( $file, $options ); return $self->upload_file_content_single( $upload_uri, $file ); } ########################################### sub delete_file { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('delete_file() missing file ID'); $options //= {}; my $info = { 'query_parameters' => { 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "files/$fileId", 'method_name' => 'delete_file', 'http_method' => HTTP_METHOD_DELETE(), }; if ( defined $options->{'enforceSingleParent'} ) { 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"); } return $self->_handle_api_method( $info, $options ); } ########################################### sub export_file { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('export_file() missing file ID'); ref $options eq 'HASH' or LOGDIE('export_file() missing parameters'); my $info = { 'query_parameters' => { 'mimeType' => [ TYPE_STRING(), 1 ], 'fields' => [ TYPE_STRING(), 0 ], }, 'path' => "files/$fileId/export", 'method_name' => 'export_file', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub generateIds { ########################################### my ( $self, $options ) = @_; $options //= {}; my $info = { 'query_parameters' => { 'count' => [ TYPE_INTEGER(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], 'space' => [ TYPE_STRING(), 0 ], 'type' => [ TYPE_STRING(), 0 ], }, 'parameter_checks' => { 'spaces' => sub { /^( files | shortcuts )$/xms or return 'must be: files|shortcuts'; return 0; } }, 'path' => 'files/generateIds', 'method_name' => 'generateIds', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub get_file { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('get_file() missing file ID'); $options //= {}; my $info = { 'query_parameters' => { 'alt' => [ TYPE_STRING(), 0 ], 'acknowledgeAbuse' => [ TYPE_BOOLEAN(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], }, 'parameter_checks' => { 'alt' => sub { $_ eq 'media' or return 'must be: media'; return 0; }, }, 'path' => "files/$fileId", 'method_name' => 'get_file', 'http_method' => HTTP_METHOD_GET(), }; if ( $options->{'alt'} eq 'media' ) { $info->{'return_http_response'} = 1; } my $response = $self->_handle_api_method( $info, $options ); return $response->decoded_content(); } ########################################### sub files { ########################################### my ( $self, $options ) = @_; $options //= {}; # TODO: orderBy # 'createdTime', 'folder', 'modifiedByMeTime', 'modifiedTime', 'name', 'name_natural', 'quotaBytesUsed', 'recency', 'sharedWithMeTime', 'starred', and 'viewedByMeTime'. # ?orderBy=folder,modifiedTime desc,name # TODO: Steal the struct to query syntax from MetaCPAN::Client # https://developers.google.com/drive/api/guides/search-files #if ( my $query_str = $options->{'q'} ) {...} my $info = { 'query_parameters' => { 'corpora' => [ TYPE_STRING(), 0 ], 'corpus' => [ TYPE_STRING(), 0 ], # deprecated 'driveId' => [ TYPE_STRING(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], 'includeItemsFromAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'includeTeamDriveItems' => [ TYPE_BOOLEAN(), 0 ], # deprecated 'orderBy' => [ TYPE_STRING(), 0 ], 'pageSize' => [ TYPE_INTEGER(), 0 ], 'pageToken' => [ TYPE_STRING(), 0 ], 'q' => [ TYPE_STRING(), 0 ], 'spaces' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], # deprecated 'teamDriveId' => [ TYPE_STRING(), 0 ], # deprecated 'trashed' => [ TYPE_BOOLEAN(), 0 ], # not documented }, 'parameter_checks' => { 'corpora' => sub { /^( user | drive | domain | allDrives )$/xms or return 'must be: user|drive|domain|allDrives'; if ( $_ eq 'drive' ) { defined $options->{'driveId'} or return 'if corpora is drive, parameter driveId must be set'; } return 0; }, 'corpus' => sub { /^( domain | user )$/xms or return 'must be: domain|user'; return 0; }, 'spaces' => sub { /^( drive | appDataFolder )$/xms or return 'must be: drive|appDataFolder'; return 0; }, }, 'path' => 'files', 'method_name' => 'files', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } # Update with upload ########################################### sub update_file { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('update_file() missing file ID'); ref $options eq 'HASH' or LOGDIE('update_file() missing parameters'); my $info = { 'query_parameters' => { 'uploadType' => [ TYPE_STRING(), 1 ], 'addParents' => [ TYPE_STRING(), 0 ], 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'removeParents' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ], }, 'body_parameters' => [ qw< appProperties contentHints contentRestrictions copyRequiresWriterPermission description folderColorRgb mimeType modifiedTime name originalFilename properties starred trashed viewedByMeTime writersCanShare > ], 'path' => $self->{'api_upload_file'} . "/$fileId", 'method_name' => 'update_file', 'http_method' => HTTP_METHOD_PATCH(), }; if ( defined $options->{'enforceSingleParent'} ) { LOGDIE("[$info->{'method_name'}] Adding files to multiple folders is no longer supported. Use shortcuts instead"); } return $self->_handle_api_method( $info, $options ); } # Metadata ########################################### sub update_file_metadata { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('update_file_metadata() missing file ID'); ref $options eq 'HASH' or LOGDIE('update_file_metadata() missing parameters'); my $info = { 'query_parameters' => { 'addParents' => [ TYPE_STRING(), 0 ], 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'keepRevisionForever' => [ TYPE_BOOLEAN(), 0 ], 'ocrLanguage' => [ TYPE_STRING(), 0 ], 'removeParents' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useContentAsIndexableText' => [ TYPE_BOOLEAN(), 0 ], }, 'body_parameters' => [ qw< appProperties contentHints contentRestrictions copyRequiresWriterPermission description folderColorRgb mimeType modifiedTime name originalFilename properties starred trashed viewedByMeTime writersCanShare > ], 'path' => "files/$fileId", 'method_name' => 'update_file_metadata', 'http_method' => HTTP_METHOD_PATCH(), }; if ( defined $options->{'enforceSingleParent'} ) { LOGDIE("[$info->{'method_name'}] Adding files to multiple folders is no longer supported. Use shortcuts instead"); } return $self->_handle_api_method( $info, $options ); } ########################################### sub watch_file { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('watch_file() missing file ID'); ref $options eq 'HASH' or LOGDIE('watch_file() missing parameters'); my $info = { 'query_parameters' => { 'acknowledgeAbuse' => [ TYPE_BOOLEAN(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], }, 'body_parameters' => [ qw< kind id resourceId resourceUri token expiration type address payload params > ], 'path' => "files/$fileId/watch", 'method_name' => 'watch_file', 'http_method' => HTTP_METHOD_POST(), }; $options->{'kind'} = 'api#channel'; return $self->_handle_api_method( $info, $options ); } ########################################### sub empty_trash { ########################################### my ( $self, $options ) = @_; $options //= {}; my $info = { 'query_parameters' => { 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => 'files/trash', 'method' => 'empty_trash', 'http_method' => HTTP_METHOD_DELETE(), }; if ( defined $options->{'enforceSingleParent'} ) { 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"); } return $self->_handle_api_method( $info, {} ); } # --- permissions ########################################### sub create_permission { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('create_permission() missing file ID'); ref $options eq 'HASH' or LOGDIE('create_permission() missing parameters'); my $info = { 'query_parameters' => { 'emailMessage' => [ TYPE_STRING(), 0 ], 'enforceSingleParent' => [ TYPE_BOOLEAN(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], 'moveToNewOwnersRoot' => [ TYPE_BOOLEAN(), 0 ], 'sendNotificationEmail' => [ TYPE_BOOLEAN(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'transferOwnership' => [ TYPE_BOOLEAN(), 0 ], 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'body_parameters' => [ qw< role type allowFileDiscovery domain emailAddress pendingOwner view > ], 'deprecated_param_names' => { 'enforceSingleParent' => 'moveToNewOwnersRoot', }, 'path' => "files/$fileId/permissions", 'method_name' => 'create_permission', 'http_method' => HTTP_METHOD_POST(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub delete_permission { ########################################### my ( $self, $fileId, $permissionId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('delete_permission() missing file ID'); defined $permissionId && length $permissionId or LOGDIE('delete_permission() missing permission ID'); $options //= {}; my $info = { 'query_parameters' => { 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "files/$fileId/permissions/$permissionId", 'method_name' => 'delete_permission', 'http_method' => HTTP_METHOD_DELETE(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub get_permission { ########################################### my ( $self, $fileId, $permissionId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('get_permission() missing file ID'); defined $permissionId && length $permissionId or LOGDIE('get_permission() missing permission ID'); $options //= {}; my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 0 ], 'supportsAllDrivee' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "files/$fileId/permissions/$permissionId", 'method_name' => 'get_permission', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub permissions { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('permissions() missing file ID'); $options //= {}; my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 0 ], 'includePermissionsForView' => [ TYPE_STRING(), 0 ], 'pageSize' => [ TYPE_INTEGER(), 0 ], 'pageToken' => [ TYPE_STRING(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "files/$fileId/permissions", 'method_name' => 'permissions', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub update_permission { ########################################### my ( $self, $fileId, $permissionId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('update_permission() missing file ID'); defined $permissionId && length $permissionId or LOGDIE('update_permission() missing permission ID'); $options //= {}; my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 0 ], 'removeExpiration' => [ TYPE_BOOLEAN(), 0 ], 'supportsAllDrives' => [ TYPE_BOOLEAN(), 0 ], 'supportsTeamDrives' => [ TYPE_BOOLEAN(), 0 ], 'transferOwnership' => [ TYPE_BOOLEAN(), 0 ], 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "files/$fileId/permissions/$permissionId", 'method_name' => 'update_permission', 'http_method' => HTTP_METHOD_PATCH(), }; return $self->_handle_api_method( $info, $options ); } # --- replies ########################################### sub create_reply { ########################################### my ( $self, $fileId, $commentId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('create_reply() missing file ID'); defined $commentId && length $commentId or LOGDIE('create_reply() missing comment ID'); ref $options eq 'HASH' or LOGDIE('create_reply() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], }, 'body_parameters' => [ qw< action content > ], 'parameter_checks' => { 'action' => sub { m{^( resolve | reopen )$}xms or return 'must be: resolve|reopen'; return 0; }, }, 'path' => "files/$fileId/comments/$commentId/replies", 'method_name' => 'create_reply', 'http_method' => HTTP_METHOD_POST(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub delete_reply { ########################################### my ( $self, $fileId, $commentId, $replyId ) = @_; defined $fileId && length $fileId or LOGDIE('delete_reply() missing file ID'); defined $commentId && length $commentId or LOGDIE('delete_reply() missing comment ID'); my $info = { 'path' => "files/$fileId/comments/$commentId/replies/$replyId", 'method_name' => 'delete_reply', 'http_method' => HTTP_METHOD_DELETE(), }; return $self->_handle_api_method( $info, {} ); } ########################################### sub get_reply { ########################################### my ( $self, $fileId, $commentId, $replyId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('get_reply() missing file ID'); defined $commentId && length $commentId or LOGDIE('get_reply() missing comment ID'); defined $replyId && length $replyId or LOGDIE('get_reply() missing reply ID'); ref $options eq 'HASH' or LOGDIE('get_reply() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], 'includeDeleted' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "files/$fileId/comments/$commentId/replies/$replyId", 'method_name' => 'get_reply', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub replies { ########################################### my ( $self, $fileId, $commentId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('replies() missing file ID'); defined $commentId && length $commentId or LOGDIE('replies() missing comment ID'); ref $options eq 'HASH' or LOGDIE('replies() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], 'includeDeleted' => [ TYPE_BOOLEAN(), 0 ], 'pageSize' => [ TYPE_INTEGER(), 0 ], 'pageToken' => [ TYPE_STRING(), 0 ], }, 'path' => "files/$fileId/comments/$commentId/replies", 'method_name' => 'replies', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub update_reply { ########################################### my ( $self, $fileId, $commentId, $replyId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('update_reply() missing file ID'); defined $commentId && length $commentId or LOGDIE('update_reply() missing comment ID'); defined $replyId && length $replyId or LOGDIE('update_reply() missing reply ID'); ref $options eq 'HASH' or LOGDIE('update_reply() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 1 ], }, 'body_parameters' => { 'content' => [ TYPE_STRING(), 1 ], }, 'path' => "files/$fileId/comments/$commentId/replies/$replyId", 'method_name' => 'update_reply', 'http_method' => HTTP_METHOD_PATCH(), }; return $self->_handle_api_method( $info, $options ); } # --- revisions ########################################### sub delete_revision { ########################################### my ( $self, $fileId, $revisionId ) = @_; defined $fileId && length $fileId or LOGDIE('delete_revision() missing file ID'); my $info = { 'path' => "files/$fileId/revisions/$revisionId", 'method_name' => 'delete_revision', 'http_method' => HTTP_METHOD_DELETE(), }; return $self->_handle_api_method( $info, {} ); } ########################################### sub get_revision { ########################################### my ( $self, $fileId, $revisionId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('get_revision() missing file ID'); defined $revisionId && length $revisionId or LOGDIE('get_revision() missing revision ID'); $options //= {}; my $info = { 'query_parameters' => { 'acknowledgeAbuse' => [ TYPE_BOOLEAN(), 0 ], 'fields' => [ TYPE_STRING(), 0 ], }, 'path' => "files/$fileId/revisions/$revisionId", 'method_name' => 'get_revision', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub revisions { ########################################### my ( $self, $fileId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('revisions() missing file ID'); $options //= {}; my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 0 ], 'pageSize' => [ TYPE_INTEGER(), 0 ], 'pageToken' => [ TYPE_STRING(), 0 ], }, 'path' => "files/$fileId/revisions", 'method_name' => 'revisions', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub update_revision { ########################################### my ( $self, $fileId, $revisionId, $options ) = @_; defined $fileId && length $fileId or LOGDIE('update_revision() missing file ID'); defined $revisionId && length $revisionId or LOGDIE('update_revision() missing revision ID'); ref $options eq 'HASH' or LOGDIE('update_revision() missing parameters'); my $info = { 'query_parameters' => { 'fields' => [ TYPE_STRING(), 0 ], }, 'body_parameter' => [ qw< keepForever publishAuto published publishedOutsideDomain > ], 'path' => "files/$fileId/revisions/$revisionId", 'method_name' => 'update_revision', 'http_method' => HTTP_METHOD_PATCH(), }; return $self->_handle_api_method( $info, $options ); } # --- drives ########################################### sub create_drive { ########################################### my ( $self, $options ) = @_; ref $options eq 'HASH' or LOGDIE('create_drive() missing parameters'); my $info = { 'query_parameters' => { 'requestId' => [ TYPE_STRING(), 1 ], }, 'body_parameters' => { 'name' => [ TYPE_STRING(), 1 ], 'themeId' => [ TYPE_STRING(), 0 ], }, 'path' => 'drives', 'method_name' => 'create_drive', 'http_method' => HTTP_METHOD_POST(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub delete_drive { ########################################### my ( $self, $driveId ) = @_; defined $driveId && length $driveId or LOGDIE('delete_drive() missing drive ID'); my $info = { 'path' => "drives/$driveId", 'method_name' => 'delete_drive', 'http_method' => HTTP_METHOD_DELETE(), }; return $self->_handle_api_method( $info, {} ); } ########################################### sub get_drive { ########################################### my ( $self, $driveId, $options ) = @_; defined $driveId && length $driveId or LOGDIE('get_drive() missing drive ID'); $options //= {}; my $info = { 'query_parameters' => { 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => "drives/$driveId", 'method_name' => 'get_drive', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub hide_drive { ########################################### my ( $self, $driveId ) = @_; defined $driveId && length $driveId or LOGDIE('hide_drive() missing drive ID'); my $info = { 'path' => "drives/$driveId/hide", 'method_name' => 'hide_drive', 'http_method' => HTTP_METHOD_POST(), }; return $self->_handle_api_method( $info, {} ); } ########################################### sub drives { ########################################### my ( $self, $options ) = @_; $options //= {}; my $info = { 'query_parameters' => { 'pageSize' => [ TYPE_INTEGER(), 0 ], 'pageToken' => [ TYPE_STRING(), 0 ], 'q' => [ TYPE_STRING(), 0 ], 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'path' => 'drives', 'method_name' => 'drives', 'http_method' => HTTP_METHOD_GET(), }; return $self->_handle_api_method( $info, $options ); } ########################################### sub unhide_drive { ########################################### my ( $self, $driveId ) = @_; defined $driveId && length $driveId or LOGDIE('unhide_drive() missing drive ID'); my $info = { 'path' => "drives/$driveId/unhide", 'method_name' => 'unhide_drive', 'http_method' => HTTP_METHOD_POST(), }; return $self->_handle_api_method( $info, {} ); } ########################################### sub update_drive { ########################################### my ( $self, $driveId, $options ) = @_; defined $driveId && length $driveId or LOGDIE('update_drive() missing drive ID'); ref $options eq 'HASH' or LOGDIE('update_drive() missing parameters'); my $info = { 'query_parameters' => { 'useDomainAdminAccess' => [ TYPE_BOOLEAN(), 0 ], }, 'body_parameters' => [ qw< backgroundImageFile colorRgb name restrictions themeId > ], 'path' => "drives/$driveId", 'method_name' => 'update_drive', 'http_method' => HTTP_METHOD_PATCH(), }; return $self->_handle_api_method( $info, $options ); } # Helper methods ########################################### sub children { ########################################### my ( $self, $path, $opts, $search_opts ) = @_; DEBUG("Determine children of $path"); LOGDIE("No $path given") unless defined $path; $opts //= {}; $search_opts //= {}; $opts->{'maxResults'} and LOGDIE("'maxResults' not supported, use 'pagesize' instead"); my ( $folder_id, $parent ) = $self->_path_resolve( $path, $opts, $search_opts ); return unless defined $folder_id; DEBUG("Getting content of folder $folder_id"); my $children = $self->children_by_folder_id( $folder_id, $opts, $search_opts ) or return; return $children; } ########################################### sub children_by_folder_id { ########################################### my ( $self, $folder_id, $opts, $search_opts ) = @_; $folder_id or LOGDIE('Must provide a folder id'); $self->init(); $opts = {} unless defined $opts; $search_opts = {} unless defined $search_opts; exists $search_opts->{'page'} and LOGDIE("Search option 'page' is deprecated, use 'auto_paging'"); exists $search_opts->{'title'} and LOGDIE("Search option 'title' is deprecated, set 'q' parameter with 'name' accordingly"); $search_opts->{'auto_paging'} //= 1; # Append or create a search in folder if ( defined $opts->{'q'} && length $opts->{'q'} ) { $opts->{'q'} .= ' AND '; } else { $opts->{'q'} = ''; } $opts->{'q'} .= "'$folder_id' in parents"; if ( my $name = $search_opts->{'name'} ) { $name =~ s{\'}{\\\'}xmsg; $opts->{'q'} .= " AND name = '$name'"; } $opts->{'fields'} //= ''; if ( $opts->{'fields'} ) { $opts->{'fields'} .= ','; } $opts->{'fields'} .= 'files(id,kind,name,mimeType,parents,originalFilename,trashed)'; # Find only those not in the trash # possibly go through all paged results my @children; while (1) { my $data = $self->files($opts) or return; my @items = @{ $data->{'files'} || [] }; while ( my $item = shift @items ) { if ( $item->{'trashed'} ) { DEBUG("Skipping $item->{'name'} (item in trash)"); next; } # use the Item.pm object with AUTOLOAD, is_folder, and is_file # TODO: I dislke the AUTOLOAD format... push @children, $self->data_factory($item); } if ( $search_opts->{'auto_paging'} && $data->{'nextPageToken'} ) { $opts->{'pageToken'} = $data->{'nextPageToken'}; } else { last; } } return \@children; } ########################################### sub _path_resolve { ########################################### my ( $self, $path, $opts, $search_opts ) = @_; $opts = {} if !defined $opts; $search_opts = {} if !defined $search_opts; my @parts = grep length, split '/', $path; my @ids = qw(root); my $folder_id = my $parent = 'root'; DEBUG("Parent: $parent"); PART: foreach my $part (@parts) { DEBUG("Looking up part $part (folder_id=$folder_id)"); # We append to 'q' parameter in case the user provided it my $name = $part =~ s{\'}{\\\'}xmsgr; my $children = $self->children_by_folder_id( $folder_id, {}, { %{$search_opts}, 'name' => $name }, ) or return; for my $child (@$children) { DEBUG( "Found child: " . $child->name() ); if ( $child->name() eq $part ) { $folder_id = $child->id(); unshift @ids, $folder_id; $parent = $folder_id; DEBUG("Parent: $parent"); next PART; } } my $msg = "Child $part not found"; $self->error($msg); ERROR($msg); return; } # parent of root is root if ( @ids == 1 ) { push @ids, $ids[0]; } return @ids; } # TODO: Placed here until I have a use for it #my %IMPORT_FORMATS = ( # 'application/x-vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation', # 'text/tab-separated-values' => 'application/vnd.google-apps.spreadsheet', # 'image/jpeg' => 'application/vnd.google-apps.document', # 'image/bmp' => 'application/vnd.google-apps.document', # 'image/gif' => 'application/vnd.google-apps.document', # 'application/vnd.ms-excel.sheet.macroenabled.12' => 'application/vnd.google-apps.spreadsheet', # 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'application/vnd.google-apps.document', # 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'application/vnd.google-apps.presentation', # 'application/vnd.ms-word.template.macroenabled.12' => 'application/vnd.google-apps.document', # 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'application/vnd.google-apps.document', # 'image/pjpeg' => 'application/vnd.google-apps.document', # 'application/vnd.google-apps.script+text/plain' => 'application/vnd.google-apps.script', # 'application/vnd.ms-excel' => 'application/vnd.google-apps.spreadsheet', # 'application/vnd.sun.xml.writer' => 'application/vnd.google-apps.document', # 'application/vnd.ms-word.document.macroenabled.12' => 'application/vnd.google-apps.document', # 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'application/vnd.google-apps.presentation', # 'text/rtf' => 'application/vnd.google-apps.document', # 'application/vnd.oasis.opendocument.spreadsheet' => 'application/vnd.google-apps.spreadsheet', # 'text/plain' => 'application/vnd.google-apps.document', # 'application/x-vnd.oasis.opendocument.spreadsheet' => 'application/vnd.google-apps.spreadsheet', # 'application/x-vnd.oasis.opendocument.text' => 'application/vnd.google-apps.document', # 'image/png' => 'application/vnd.google-apps.document', # 'application/msword' => 'application/vnd.google-apps.document', # 'application/pdf' => 'application/vnd.google-apps.document', # 'application/x-msmetafile' => 'application/vnd.google-apps.drawing', # 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'application/vnd.google-apps.spreadsheet', # 'application/vnd.ms-powerpoint' => 'application/vnd.google-apps.presentation', # 'application/vnd.ms-excel.template.macroenabled.12' => 'application/vnd.google-apps.spreadsheet', # 'image/x-bmp' => 'application/vnd.google-apps.document', # 'application/rtf' => 'application/vnd.google-apps.document', # 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'application/vnd.google-apps.presentation', # 'image/x-png' => 'application/vnd.google-apps.document', # 'text/html' => 'application/vnd.google-apps.document', # 'application/vnd.oasis.opendocument.text' => 'application/vnd.google-apps.document', # 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'application/vnd.google-apps.presentation', # 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'application/vnd.google-apps.spreadsheet', # 'application/vnd.google-apps.script+json' => 'application/vnd.google-apps.script', # 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'application/vnd.google-apps.presentation', # 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'application/vnd.google-apps.presentation', # 'text/csv' => 'application/vnd.google-apps.spreadsheet', # 'application/vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation', # 'image/jpg' => 'application/vnd.google-apps.document', # 'text/richtext' => 'application/vnd.google-apps.document'] #); 1; __END__ =head1 SYNOPSIS my $gd_v3 = Net::Google::Drive::Simple->new( 'version' => 3 ); # same as: my $gd_v3 = Net::Google::Drive::Simple::V3->new(); $gd->changes(...); $gd->create_comment(...); $gd->delete_file(...); =head1 DESCRIPTION This is a complete implementation of the Google Drive API V3. You can use all the documented methods below. Uploading files over 5 MB require reading the B<UPLOADING> section below. =head1 METHODS =head2 C<new> my $gd = Net::Google::Drive::Simple::V3->new(); Create a new instance. You can also create an instance using L<Net::Google::Drive::Simple>: my $gd = Net::Google::Drive::Simple->new( 'version' => 3 ); =head2 C<about> my $about = $gd->about({%params}); This serves the path to C</about>. It's also referred to as C<about.get>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/about/get>. =head2 C<getStartPageToken> my $data = $gd->getStartPageToken({%params}); Parameters are optional. This serves the path to C</changes/startPageToken>. This is also known as C<changes_getStartPageToken>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/changes/getStartPageToken>. =head2 C<changes> my $changes_list = $gd->changes({%params}); This serves the path to C</changes>. This is also known as C<changes.list>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/changes/list>. =head2 C<watch_changes> my $data = $gd->watch_changes({%params}); Parameters are optional. This serves the path to C</changes/watch>. This is also known as C<changes.watch>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/changes/watch>. =head2 C<stop_channels> $gd->stop_channels({%params}); This serves the path to C</channels/stop>. This is also known as C<channels.stop>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/channels/stop>. =head2 C<create_comment> my $comment = $gd->create_comment( $fileId, {%params} ); This serves the path to C</files/$fileId/comments>. This is also known as C<comments.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/comments/create>. =head2 C<delete_comment( $fileId, $commentId )> $gd->delete_comment( $fileId, $commentId ); This serves the path to C</files/$fileId/comments/$commentId>. This is also known as C<comments.delete>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/comments/delete>. =head2 C<get_comment( $fileId, $commentId, $params )> my $comment = $gd->get_comment( $fileId, $commentId, {%params} ); This serves the path to C</files/$fileId/comments/$commentId>. This is also known as C<comments.get>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/comments/get>. =head2 C<comments> my $comments = $gd->comments( $fileId, {%params} ); This serves the path to C</files/$fileId/comments>. This is also known as C<comments.list>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/comments/list>. =head2 C<update_comment> my $comment = $gd->update_comment( $fileId, $commentId, {%params} ); This serves the path to C</files/$fileId/comments/$commentId>. This is also known as C<comments.update>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/comments/update>. =head2 C<copy_file> my $file = $gd->copy_file( $fileId, {%params} ); This serves the path to C</files/$fileId/copy>. This is also known as C<files.copy>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/copy>. =head2 C<create_folder> my $folder_data = $gd->create_folder( $name, $parent_id ); print $folder_data->{'id'}; This method is just for convenience. It's effectively: $self->create_file({ 'name' => $name, 'mimeType' => "application/vnd.google-apps.folder", 'parents' => [$parent_id], }); =head2 C<create_file> my $file = $gd->create_file({%params}); This serves the path to C</files>. This is also known as C<files.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/create>. This one is B<ONLY for creating metadata for a file>. It will not upload any content. If you want to set content, use the following methods below: C<upload_media_file()>, C<upload_multipart_file>, C<create_resumable_upload()>, C<upload_file_content_single()>, C<upload_file_content_multiple()>, and C<upload_file_content_iterator>. Read more about uploading under B<UPLOADING> below. If you want a simple way to upload files, check C<upload_file()>. =head2 C<upload_file> my $result = $gd->upload_file( $filename, {%params} ); Parameters are optional. This method combines the different mechanisms for uploading a file and makes it easy to upload large files. There are disadvantages, but for most cases, this method should serve you well. Read more about uploading under B<UPLOADING> below. =head2 C<upload_media_file> my $file = $gd->upload_media_file( $filename, {%params} ); Parameters are optional. This serves the path to C</files>. This is also known as C<files.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/create>. This one is for uploading a file B<without metadata>. File size limitation is 5 MB and we read it 4K at a time. Read more about uploading under B<UPLOADING> below. =head2 C<upload_multipart_file> my $file = $gd->upload_multipart_file( $filename, {%params} ); Parameters are optional. This serves the path to C</files>. This is also known as C<files.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/create>. This one is for uploading B<both a file and its metadata>. File size limitation is 5 MB and we read the entire file into memory at once before uploading. Read more about uploading under B<UPLOADING> below. =head2 C<create_resumable_upload_for> my $upload_id = $gd->upload_multimedia_file( $filename, {%params} ); Parameters are optional. This serves the path to C</files>. This is also known as C<files.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/create>. This method starts a resumable upload for a specific file on disk. It provides you with an ID that you can then feed to the following methods: C<upload_file_content_single()>, C<upload_file_content_multiple()>, and C<upload_file_content_iterator()>. File size limitation for any resumable upload is 5120 GB and C<create_resumable_upload_for()> checks for this limit. Read more about uploading under B<UPLOADING> below. =head2 C<create_resumable_upload> my $upload_id = $gd->create_resumable_upload( {%params} ); Parameters are optional. This serves the path to C</files>. This is also known as C<files.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/create>. This method starts a resumable upload. It provides you with an ID that you can then feed to the following methods: C<upload_file_content_single()>, C<upload_file_content_multiple()>, and C<upload_file_content_iterator()>. File size limitation for any resumable upload is 5120 GB and C<create_resumable_upload_for()> checks for this limit. Read more about uploading under B<UPLOADING> below. =head2 C<upload_file_content_single> my $result = $gd->upload_file_content_single( $upload_uri, $filename ); This serves the path to the upload URI you provide it. There is a limitation of 5120 GB for the file. With this method, we feed the file 4K at a time. Read more about uploading under B<UPLOADING>. =head2 C<upload_file_content_multiple> my $result = $gd->upload_file_content_multiple( $upload_uri, $file, $chunk_size ); Chunk size is optional, defaults to 10 MB. This serves the path to the upload URI you provide it. There is a limitation of 5120 GB for the file. With this method, we feed the file in chunks defined by the chunk size you provide or the default 10 MB. Read more about uploading under B<UPLOADING>. The difference between C<upload_file_content_single()> and C<upload_file_content_multiple()> is that this variation will work better for long files, in theory. More importantly, it uses C<upload_file_content_iterator()> which really allows you to control matters. =head2 C<upload_file_content_iterator> use JSON qw< from_json >; my $iter_cb = $gd->upload_file_content_iterator( $filename, $file, $chunk_size ); while ( my $request = $iter->() ) { my $response = handle_http_request($request); # anything else - we're confused, so it's an error no matter what # 200 - Done! if ( $response->code() == 200 ) { return from_json( $response->decoded_content() ); } # 308 - Continue uploading! # anything else - not what we're expecting if ( $response->code() != 308 ) { die "Error: " . $response->code(); } } Chunk size is optional, defaults to 10 MB. This returns a callback that allows you to control how to handle the uploading. It's especially valuable when you want to connect the uploading to an event loop like L<IO::Async>, L<POE>, L<AnyEvent>, or others. The callback returns an object of L<HTTP::Request> which represent the request that needs to be done. Once requested, if you receive a 308, it means you can continue, and if you receive a 200, it means you are done uploading. There is a limitation of 5120 GB for the file. With this method, we feed the file in chunks defined by the chunk size you provide or the default 10 MB. Read more about uploading under B<UPLOADING>. =head2 C<delete_file> $gd->delete_file( $fileId, {%params} ); Parameters are optional. This serves the path to C</files/$fileId>. This is also known as C<files.delete>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/delete>. =head2 C<export_file> my $file_content_in_bytes = $gd->export_file( $fileId, {%params} ); This serves the path to C</files/$fileId/export>. This is also known as C<files.export>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/export>. =head2 C<generateIds> my $data = $gd->generateIds({%params}); Parameters are optional. This serves the path to C</files/generateIds>. This is also known as C<files.generateIds>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/generateIds>. =head2 C<get_file> my $file_metadata = $gd->get_file( $fileId, {%params} ); my $file_content = $gd->get_file( $fileId, { 'alt' => 'media', # get the content 'acknowledgeAbuse' => JSON::true(), # (optional) when there's risk }, ); Parameters are optional. This serves the path to C</files/$fileId>. This is also known as C<files.get>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/get>. =head2 C<files> my $files = $gd->files({%params}); Parameters are optional. This serves the path to C</files>. This is also known as C<files.list>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/list>. =head2 C<update_file> my $file = $gd->update_file( $fileId, {%params} ); This serves the path to C</files/$fileId>. This is also known as C<files.update>. This is for updating a file's metadata and content. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/update>. =head2 C<update_file_metadata> my $file = $gd->update_file_metadata( $fileId, {%params} ); This serves the path to C</files/$fileId>. This is also known as C<files.update>. This one is only for updating a file's metadata, even though it shares a moniker with the C<update_file()> method in the Google Drive API. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/update>. =head2 C<watch_file> my $data = $gd->watch_file( $fileId, {%params} ); This serves the path to C</files/$fileId/watch>. This is also known as C<files.watch>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/watch>. =head2 C<empty_trash> $gd->empty_trash({%params}) Parameters are optional. This serves the path to C</files/trash>. This is also known as C<files.emptyTrash>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/files/emptyTrash>. =head2 C<create_permission> my $permission = $gd-><create_permission( $fileId, {%params} ); This serves the path to C</files/$fileId/permissions>. This is also known as C<permissions.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/permissions/create>. =head2 C<delete_permission> $gd->delete_permission( $fileId, $permissionId, {%params} ); Parameters are optional. This serves the path to C</files/$fileId/permissions/$permissionId>. This is also known as C<permissions.delete>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/permissions/delete>. =head2 C<get_permission> my $permission = $gd->get_permission( $fileId, $permissionId, {%params} ); Parameters are optional. This serves the path to C</files/$fileId/permissions/$permissionId>. This is also known as C<permissions.get>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/permissions/get>. =head2 C<permissions> my $permissions = $gd->permissions( $fileId, {%params} ); Parameters are optional. This serves the path to C</files/$fileId/permissions>. This is also known as C<permissions.list>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/permissions/list>. =head2 C<update_permission> my $permission = $gd->update_permission( $fileId, $permissionId, {%params} ); Parameters are optional. This serves the path to C</files/$fileId/permissions/$permissionId>. This is also known as C<permissions.update>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/permissions/update>. =head2 C<create_reply> my $reply = $gd->create_reply( $fileId, $commentId, {%params} ); This serves the path to C</files/$fileId/comments/$commentId/replies>. This is also known as C<replies.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/replies/create>. =head2 C<delete_reply( $fileId, $commentId, $replyId )> $gd->delete_reply( $fileId, $commentId, $replyId ); This serves the path to C</files/$fileId/comments/$commentId/replies/$replyId>. This is also known as C<replies.delete>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/replies/delete>. =head2 C<get_reply> my $reply = %gd->get_reply( $fileId, $commentId, $replyId, {%params} ); This serves the path to C</files/$fileId/comments/$commentId/replies/$replyId>. This is also known as C<replies.get>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/replies/get>. =head2 C<replies> my $replies = $gd->replies( $fileId, $commentId, {%params} ); This serves the path to C</files/$fileId/comments/$commentId/replies>. This is also known as C<replies.list>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/replies/list>. =head2 C<update_reply> my $reply = $gd->update_reply( $fileId, $commentId, $replyId, {%params} ); This serves the path to C</files/$fileId/comments/$commentId/replies/$replyId>. This is also known as C<replies.update>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/replies/update>. =head2 C<delete_revision> $gd->delete_revision( $fileId, {%params} ); This serves the path to C</files/$fileId/revisions/$revisionId>. This is also known as C<revisions.delete>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/revisions/delete>. =head2 C<get_revision( $fileId, $revisionId, $params )> my $revision = $gd->get_revision( $fileId, $revisionId, {%params} ); Parameters are optional. This serves the path to C</files/$fileId/revisions/$revisionId>. This is also known as C<revisions.get>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/revisions/get>. =head2 C<revisions> my $revisions = $gd->revisions( $fileId, {%params} ); Parameters are optional. This serves the path to C</files/$fileId/revisions>. This is also known as C<revisions.list>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/revisions/list>. =head2 C<update_revision> my $revision = $gd->update_revision( $fileId, $revisionId, {%params} ); This serves the path to C</files/$fileId/revisions/$revisionId>. This is also known as C<revisions.update>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/revisions/update>. =head2 C<create_drive> my $drive = $gd->create_drive({%params}); This serves the path to C</drives>. This is also known as C<drives.create>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/drives/create>. =head2 C<delete_drive> $gd->delete_drive($driveId); This serves the path to C</drives/$driveId>. This is also known as C<drives.delete>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/drives/delete>. =head2 C<get_drive> my $drive = $gd->get_drive( $driveId, {%params} ); Parameters are optional. This serves the path to C</drives/$driveId>. This is also known as C<drives.get>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/drives/get>. =head2 C<hide_drive> my $drive = $gd->hide_drive($driveId); This serves the path to C</drives/$driveId/hide>. This is also known as C<drives.hide>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/drives/hide>. =head2 C<drives> my $drives = $gd->drives({%params}); Parameters are optional. This serves the path to C</drives>. This is also known as C<drives.list>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/drives/list>. =head2 C<unhide_drive> my $drive = $gd->unhide_drive($driveId); This serves the path to C</drives/$driveId/unhide>. This is also known as C<drives.unhide>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/drives/unhide>. =head2 C<update_drive> my $drive = $gd->update_drive( $driveId, {%params} ); This serves the path to C</drives/$driveId>. This is also known as C<drives.update>. You can read about the parameters on the Google Drive L<API page|https://developers.google.com/drive/api/v3/reference/drives/update>. =head2 C<children> my $children = $gd->children( '/path/to', {%params}, {%extra_params} )> The parameters and extra parameters are both optional. Return the entries under a given path on the Google Drive as a reference to an array. Each entry is an object composed of the JSON data returned by the Google Drive API. Each object offers methods named like the fields in the JSON data, e.g. C<originalFilename()>, C<downloadUrl>, etc. Will return all entries found unless C<pageSize> is set: my $children = $gd->children( "/path/to", { pageSize => 3 } ) Due to the somewhat capricious ways Google Drive handles its directory structures, the method needs to traverse the path component by component and determine the ID of each directory to get to the next level. To speed up subsequent lookups, it also returns the ID of the last component to the caller: my( $children, $parent ) = $gd->children( "/path/to" ); If the caller now wants to e.g. insert a file into the directory, its ID is available in $parent. Each child comes back as a files#resource type and gets mapped into an object that offers access to the various fields via methods: for my $child ( @$children ) { print $child->kind(), " ", $child->name(), "\n"; } Please refer to https://developers.google.com/drive/v3/reference/files#resource for details on which fields are available. =head2 C<children_by_folder_id> my $children = $gd->children_by_folder_id($folderId); # Search with a particular query and stop at the first page my $children = $gd->children_by_folder_id( $folderId, { 'q' => q{name contains 'hello'} }, { 'auto_paging' => 0 }, ); Similar to C<children()> but uses a folder ID. The second parameter is the parameters the C<files()> method receives. The third parameter is for additional options on how to conduct this search. Only one option is supported: C<auto_paging>. When C<auto_paging> is turned on (which is the default), the search will be done on every page of the results instead of stopping at the first page. =head1 UPLOADING Uploading of a 5 MB file or lower is simple, but uploading a larger file is more difficult. This module supports every possible option, including connecting uploads to a different systems. =head2 SIMPLE AND EASY UPLOADING If you are not interested in all the details and finer controls of uploading files, you can just use C<upload_file>. my $data = $gd->upload_file( $filename, {%params} ); The parameters are optional and you can read more about them in the appropriate L<API page|https://developers.google.com/drive/api/v3/reference/files/create>. The disadvantages are that you cannot control how much is uploaded at a time, it's not resumable, nor coudl you connect it with an event loop. However, this has no size limitations other than 5120 GB. =head2 UPLOADING FILES 5 MB OR SMALLER When uploading 5 MB and under, you can either use C<create_file()> for metadata or C<upload_media_file()> for both metadata and content. Despite the name, you may upload any form of file, not just media files. (This is the name Google provides this form of upload.) # Create only the metadata my $data = $gd->create_file({ 'name' => 'foo.txt', 'mediaType' => 'text/plain', }) Once you call C<create_file()>, you can use the ID of the response in subsequent calls to C<upload_media_file()> or C<upload_multipart_file()>. (This might work for resumable uploads too, but it's not tested.) # Upload just the file $data = $gd->upload_media_file('foo.txt'); $data = $gd->upload_media_file( 'foo.txt', { 'name' => 'bar.txt' } ); The only supported parameters are the query parameters. # Upload the file and all the metadata $data = $gd->upload_multipart_file( 'foo.txt', {...} ); The difference between C<upload_media_file()> and C<upload_multipart_file()> is that the former method allows the query parameters, but the latter method allows all options. You can read more about the available options for either of these file uploads L<here|https://developers.google.com/drive/api/v3/reference/files/create>. =head2 UPLOADING FILES ABOVE 5 MB When uploading files above 5 MB, you must first create a URI for the file upload and then upload to that URI with another method. There are two ways to create it, depending on whether you have a file available on disk or not. # Creating the URI using a file my $upload_uri = $gd->create_resumable_upload_for( $file, {...} ); # Creating the URI without a file my $upload_uri = $gd->create_resumable_upload({ 'name' => 'foo.txt', 'mimeType' => 'text/plain', }); The benefit of using C<create_resumable_upload_for()> is that it allows you to use an existing file's mime type and filename. Otherwise, if you use the low-level C<create_resumable_upload()>, you will need to provide the C<name> and C<mediaType> parameters yourself. There are three different methods for uploading - choose the one that suits you best. # Just upload the entire file my $data = upload_file_content_single( $upload_uri, $file ); This upload will still try to chunk it by 4K so it doesn't load the entire file into memory. The biggest downside of this method is that if the file fails, it fully fails and you have to start from scratch. # Upload the file in resumable chunks my $data = upload_file_content_multiple( $upload_uri, $file, $optional_chunk_size, ); This method will attempt to upload the file in chunks of 10 MB (or a whatever chunk size you ask for - in bytes) until it successfully finishes. If it fails, you might be able to resume. However, resuming is not yet supported within the API, sorry. =head2 UPLOADING FILES WITH EVENT LOOPS If you want to connect file uploading to an event loop, you can use C<upload_file_content_iterator()> to receive an iterator which will generate a proper L<HTTP::Request> object you can then use in the event loop request. my $iterator = upload_file_content_iterator( $upload_uir, $file, $optional_chunk_size, ); while ( my $request = $iter->() ) { my $response = do_something_with_request($request); # $response->code() == 200 - Done # $response->code() == 308 - Keep going # anything else - Probably some form of error } There is currently no sample code for any particular event loop. =head1 LEGALESE Copyright 2012-2019 by Mike Schilli, all rights reserved. This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Sawyer X <xsawyerx@cpan.org>
| ver. 1.4 |
Github
|
.
| PHP 8.1.34 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings