n8n/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts
Iván Ovejero 176e475c8e
:xap: Open tooltip links on new tab (#2106)
*  Create mixin

*  Implement mixin

* 🔥 Remove hardcoded attribute

* 🔨 Refactor mixin into helper

* 🔨 Refactor string replacement

* ✏️ Unrelated description fixes

*  Simplify helper import
2021-08-26 19:42:38 +02:00

2465 lines
58 KiB
TypeScript

import {
BINARY_ENCODING,
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
INodeExecutionData,
INodeType,
INodeTypeDescription,
NodeOperationError,
} from 'n8n-workflow';
import {
googleApiRequest,
googleApiRequestAllItems,
} from './GenericFunctions';
import { v4 as uuid } from 'uuid';
export class GoogleDrive implements INodeType {
description: INodeTypeDescription = {
displayName: 'Google Drive',
name: 'googleDrive',
icon: 'file:googleDrive.svg',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Access data on Google Drive',
defaults: {
name: 'Google Drive',
color: '#4285F4',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'googleApi',
required: true,
displayOptions: {
show: {
authentication: [
'serviceAccount',
],
},
},
},
{
name: 'googleDriveOAuth2Api',
required: true,
displayOptions: {
show: {
authentication: [
'oAuth2',
],
},
},
},
],
properties: [
{
displayName: 'Authentication',
name: 'authentication',
type: 'options',
options: [
{
name: 'Service Account',
value: 'serviceAccount',
},
{
name: 'OAuth2',
value: 'oAuth2',
},
],
default: 'serviceAccount',
},
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Drive',
value: 'drive',
},
{
name: 'File',
value: 'file',
},
{
name: 'Folder',
value: 'folder',
},
],
default: 'file',
description: 'The resource to operate on.',
},
// ----------------------------------
// operations
// ----------------------------------
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'file',
],
},
},
options: [
{
name: 'Copy',
value: 'copy',
description: 'Copy a file',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a file',
},
{
name: 'Download',
value: 'download',
description: 'Download a file',
},
{
name: 'List',
value: 'list',
description: 'List files and folders',
},
{
name: 'Share',
value: 'share',
description: 'Share a file',
},
{
name: 'Update',
value: 'update',
description: 'Update a file',
},
{
name: 'Upload',
value: 'upload',
description: 'Upload a file',
},
],
default: 'upload',
description: 'The operation to perform.',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'folder',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a folder',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a folder',
},
{
name: 'Share',
value: 'share',
description: 'Share a folder',
},
],
default: 'create',
description: 'The operation to perform.',
},
// ----------------------------------
// file
// ----------------------------------
// ----------------------------------
// file:copy
// ----------------------------------
{
displayName: 'ID',
name: 'fileId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'copy',
],
resource: [
'file',
],
},
},
description: 'The ID of the file to copy.',
},
// ----------------------------------
// file/folder:delete
// ----------------------------------
{
displayName: 'ID',
name: 'fileId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'delete',
],
resource: [
'file',
'folder',
],
},
},
description: 'The ID of the file/folder to delete.',
},
// ----------------------------------
// file:download
// ----------------------------------
{
displayName: 'File Id',
name: 'fileId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'download',
],
resource: [
'file',
],
},
},
description: 'The ID of the file to download.',
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
required: true,
default: 'data',
displayOptions: {
show: {
operation: [
'download',
],
resource: [
'file',
],
},
},
description: 'Name of the binary property to which to<br />write the data of the read file.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'download',
],
resource: [
'file',
],
},
},
options: [
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
default: '',
description: 'File name. Ex: data.pdf',
},
],
},
// ----------------------------------
// file:list
// ----------------------------------
{
displayName: 'Use Query String',
name: 'useQueryString',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: [
'list',
],
resource: [
'file',
],
},
},
description: 'If a query string should be used to filter results.',
},
{
displayName: 'Query String',
name: 'queryString',
type: 'string',
default: '',
displayOptions: {
show: {
operation: [
'list',
],
useQueryString: [
true,
],
resource: [
'file',
],
},
},
placeholder: 'name contains \'invoice\'',
description: 'Query to use to return only specific files.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'list',
],
resource: [
'file',
],
},
},
typeOptions: {
minValue: 1,
maxValue: 1000,
},
default: 100,
description: 'How many files to return.',
},
{
displayName: 'Filters',
name: 'queryFilters',
placeholder: 'Add Filter',
description: 'Filters to use to return only specific files.',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
displayOptions: {
show: {
operation: [
'list',
],
useQueryString: [
false,
],
resource: [
'file',
],
},
},
options: [
{
name: 'name',
displayName: 'Name',
values: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: 'Contains',
value: 'contains',
},
{
name: 'Is',
value: 'is',
},
{
name: 'Is Not',
value: 'isNot',
},
],
default: 'contains',
description: 'Operation to perform.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value for operation.',
},
],
},
{
name: 'mimeType',
displayName: 'Mime Type',
values: [
{
displayName: 'Mime Type',
name: 'mimeType',
type: 'options',
options: [
{
name: 'Custom Mime Type',
value: 'custom',
},
{
name: ' 3rd party shortcut',
value: 'application/vnd.google-apps.drive-sdk',
},
{
name: 'Audio',
value: 'application/vnd.google-apps.audio',
},
{
name: 'Google Apps Scripts',
value: 'application/vnd.google-apps.script',
},
{
name: 'Google Docs',
value: 'application/vnd.google-apps.document',
},
{
name: 'Google Drawing',
value: 'application/vnd.google-apps.drawing',
},
{
name: 'Google Drive file',
value: 'application/vnd.google-apps.file',
},
{
name: 'Google Drive folder',
value: 'application/vnd.google-apps.folder',
},
{
name: 'Google Forms',
value: 'application/vnd.google-apps.form',
},
{
name: 'Google Fusion Tables',
value: 'application/vnd.google-apps.fusiontable',
},
{
name: 'Google My Maps',
value: 'application/vnd.google-apps.map',
},
{
name: 'Google Sheets',
value: 'application/vnd.google-apps.spreadsheet',
},
{
name: 'Google Sites',
value: 'application/vnd.google-apps.site',
},
{
name: 'Google Slides',
value: 'application/vnd.google-apps.presentation',
},
{
name: 'Photo',
value: 'application/vnd.google-apps.photo',
},
{
name: 'Unknown',
value: 'application/vnd.google-apps.unknown',
},
{
name: 'Video',
value: 'application/vnd.google-apps.video',
},
],
default: 'application/vnd.google-apps.file',
description: 'The Mime-Type of the files to return.',
},
{
displayName: 'Custom Mime Type',
name: 'customMimeType',
type: 'string',
default: '',
displayOptions: {
show: {
mimeType: [
'custom',
],
},
},
description: 'Custom Mime Type',
},
],
},
],
},
// ----------------------------------
// file:share
// ----------------------------------
{
displayName: 'File ID',
name: 'fileId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'share',
],
resource: [
'file',
'folder',
],
},
},
description: 'The ID of the file or shared drive.',
},
{
displayName: 'Permissions',
name: 'permissionsUi',
placeholder: 'Add Permission',
type: 'fixedCollection',
default: {},
typeOptions: {
multipleValues: false,
},
displayOptions: {
show: {
resource: [
'file',
'folder',
],
operation: [
'share',
],
},
},
options: [
{
displayName: 'Permission',
name: 'permissionsValues',
values: [
{
displayName: 'Role',
name: 'role',
type: 'options',
options: [
{
name: 'Owner',
value: 'owner',
},
{
name: 'Organizer',
value: 'organizer',
},
{
name: 'File Organizer',
value: 'fileOrganizer',
},
{
name: 'Writer',
value: 'writer',
},
{
name: 'Commenter',
value: 'commenter',
},
{
name: 'Reader',
value: 'reader',
},
],
default: '',
},
{
displayName: 'Type',
name: 'type',
type: 'options',
options: [
{
name: 'User',
value: 'user',
},
{
name: 'Group',
value: 'group',
},
{
name: 'Domain',
value: 'domain',
},
{
name: 'Anyone',
value: 'anyone',
},
],
default: '',
description: 'Information about the different types can be found <a href="https://developers.google.com/drive/api/v3/ref-roles">here</a>.',
},
{
displayName: 'Email Address',
name: 'emailAddress',
type: 'string',
displayOptions: {
show: {
type: [
'user',
'group',
],
},
},
default: '',
description: 'The email address of the user or group to which this permission refers',
},
{
displayName: 'Domain',
name: 'domain',
type: 'string',
displayOptions: {
show: {
type: [
'domain',
],
},
},
default: '',
description: 'The domain to which this permission refers',
},
{
displayName: 'Allow File Discovery',
name: 'allowFileDiscovery',
type: 'boolean',
displayOptions: {
show: {
type: [
'domain',
'anyone',
],
},
},
default: false,
description: 'Whether the permission allows the file to be discovered through search',
},
],
},
],
},
{
displayName: 'Binary Data',
name: 'binaryData',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: [
'upload',
],
resource: [
'file',
],
},
},
description: 'If the data to upload should be taken from binary field.',
},
{
displayName: 'File Content',
name: 'fileContent',
type: 'string',
default: '',
displayOptions: {
show: {
operation: [
'upload',
],
resource: [
'file',
],
binaryData: [
false,
],
},
},
placeholder: '',
description: 'The text content of the file to upload.',
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
operation: [
'upload',
],
resource: [
'file',
],
binaryData: [
true,
],
},
},
placeholder: '',
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
},
// ----------------------------------
// file:update
// ----------------------------------
{
displayName: 'ID',
name: 'fileId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'file',
],
},
},
description: 'The ID of the file to update.',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'file',
],
},
},
options: [
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
default: '',
description: `The name of the file`,
},
{
displayName: 'Keep Revision Forever',
name: 'keepRevisionForever',
type: 'boolean',
default: false,
description: `Whether to set the 'keepForever' field in the new head revision.</br>
his is only applicable to files with binary content in Google Drive.</br>
Only 200 revisions for the file can be kept forever. If the limit is reached, try deleting pinned revisions.`,
},
{
displayName: 'OCR Language',
name: 'ocrLanguage',
type: 'string',
default: '',
description: `A language hint for OCR processing during image import (ISO 639-1 code).`,
},
{
displayName: 'Parent ID',
name: 'parentId',
type: 'string',
default: '',
description: `The ID of the parent to set.`,
},
{
displayName: 'Use Content As Indexable Text',
name: 'useContentAsIndexableText',
type: 'boolean',
default: false,
description: `Whether to use the uploaded content as indexable text.`,
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'file',
],
},
},
options: [
{
displayName: 'Fields',
name: 'fields',
type: 'multiOptions',
options: [
{
name: '*',
value: '*',
description: 'All fields.',
},
{
name: 'explicitlyTrashed',
value: 'explicitlyTrashed',
},
{
name: 'exportLinks',
value: 'exportLinks',
},
{
name: 'iconLink',
value: 'iconLink',
},
{
name: 'hasThumbnail',
value: 'hasThumbnail',
},
{
name: 'id',
value: 'id',
},
{
name: 'kind',
value: 'kind',
},
{
name: 'name',
value: 'name',
},
{
name: 'mimeType',
value: 'mimeType',
},
{
name: 'permissions',
value: 'permissions',
},
{
name: 'shared',
value: 'shared',
},
{
name: 'spaces',
value: 'spaces',
},
{
name: 'starred',
value: 'starred',
},
{
name: 'thumbnailLink',
value: 'thumbnailLink',
},
{
name: 'trashed',
value: 'trashed',
},
{
name: 'version',
value: 'version',
},
{
name: 'webViewLink',
value: 'webViewLink',
},
],
required: true,
default: [],
description: 'The fields to return.',
},
],
},
// ----------------------------------
// file:upload
// ----------------------------------
{
displayName: 'File Name',
name: 'name',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'upload',
],
resource: [
'file',
],
},
},
placeholder: 'invoice_1.pdf',
description: 'The name the file should be saved as.',
},
// ----------------------------------
{
displayName: 'Resolve Data',
name: 'resolveData',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: [
'upload',
],
resource: [
'file',
],
},
},
description: 'By default the response only contain the ID of the file.<br />If this option gets activated it will resolve the data automatically.',
},
{
displayName: 'Parents',
name: 'parents',
type: 'string',
typeOptions: {
multipleValues: true,
},
default: [],
displayOptions: {
show: {
operation: [
'upload',
],
resource: [
'file',
],
},
},
description: 'The IDs of the parent folders which contain the file.',
},
// ----------------------------------
// folder
// ----------------------------------
// ----------------------------------
// folder:create
// ----------------------------------
{
displayName: 'Folder',
name: 'name',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'folder',
],
},
},
placeholder: 'invoices',
description: 'The name of folder to create.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
'/operation': [
'copy',
'list',
'share',
'create',
],
'/resource': [
'file',
'folder',
],
},
},
options: [
{
displayName: 'Email Message',
name: 'emailMessage',
type: 'string',
displayOptions: {
show: {
'/operation': [
'share',
],
'/resource': [
'file',
'folder',
],
},
},
default: '',
description: 'A plain text custom message to include in the notification email.',
},
{
displayName: 'Enforce Single Parent',
name: 'enforceSingleParent',
type: 'boolean',
displayOptions: {
show: {
'/operation': [
'share',
],
'/resource': [
'file',
'folder',
],
},
},
default: false,
description: `Set to true to opt in to API behavior that aims for all items to have exactly one parent<br>
This parameter only takes effect if the item is not in a shared drive`,
},
{
displayName: 'Fields',
name: 'fields',
type: 'multiOptions',
displayOptions: {
show: {
'/operation': [
'list',
'copy',
],
},
},
options: [
{
name: '*',
value: '*',
description: 'All fields.',
},
{
name: 'explicitlyTrashed',
value: 'explicitlyTrashed',
},
{
name: 'exportLinks',
value: 'exportLinks',
},
{
name: 'iconLink',
value: 'iconLink',
},
{
name: 'hasThumbnail',
value: 'hasThumbnail',
},
{
name: 'id',
value: 'id',
},
{
name: 'kind',
value: 'kind',
},
{
name: 'name',
value: 'name',
},
{
name: 'mimeType',
value: 'mimeType',
},
{
name: 'permissions',
value: 'permissions',
},
{
name: 'shared',
value: 'shared',
},
{
name: 'spaces',
value: 'spaces',
},
{
name: 'starred',
value: 'starred',
},
{
name: 'thumbnailLink',
value: 'thumbnailLink',
},
{
name: 'trashed',
value: 'trashed',
},
{
name: 'version',
value: 'version',
},
{
name: 'webViewLink',
value: 'webViewLink',
},
],
required: true,
default: [],
description: 'The fields to return.',
},
{
displayName: 'Move To New Owners Root',
name: 'moveToNewOwnersRoot',
type: 'boolean',
displayOptions: {
show: {
'/operation': [
'share',
],
'/resource': [
'file',
'folder',
],
},
},
default: '',
description: `This parameter only takes effect if the item is not in a shared drive and the request is attempting to transfer the ownership of the item.<br>
When set to true, the item is moved to the new owner's My Drive root folder and all prior parents removed`,
},
{
displayName: 'Send Notification Email',
name: 'sendNotificationEmail',
type: 'boolean',
displayOptions: {
show: {
'/operation': [
'share',
],
'/resource': [
'file',
'folder',
],
},
},
default: false,
description: 'Whether to send a notification email when sharing to users or groups',
},
{
displayName: 'Supports All Drives',
name: 'supportsAllDrives',
type: 'boolean',
displayOptions: {
show: {
'/operation': [
'share',
],
'/resource': [
'file',
'folder',
],
},
},
default: false,
description: 'Whether the requesting application supports both My Drives and shared drives',
},
{
displayName: 'Transfer Ownership',
name: 'transferOwnership',
type: 'boolean',
displayOptions: {
show: {
'/operation': [
'share',
],
'/resource': [
'file',
'folder',
],
},
},
default: false,
description: 'Whether to transfer ownership to the specified user and downgrade the current owner to a writer.',
},
{
displayName: 'Use Domain Admin Access',
name: 'useDomainAdminAccess',
type: 'boolean',
displayOptions: {
show: {
'/operation': [
'share',
],
'/resource': [
'file',
'folder',
],
},
},
default: false,
description: `Perform the operation as domain administrator, i.e. if you are an administrator of the domain to which the shared drive belongs, you will be granted access automatically`,
},
{
displayName: 'File Name',
name: 'name',
type: 'string',
displayOptions: {
show: {
'/operation': [
'copy',
],
'/resource': [
'file',
],
},
},
default: '',
placeholder: 'invoice_1.pdf',
description: 'The name the file should be saved as.',
},
{
displayName: 'Parents',
name: 'parents',
type: 'string',
displayOptions: {
show: {
'/operation': [
'copy',
'create',
],
'/resource': [
'file',
'folder',
],
},
},
typeOptions: {
multipleValues: true,
},
default: [],
description: 'The IDs of the parent folders the file/folder should be saved in.',
},
{
displayName: 'Spaces',
name: 'spaces',
type: 'multiOptions',
displayOptions: {
show: {
'/operation': [
'list',
],
'/resource': [
'file',
],
},
},
options: [
{
name: '*',
value: '*',
description: 'All spaces.',
},
{
name: 'appDataFolder',
value: 'appDataFolder',
},
{
name: 'drive',
value: 'drive',
},
{
name: 'photos',
value: 'photos',
},
],
required: true,
default: [],
description: 'The spaces to operate on.',
},
{
displayName: 'Corpora',
name: 'corpora',
type: 'options',
displayOptions: {
show: {
'/operation': [
'list',
],
'/resource': [
'file',
],
},
},
options: [
{
name: 'user',
value: 'user',
description: 'All files in "My Drive" and "Shared with me"',
},
{
name: 'domain',
value: 'domain',
description: 'All files shared to the user\'s domain that are searchable',
},
{
name: 'drive',
value: 'drive',
description: 'All files contained in a single shared drive',
},
{
name: 'allDrives',
value: 'allDrives',
description: 'All drives',
},
],
required: true,
default: '',
description: 'The corpora to operate on.',
},
{
displayName: 'Drive ID',
name: 'driveId',
type: 'string',
default: '',
required: false,
displayOptions: {
show: {
'/operation': [
'list',
],
'/resource': [
'file',
],
corpora: [
'drive',
],
},
},
description: 'ID of the shared drive to search. The driveId parameter must be specified if and only if corpora is set to drive.',
},
],
},
// ----------------------------------
// drive
// ----------------------------------
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'drive',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a drive',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a drive',
},
{
name: 'Get',
value: 'get',
description: 'Get a drive',
},
{
name: 'List',
value: 'list',
description: 'List all drives',
},
{
name: 'Update',
value: 'update',
description: 'Update a drive',
},
],
default: 'create',
description: 'The operation to perform.',
},
// ----------------------------------
// drive:create
// ----------------------------------
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
required: false,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'drive',
],
},
},
description: 'The name of this shared drive.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'drive',
],
},
},
options: [
{
displayName: 'Capabilities',
name: 'capabilities',
type: 'collection',
placeholder: 'Add Field',
default: {},
options: [
{
displayName: 'Can Add Children',
name: 'canAddChildren',
type: 'boolean',
default: false,
description: `Whether the current user can add children to folders in this shared drive.`,
},
{
displayName: 'Can Change Copy Requires Writer Permission Restriction',
name: 'canChangeCopyRequiresWriterPermissionRestriction',
type: 'boolean',
default: false,
description: `Whether the current user can change the copyRequiresWriterPermission restriction of this shared drive.`,
},
{
displayName: 'Can Change Domain Users Only Restriction',
name: 'canChangeDomainUsersOnlyRestriction',
type: 'boolean',
default: false,
description: `Whether the current user can change the domainUsersOnly restriction of this shared drive.`,
},
{
displayName: 'Can Change Drive Background',
name: 'canChangeDriveBackground',
type: 'boolean',
default: false,
description: `Whether the current user can change the background of this shared drive.`,
},
{
displayName: 'Can Change Drive Members Only Restriction',
name: 'canChangeDriveMembersOnlyRestriction',
type: 'boolean',
default: false,
description: `Whether the current user can change the driveMembersOnly restriction of this shared drive.`,
},
{
displayName: 'Can Comment',
name: 'canComment',
type: 'boolean',
default: false,
description: `Whether the current user can comment on files in this shared drive.`,
},
{
displayName: 'Can Copy',
name: 'canCopy',
type: 'boolean',
default: false,
description: `Whether the current user can copy files in this shared drive.`,
},
{
displayName: 'Can Delete Children',
name: 'canDeleteChildren',
type: 'boolean',
default: false,
description: `Whether the current user can delete children from folders in this shared drive.`,
},
{
displayName: 'Can Delete Drive',
name: 'canDeleteDrive',
type: 'boolean',
default: false,
description: `Whether the current user can delete this shared drive. Attempting to delete the shared drive may still fail if there are untrashed items inside the shared drive.`,
},
{
displayName: 'Can Download',
name: 'canDownload',
type: 'boolean',
default: false,
description: `Whether the current user can download files in this shared drive.`,
},
{
displayName: 'Can Edit',
name: 'canEdit',
type: 'boolean',
default: false,
description: `Whether the current user can edit files in this shared drive`,
},
{
displayName: 'Can List Children',
name: 'canListChildren',
type: 'boolean',
default: false,
description: `Whether the current user can list the children of folders in this shared drive.`,
},
{
displayName: 'Can Manage Members',
name: 'canManageMembers',
type: 'boolean',
default: false,
description: `Whether the current user can add members to this shared drive or remove them or change their role.`,
},
{
displayName: 'Can Read Revisions',
name: 'canReadRevisions',
type: 'boolean',
default: false,
description: `Whether the current user can read the revisions resource of files in this shared drive.`,
},
{
displayName: 'Can Rename',
name: 'canRename',
type: 'boolean',
default: false,
description: `Whether the current user can rename files or folders in this shared drive.`,
},
{
displayName: 'Can Rename Drive',
name: 'canRenameDrive',
type: 'boolean',
default: false,
description: `Whether the current user can rename this shared drive.`,
},
{
displayName: 'Can Share',
name: 'canShare',
type: 'boolean',
default: false,
description: `Whether the current user can rename this shared drive.`,
},
{
displayName: 'Can Trash Children',
name: 'canTrashChildren',
type: 'boolean',
default: false,
description: `Whether the current user can trash children from folders in this shared drive.`,
},
],
},
{
displayName: 'Color RGB',
name: 'colorRgb',
type: 'color',
default: '',
description: 'The color of this shared drive as an RGB hex string',
},
{
displayName: 'Created Time',
name: 'createdTime',
type: 'dateTime',
default: '',
description: 'The time at which the shared drive was created (RFC 3339 date-time).',
},
{
displayName: 'Hidden',
name: 'hidden',
type: 'boolean',
default: false,
description: 'Whether the shared drive is hidden from default view.',
},
{
displayName: 'Restrictions',
name: 'restrictions',
type: 'collection',
placeholder: 'Add Field',
default: {},
options: [
{
displayName: 'Admin Managed Restrictions',
name: 'adminManagedRestrictions',
type: 'boolean',
default: false,
description: `Whether the options to copy, print, or download files inside this shared drive,<br/>
should be disabled for readers and commenters. When this restriction is set to true, it will<br/>
override the similarly named field to true for any file inside this shared drive.`,
},
{
displayName: 'Copy Requires Writer Permission',
name: 'copyRequiresWriterPermission',
type: 'boolean',
default: false,
description: `Whether the options to copy, print, or download files inside this shared drive,<br/>
should be disabled for readers and commenters. When this restriction is set to true, it will<br/>
override the similarly named field to true for any file inside this shared drive.`,
},
{
displayName: 'Domain Users Only',
name: 'domainUsersOnly',
type: 'boolean',
default: false,
description: `Whether access to this shared drive and items inside this shared drive<br/>
is restricted to users of the domain to which this shared drive belongs. This restriction<br/>
may be overridden by other sharing policies controlled outside of this shared drive.`,
},
{
displayName: 'Drive Members Only',
name: 'driveMembersOnly',
type: 'boolean',
default: false,
description: `Whether access to items inside this shared drive is restricted to its members.`,
},
],
},
],
},
// ----------------------------------
// drive:delete
// ----------------------------------
{
displayName: 'Drive ID',
name: 'driveId',
type: 'string',
default: '',
required: false,
displayOptions: {
show: {
operation: [
'delete',
],
resource: [
'drive',
],
},
},
description: 'The ID of the shared drive.',
},
// ----------------------------------
// drive:get
// ----------------------------------
{
displayName: 'Drive ID',
name: 'driveId',
type: 'string',
default: '',
required: false,
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'drive',
],
},
},
description: 'The ID of the shared drive.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'drive',
],
},
},
options: [
{
displayName: 'Use Domain Admin Access',
name: 'useDomainAdminAccess',
type: 'boolean',
default: false,
description: 'Issue the request as a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the shared drive belongs. (Default: false)',
},
],
},
// ----------------------------------
// drive:list
// ----------------------------------
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'list',
],
resource: [
'drive',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'list',
],
resource: [
'drive',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 200,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'list',
],
resource: [
'drive',
],
},
},
options: [
{
displayName: 'Query',
name: 'q',
type: 'string',
default: '',
description: 'Query string for searching shared drives. See the <a href="https://developers.google.com/drive/api/v3/search-shareddrives">"Search for shared drives"</a> guide for supported syntax.',
},
{
displayName: 'Use Domain Admin Access',
name: 'useDomainAdminAccess',
type: 'boolean',
default: false,
description: 'Issue the request as a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the shared drive belongs. (Default: false)',
},
],
},
// ----------------------------------
// drive:update
// ----------------------------------
{
displayName: 'Drive ID',
name: 'driveId',
type: 'string',
default: '',
required: false,
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'drive',
],
},
},
description: 'The ID of the shared drive.',
},
{
displayName: 'Update Fields',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'drive',
],
},
},
options: [
{
displayName: 'Color RGB',
name: 'colorRgb',
type: 'color',
default: '',
description: 'The color of this shared drive as an RGB hex string',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'The name of this shared drive.',
},
{
displayName: 'Restrictions',
name: 'restrictions',
type: 'collection',
placeholder: 'Add Field',
default: {},
options: [
{
displayName: 'Admin Managed Restrictions',
name: 'adminManagedRestrictions',
type: 'boolean',
default: false,
description: `Whether the options to copy, print, or download files inside this shared drive,<br/>
should be disabled for readers and commenters. When this restriction is set to true, it will<br/>
override the similarly named field to true for any file inside this shared drive.`,
},
{
displayName: 'Copy Requires Writer Permission',
name: 'copyRequiresWriterPermission',
type: 'boolean',
default: false,
description: `Whether the options to copy, print, or download files inside this shared drive,<br/>
should be disabled for readers and commenters. When this restriction is set to true, it will<br/>
override the similarly named field to true for any file inside this shared drive.`,
},
{
displayName: 'Domain Users Only',
name: 'domainUsersOnly',
type: 'boolean',
default: false,
description: `Whether access to this shared drive and items inside this shared drive<br/>
is restricted to users of the domain to which this shared drive belongs. This restriction<br/>
may be overridden by other sharing policies controlled outside of this shared drive.`,
},
{
displayName: 'Drive Members Only',
name: 'driveMembersOnly',
type: 'boolean',
default: false,
description: `Whether access to items inside this shared drive is restricted to its members.`,
},
],
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'upload',
],
resource: [
'file',
],
},
},
options: [
{
displayName: 'APP Properties',
name: 'appPropertiesUi',
placeholder: 'Add Property',
type: 'fixedCollection',
default: '',
typeOptions: {
multipleValues: true,
},
description: 'A collection of arbitrary key-value pairs which are private to the requesting app',
options: [
{
name: 'appPropertyValues',
displayName: 'APP Property',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'Name of the key to add.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value to set for the key.',
},
],
},
],
},
{
displayName: 'Properties',
name: 'propertiesUi',
placeholder: 'Add Property',
type: 'fixedCollection',
default: '',
typeOptions: {
multipleValues: true,
},
description: 'A collection of arbitrary key-value pairs which are visible to all apps.',
options: [
{
name: 'propertyValues',
displayName: 'Property',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'Name of the key to add.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value to set for the key.',
},
],
},
],
},
],
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
for (let i = 0; i < items.length; i++) {
try {
const options = this.getNodeParameter('options', i, {}) as IDataObject;
let queryFields = 'id, name';
if (options && options.fields) {
const fields = options.fields as string[];
if (fields.includes('*')) {
queryFields = '*';
} else {
queryFields = fields.join(', ');
}
}
if (resource === 'drive') {
if (operation === 'create') {
// ----------------------------------
// create
// ----------------------------------
const name = this.getNodeParameter('name', i) as string;
const body: IDataObject = {
name,
};
Object.assign(body, options);
const response = await googleApiRequest.call(this, 'POST', `/drive/v3/drives`, body, { requestId: uuid() });
returnData.push(response as IDataObject);
}
if (operation === 'delete') {
// ----------------------------------
// delete
// ----------------------------------
const driveId = this.getNodeParameter('driveId', i) as string;
await googleApiRequest.call(this, 'DELETE', `/drive/v3/drives/${driveId}`);
returnData.push({ success: true });
}
if (operation === 'get') {
// ----------------------------------
// get
// ----------------------------------
const driveId = this.getNodeParameter('driveId', i) as string;
const qs: IDataObject = {};
Object.assign(qs, options);
const response = await googleApiRequest.call(this, 'GET', `/drive/v3/drives/${driveId}`, {}, qs);
returnData.push(response as IDataObject);
}
if (operation === 'list') {
// ----------------------------------
// list
// ----------------------------------
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
const qs: IDataObject = {};
let response: IDataObject[] = [];
Object.assign(qs, options);
if (returnAll === true) {
response = await googleApiRequestAllItems.call(this, 'drives', 'GET', `/drive/v3/drives`, {}, qs);
} else {
qs.pageSize = this.getNodeParameter('limit', i) as number;
const data = await googleApiRequest.call(this, 'GET', `/drive/v3/drives`, {}, qs);
response = data.drives as IDataObject[];
}
returnData.push.apply(returnData, response);
}
if (operation === 'update') {
// ----------------------------------
// update
// ----------------------------------
const driveId = this.getNodeParameter('driveId', i) as string;
const body: IDataObject = {};
Object.assign(body, options);
const response = await googleApiRequest.call(this, 'PATCH', `/drive/v3/drives/${driveId}`, body);
returnData.push(response as IDataObject);
}
}
if (resource === 'file') {
if (operation === 'copy') {
// ----------------------------------
// copy
// ----------------------------------
const fileId = this.getNodeParameter('fileId', i) as string;
const body: IDataObject = {
fields: queryFields,
};
const optionProperties = ['name', 'parents'];
for (const propertyName of optionProperties) {
if (options[propertyName] !== undefined) {
body[propertyName] = options[propertyName];
}
}
const qs = {
supportsAllDrives: true,
};
const response = await googleApiRequest.call(this, 'POST', `/drive/v3/files/${fileId}/copy`, body, qs);
returnData.push(response as IDataObject);
} else if (operation === 'download') {
// ----------------------------------
// download
// ----------------------------------
const fileId = this.getNodeParameter('fileId', i) as string;
const options = this.getNodeParameter('options', i) as IDataObject;
const requestOptions = {
resolveWithFullResponse: true,
encoding: null,
json: false,
};
const response = await googleApiRequest.call(this, 'GET', `/drive/v3/files/${fileId}`, {}, { alt: 'media' }, undefined, requestOptions);
let mimeType: string | undefined;
let fileName: string | undefined = undefined;
if (response.headers['content-type']) {
mimeType = response.headers['content-type'];
}
if (options.fileName) {
fileName = options.fileName as string;
}
const newItem: INodeExecutionData = {
json: items[i].json,
binary: {},
};
if (items[i].binary !== undefined) {
// Create a shallow copy of the binary data so that the old
// data references which do not get changed still stay behind
// but the incoming data does not get changed.
Object.assign(newItem.binary, items[i].binary);
}
items[i] = newItem;
const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i) as string;
const data = Buffer.from(response.body as string);
items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(data as unknown as Buffer, fileName, mimeType);
} else if (operation === 'list') {
// ----------------------------------
// list
// ----------------------------------
let querySpaces = '';
if (options.spaces) {
const spaces = options.spaces as string[];
if (spaces.includes('*')) {
querySpaces = 'appDataFolder, drive, photos';
} else {
querySpaces = spaces.join(', ');
}
}
let queryCorpora = '';
if (options.corpora) {
queryCorpora = options.corpora as string;
}
let driveId: string | undefined;
driveId = options.driveId as string;
if (driveId === '') {
driveId = undefined;
}
let queryString = '';
const useQueryString = this.getNodeParameter('useQueryString', i) as boolean;
if (useQueryString === true) {
// Use the user defined query string
queryString = this.getNodeParameter('queryString', i) as string;
} else {
// Build query string out of parameters set by user
const queryFilters = this.getNodeParameter('queryFilters', i) as IDataObject;
const queryFilterFields: string[] = [];
if (queryFilters.name) {
(queryFilters.name as IDataObject[]).forEach(nameFilter => {
let operation = nameFilter.operation;
if (operation === 'is') {
operation = '=';
} else if (operation === 'isNot') {
operation = '!=';
}
queryFilterFields.push(`name ${operation} '${nameFilter.value}'`);
});
queryString += queryFilterFields.join(' or ');
}
queryFilterFields.length = 0;
if (queryFilters.mimeType) {
(queryFilters.mimeType as IDataObject[]).forEach(mimeTypeFilter => {
let mimeType = mimeTypeFilter.mimeType;
if (mimeTypeFilter.mimeType === 'custom') {
mimeType = mimeTypeFilter.customMimeType;
}
queryFilterFields.push(`mimeType = '${mimeType}'`);
});
if (queryFilterFields.length) {
if (queryString !== '') {
queryString += ' and ';
}
queryString += queryFilterFields.join(' or ');
}
}
}
const pageSize = this.getNodeParameter('limit', i) as number;
const qs = {
pageSize,
orderBy: 'modifiedTime',
fields: `nextPageToken, files(${queryFields})`,
spaces: querySpaces,
q: queryString,
includeItemsFromAllDrives: (queryCorpora !== '' || driveId !== ''),
supportsAllDrives: (queryCorpora !== '' || driveId !== ''),
};
const response = await googleApiRequest.call(this, 'GET', `/drive/v3/files`, {}, qs);
const files = response!.files;
return [this.helpers.returnJsonArray(files as IDataObject[])];
} else if (operation === 'upload') {
// ----------------------------------
// upload
// ----------------------------------
const resolveData = this.getNodeParameter('resolveData', 0) as boolean;
let mimeType = 'text/plain';
let body;
let originalFilename: string | undefined;
if (this.getNodeParameter('binaryData', i) === true) {
// Is binary file to upload
const item = items[i];
if (item.binary === undefined) {
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
}
const propertyNameUpload = this.getNodeParameter('binaryPropertyName', i) as string;
if (item.binary[propertyNameUpload] === undefined) {
throw new NodeOperationError(this.getNode(), `No binary data property "${propertyNameUpload}" does not exists on item!`);
}
if (item.binary[propertyNameUpload].mimeType) {
mimeType = item.binary[propertyNameUpload].mimeType;
}
if (item.binary[propertyNameUpload].fileName) {
originalFilename = item.binary[propertyNameUpload].fileName;
}
body = Buffer.from(item.binary[propertyNameUpload].data, BINARY_ENCODING);
} else {
// Is text file
body = Buffer.from(this.getNodeParameter('fileContent', i) as string, 'utf8');
}
const name = this.getNodeParameter('name', i) as string;
const parents = this.getNodeParameter('parents', i) as string[];
let qs: IDataObject = {
fields: queryFields,
uploadType: 'media',
};
const requestOptions = {
headers: {
'Content-Type': mimeType,
'Content-Length': body.byteLength,
},
encoding: null,
json: false,
};
let response = await googleApiRequest.call(this, 'POST', `/upload/drive/v3/files`, body, qs, undefined, requestOptions);
body = {
mimeType,
name,
originalFilename,
};
const properties = this.getNodeParameter('options.propertiesUi.propertyValues', i, []) as IDataObject[];
if (properties.length) {
Object.assign(body, { properties: properties.reduce((obj, value) => Object.assign(obj, { [`${value.key}`]: value.value }), {}) } );
}
const appProperties = this.getNodeParameter('options.appPropertiesUi.appPropertyValues', i, []) as IDataObject[];
if (properties.length) {
Object.assign(body, { appProperties: appProperties.reduce((obj, value) => Object.assign(obj, { [`${value.key}`]: value.value }), {}) });
}
qs = {
addParents: parents.join(','),
// When set to true shared drives can be used.
supportsAllDrives: true,
};
response = await googleApiRequest.call(this, 'PATCH', `/drive/v3/files/${JSON.parse(response).id}`, body, qs);
if (resolveData === true) {
response = await googleApiRequest.call(this, 'GET', `/drive/v3/files/${response.id}`, {}, { fields: '*' });
}
returnData.push(response as IDataObject);
} else if (operation === 'update') {
// ----------------------------------
// file:update
// ----------------------------------
const id = this.getNodeParameter('fileId', i) as string;
const updateFields = this.getNodeParameter('updateFields', i, {}) as IDataObject;
const qs: IDataObject = {
supportsAllDrives: true,
};
Object.assign(qs, options);
qs.fields = queryFields;
const body: IDataObject = {};
if (updateFields.fileName) {
body.name = updateFields.fileName;
}
if (updateFields.parentId && updateFields.parentId !== '') {
qs.addParents = updateFields.parentId;
}
const responseData = await googleApiRequest.call(this, 'PATCH', `/drive/v3/files/${id}`, body, qs);
returnData.push(responseData as IDataObject);
}
}
if (resource === 'folder') {
if (operation === 'create') {
// ----------------------------------
// folder:create
// ----------------------------------
const name = this.getNodeParameter('name', i) as string;
const body = {
name,
mimeType: 'application/vnd.google-apps.folder',
parents: options.parents || [],
};
const qs = {
fields: queryFields,
supportsAllDrives: true,
};
const response = await googleApiRequest.call(this, 'POST', '/drive/v3/files', body, qs);
returnData.push(response as IDataObject);
}
}
if (['file', 'folder'].includes(resource)) {
if (operation === 'delete') {
// ----------------------------------
// delete
// ----------------------------------
const fileId = this.getNodeParameter('fileId', i) as string;
const response = await googleApiRequest.call(this, 'DELETE', `/drive/v3/files/${fileId}`);
// If we are still here it did succeed
returnData.push({
fileId,
success: true,
});
}
if (operation === 'share') {
const fileId = this.getNodeParameter('fileId', i) as string;
const permissions = this.getNodeParameter('permissionsUi', i) as IDataObject;
const options = this.getNodeParameter('options', i) as IDataObject;
const body: IDataObject = {};
const qs: IDataObject = {};
if (permissions.permissionsValues) {
Object.assign(body, permissions.permissionsValues);
}
Object.assign(qs, options);
const response = await googleApiRequest.call(this, 'POST', `/drive/v3/files/${fileId}/permissions`, body, qs);
returnData.push(response as IDataObject);
}
}
} catch (error) {
if (this.continueOnFail()) {
if (resource === 'file' && operation === 'download') {
items[i].json = { error: error.message };
} else {
returnData.push({ error: error.message });
}
continue;
}
throw error;
}
}
if (resource === 'file' && operation === 'download') {
// For file downloads the files get attached to the existing items
return this.prepareOutputData(items);
} else {
// For all other ones does the output items get replaced
return [this.helpers.returnJsonArray(returnData)];
}
}
}