From d96d6f11dbe2f1c75ff572baa4906fb628a2eb5c Mon Sep 17 00:00:00 2001 From: Jonathan Bennetts Date: Tue, 15 Nov 2022 13:57:07 +0000 Subject: [PATCH] feat(Google Sheets Node): Overhaul of node --- .../GoogleSheetsOAuth2Api.credentials.ts | 1 + .../nodes/Google/Sheet/GoogleSheets.node.ts | 1417 +---------------- .../Google/Sheet/{ => v1}/GenericFunctions.ts | 0 .../Google/Sheet/{ => v1}/GoogleSheet.ts | 0 .../Google/Sheet/v1/GoogleSheetsV1.node.ts | 502 ++++++ .../Google/Sheet/v1/versionDescription.ts | 880 ++++++++++ .../Google/Sheet/v2/GoogleSheetsV2.node.ts | 25 + .../nodes/Google/Sheet/v2/actions/router.ts | 68 + .../Sheet/v2/actions/sheet/Sheet.resource.ts | 199 +++ .../v2/actions/sheet/append.operation.ts | 188 +++ .../actions/sheet/appendOrUpdate.operation.ts | 315 ++++ .../Sheet/v2/actions/sheet/clear.operation.ts | 210 +++ .../v2/actions/sheet/commonDescription.ts | 274 ++++ .../v2/actions/sheet/create.operation.ts | 127 ++ .../v2/actions/sheet/delete.operation.ts | 169 ++ .../Sheet/v2/actions/sheet/read.operation.ts | 168 ++ .../v2/actions/sheet/remove.operation.ts | 36 + .../v2/actions/sheet/update.operation.ts | 302 ++++ .../spreadsheet/SpreadSheet.resource.ts | 36 + .../actions/spreadsheet/create.operation.ts | 153 ++ .../actions/spreadsheet/delete.operation.ts | 103 ++ .../Sheet/v2/actions/versionDescription.ts | 79 + .../Google/Sheet/v2/helpers/GoogleSheet.ts | 657 ++++++++ .../Sheet/v2/helpers/GoogleSheets.types.ts | 85 + .../Sheet/v2/helpers/GoogleSheets.utils.ts | 299 ++++ .../Google/Sheet/v2/methods/credentialTest.ts | 35 + .../nodes/Google/Sheet/v2/methods/index.ts | 3 + .../Google/Sheet/v2/methods/listSearch.ts | 96 ++ .../Google/Sheet/v2/methods/loadOptions.ts | 112 ++ .../nodes/Google/Sheet/v2/transport/index.ts | 156 ++ 30 files changed, 5301 insertions(+), 1394 deletions(-) rename packages/nodes-base/nodes/Google/Sheet/{ => v1}/GenericFunctions.ts (100%) rename packages/nodes-base/nodes/Google/Sheet/{ => v1}/GoogleSheet.ts (100%) create mode 100644 packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheetsV1.node.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v1/versionDescription.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/GoogleSheetsV2.node.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/router.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/Sheet.resource.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/append.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/clear.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/commonDescription.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/create.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/delete.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/remove.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/spreadsheet/SpreadSheet.resource.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/spreadsheet/create.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/spreadsheet/delete.operation.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/actions/versionDescription.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.types.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/methods/credentialTest.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/methods/index.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/methods/listSearch.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/methods/loadOptions.ts create mode 100644 packages/nodes-base/nodes/Google/Sheet/v2/transport/index.ts diff --git a/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts index c35847f6c2..578cb4f752 100644 --- a/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts @@ -3,6 +3,7 @@ import { ICredentialType, INodeProperties } from 'n8n-workflow'; const scopes = [ 'https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/spreadsheets', + 'https://www.googleapis.com/auth/drive.metadata', ]; export class GoogleSheetsOAuth2Api implements ICredentialType { diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index 960e38d10b..de374ff9bf 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -1,1396 +1,25 @@ -import { IExecuteFunctions } from 'n8n-core'; - -import { - ICredentialsDecrypted, - ICredentialTestFunctions, - IDataObject, - ILoadOptionsFunctions, - INodeCredentialTestResult, - INodeExecutionData, - INodePropertyOptions, - INodeType, - INodeTypeDescription, - NodeOperationError, -} from 'n8n-workflow'; - -import { - GoogleSheet, - ILookupValues, - ISheetUpdateData, - IToDelete, - ValueInputOption, - ValueRenderOption, -} from './GoogleSheet'; - -import { - getAccessToken, - googleApiRequest, - hexToRgb, - IGoogleAuthCredentials, -} from './GenericFunctions'; - -export class GoogleSheets implements INodeType { - description: INodeTypeDescription = { - displayName: 'Google Sheets ', - name: 'googleSheets', - icon: 'file:googleSheets.svg', - group: ['input', 'output'], - version: [1, 2], - subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Read, update and write data to Google Sheets', - defaults: { - name: 'Google Sheets', - }, - inputs: ['main'], - outputs: ['main'], - credentials: [ - { - name: 'googleApi', - required: true, - displayOptions: { - show: { - authentication: ['serviceAccount'], - }, - }, - testedBy: 'googleApiCredentialTest', - }, - { - name: 'googleSheetsOAuth2Api', - 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', - displayOptions: { - show: { - '@version': [1], - }, - }, - }, - { - displayName: 'Authentication', - name: 'authentication', - type: 'options', - options: [ - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'OAuth2 (recommended)', - value: 'oAuth2', - }, - { - name: 'Service Account', - value: 'serviceAccount', - }, - ], - default: 'oAuth2', - displayOptions: { - show: { - '@version': [2], - }, - }, - }, - { - displayName: 'Resource', - name: 'resource', - type: 'options', - noDataExpression: true, - options: [ - { - name: 'Spreadsheet', - value: 'spreadsheet', - }, - { - name: 'Sheet', - value: 'sheet', - }, - ], - default: 'sheet', - }, - { - displayName: 'Operation', - name: 'operation', - type: 'options', - noDataExpression: true, - displayOptions: { - show: { - resource: ['sheet'], - }, - }, - options: [ - { - name: 'Append', - value: 'append', - description: 'Append data to a sheet', - action: 'Append data to a sheet', - }, - { - name: 'Clear', - value: 'clear', - description: 'Clear data from a sheet', - action: 'Clear a sheet', - }, - { - name: 'Create', - value: 'create', - description: 'Create a new sheet', - action: 'Create a sheet', - }, - { - name: 'Create or Update', - value: 'upsert', - description: - 'Create a new record, or update the current one if it already exists (upsert)', - action: 'Create or update a sheet', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete columns and rows from a sheet', - action: 'Delete a sheet', - }, - { - name: 'Lookup', - value: 'lookup', - description: 'Look up a specific column value and return the matching row', - action: 'Look up a column value in a sheet', - }, - { - name: 'Read', - value: 'read', - description: 'Read data from a sheet', - action: 'Read a sheet', - }, - { - name: 'Remove', - value: 'remove', - description: 'Remove a sheet', - action: 'Remove a sheet', - }, - { - name: 'Update', - value: 'update', - description: 'Update rows in a sheet', - action: 'Update a sheet', - }, - ], - default: 'read', - }, - - // ---------------------------------- - // All - // ---------------------------------- - { - displayName: 'Spreadsheet ID', - name: 'sheetId', - type: 'string', - displayOptions: { - show: { - resource: ['sheet'], - }, - }, - default: '', - required: true, - description: - 'The ID of the Google Spreadsheet. Found as part of the sheet URL https://docs.google.com/spreadsheets/d/{ID}/.', - }, - { - displayName: 'Range', - name: 'range', - type: 'string', - displayOptions: { - show: { - resource: ['sheet'], - }, - hide: { - operation: ['create', 'delete', 'remove'], - }, - }, - default: 'A:F', - required: true, - description: - 'The table range to read from or to append data to. See the Google documentation for the details. If it contains multiple sheets it can also be added like this: "MySheet!A:F"', - }, - - // ---------------------------------- - // Delete - // ---------------------------------- - { - displayName: 'To Delete', - name: 'toDelete', - placeholder: 'Add Columns/Rows to delete', - description: 'Deletes columns and rows from a sheet', - type: 'fixedCollection', - typeOptions: { - multipleValues: true, - }, - displayOptions: { - show: { - resource: ['sheet'], - operation: ['delete'], - }, - }, - default: {}, - options: [ - { - displayName: 'Columns', - name: 'columns', - values: [ - { - displayName: 'Sheet Name or ID', - name: 'sheetId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getSheets', - }, - options: [], - default: '', - required: true, - description: - 'The sheet to delete columns from. Choose from the list, or specify an ID using an expression.', - }, - { - displayName: 'Start Index', - name: 'startIndex', - type: 'number', - typeOptions: { - minValue: 0, - }, - default: 0, - description: 'The start index (0 based and inclusive) of column to delete', - }, - { - displayName: 'Amount', - name: 'amount', - type: 'number', - typeOptions: { - minValue: 1, - }, - default: 1, - description: 'Number of columns to delete', - }, - ], - }, - { - displayName: 'Rows', - name: 'rows', - values: [ - { - displayName: 'Sheet Name or ID', - name: 'sheetId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getSheets', - }, - options: [], - default: '', - required: true, - description: - 'The sheet to delete columns from. Choose from the list, or specify an ID using an expression.', - }, - { - displayName: 'Start Index', - name: 'startIndex', - type: 'number', - typeOptions: { - minValue: 0, - }, - default: 0, - description: 'The start index (0 based and inclusive) of row to delete', - }, - { - displayName: 'Amount', - name: 'amount', - type: 'number', - typeOptions: { - minValue: 1, - }, - default: 1, - description: 'Number of rows to delete', - }, - ], - }, - ], - }, - - // ---------------------------------- - // Read - // ---------------------------------- - { - displayName: 'RAW Data', - name: 'rawData', - type: 'boolean', - displayOptions: { - show: { - resource: ['sheet'], - operation: ['read'], - }, - }, - default: false, - description: - 'Whether the data should be returned RAW instead of parsed into keys according to their header', - }, - { - displayName: 'Data Property', - name: 'dataProperty', - type: 'string', - default: 'data', - displayOptions: { - show: { - resource: ['sheet'], - operation: ['read'], - rawData: [true], - }, - }, - description: 'The name of the property into which to write the RAW data', - }, - - // ---------------------------------- - // Update - // ---------------------------------- - { - displayName: 'RAW Data', - name: 'rawData', - type: 'boolean', - displayOptions: { - show: { - resource: ['sheet'], - operation: ['update', 'upsert'], - }, - }, - default: false, - description: 'Whether the data supplied is RAW instead of parsed into keys', - }, - { - displayName: 'Data Property', - name: 'dataProperty', - type: 'string', - default: 'data', - displayOptions: { - show: { - resource: ['sheet'], - operation: ['update', 'upsert'], - rawData: [true], - }, - }, - description: 'The name of the property from which to read the RAW data', - }, - - // ---------------------------------- - // Read & Update & lookupColumn - // ---------------------------------- - { - displayName: 'Data Start Row', - name: 'dataStartRow', - type: 'number', - typeOptions: { - minValue: 1, - }, - default: 1, - displayOptions: { - show: { - resource: ['sheet'], - }, - hide: { - operation: ['append', 'create', 'clear', 'delete', 'remove'], - rawData: [true], - }, - }, - description: - 'Index of the first row which contains the actual data and not the keys. Starts with 0.', - }, - - // ---------------------------------- - // Mixed - // ---------------------------------- - { - displayName: 'Key Row', - name: 'keyRow', - type: 'number', - typeOptions: { - minValue: 0, - }, - displayOptions: { - show: { - resource: ['sheet'], - }, - hide: { - operation: ['clear', 'create', 'delete', 'remove'], - rawData: [true], - }, - }, - default: 0, - description: - 'Index of the row which contains the keys. Starts at 0. The incoming node data is matched to the keys for assignment. The matching is case sensitive.', - }, - - // ---------------------------------- - // lookup - // ---------------------------------- - { - displayName: 'Lookup Column', - name: 'lookupColumn', - type: 'string', - default: '', - placeholder: 'Email', - required: true, - displayOptions: { - show: { - resource: ['sheet'], - operation: ['lookup'], - }, - }, - description: 'The name of the column in which to look for value', - }, - { - displayName: 'Lookup Value', - name: 'lookupValue', - type: 'string', - default: '', - placeholder: 'frank@example.com', - displayOptions: { - show: { - resource: ['sheet'], - operation: ['lookup'], - }, - }, - description: 'The value to look for in column', - }, - - // ---------------------------------- - // Update - // ---------------------------------- - { - displayName: 'Key', - name: 'key', - type: 'string', - default: 'id', - displayOptions: { - show: { - resource: ['sheet'], - operation: ['update', 'upsert'], - rawData: [false], - }, - }, - description: 'The name of the key to identify which data should be updated in the sheet', - }, - - { - displayName: 'Options', - name: 'options', - type: 'collection', - placeholder: 'Add Option', - default: {}, - displayOptions: { - show: { - resource: ['sheet'], - operation: ['append', 'lookup', 'read', 'update', 'upsert'], - }, - }, - options: [ - { - displayName: 'Continue If Empty', - name: 'continue', - type: 'boolean', - default: false, - displayOptions: { - show: { - '/operation': ['lookup', 'read'], - }, - }, - // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether - description: - 'By default, the workflow stops executing if the lookup/read does not return values', - }, - { - displayName: 'Return All Matches', - name: 'returnAllMatches', - type: 'boolean', - default: false, - displayOptions: { - show: { - '/operation': ['lookup'], - }, - }, - // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether - description: - 'By default only the first result gets returned. If options gets set all found matches get returned.', - }, - { - displayName: 'Use Header Names as JSON Paths', - name: 'usePathForKeyRow', - type: 'boolean', - default: false, - displayOptions: { - show: { - '/operation': ['append'], - }, - }, - description: - 'Whether you want to match the headers as path, for example, the row header "category.name" will match the "category" object and get the field "name" from it. By default "category.name" will match with the field with exact name, not nested object.', - }, - { - displayName: 'Value Input Mode', - name: 'valueInputMode', - type: 'options', - displayOptions: { - show: { - '/operation': ['append', 'update', 'upsert'], - }, - }, - options: [ - { - name: 'RAW', - value: 'RAW', - description: 'The values will not be parsed and will be stored as-is', - }, - { - name: 'User Entered', - value: 'USER_ENTERED', - description: - 'The values will be parsed as if the user typed them into the UI. Numbers will stay as numbers, but strings may be converted to numbers, dates, etc. following the same rules that are applied when entering text into a cell via the Google Sheets UI.', - }, - ], - default: 'RAW', - description: 'Determines how data should be interpreted', - }, - { - displayName: 'Value Render Mode', - name: 'valueRenderMode', - type: 'options', - displayOptions: { - show: { - '/operation': ['lookup', 'read'], - }, - }, - options: [ - { - name: 'Formatted Value', - value: 'FORMATTED_VALUE', - description: - "Values will be calculated & formatted in the reply according to the cell's formatting.Formatting is based on the spreadsheet's locale, not the requesting user's locale.For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return \"$1.23\"", - }, - { - name: 'Formula', - value: 'FORMULA', - description: - 'Values will not be calculated. The reply will include the formulas. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return "=A1".', - }, - { - name: 'Unformatted Value', - value: 'UNFORMATTED_VALUE', - description: - 'Values will be calculated, but not formatted in the reply. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return the number 1.23.', - }, - ], - default: 'UNFORMATTED_VALUE', - description: 'Determines how values should be rendered in the output', - }, - { - displayName: 'Value Render Mode', - name: 'valueRenderMode', - type: 'options', - displayOptions: { - show: { - '/operation': ['update', 'upsert'], - '/rawData': [false], - }, - }, - options: [ - { - name: 'Formatted Value', - value: 'FORMATTED_VALUE', - description: - "Values will be calculated & formatted in the reply according to the cell's formatting.Formatting is based on the spreadsheet's locale, not the requesting user's locale. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return \"$1.23\".", - }, - { - name: 'Formula', - value: 'FORMULA', - description: - 'Values will not be calculated. The reply will include the formulas. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return "=A1".', - }, - { - name: 'Unformatted Value', - value: 'UNFORMATTED_VALUE', - description: - 'Values will be calculated, but not formatted in the reply. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return the number 1.23.', - }, - ], - default: 'UNFORMATTED_VALUE', - description: 'Determines how values should be rendered in the output', - }, - ], - }, - - { - displayName: 'Operation', - name: 'operation', - type: 'options', - noDataExpression: true, - displayOptions: { - show: { - resource: ['spreadsheet'], - }, - }, - options: [ - { - name: 'Create', - value: 'create', - description: 'Create a spreadsheet', - action: 'Create a spreadsheet', - }, - ], - default: 'create', - }, - // ---------------------------------- - // spreadsheet:create - // ---------------------------------- - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - displayOptions: { - show: { - resource: ['spreadsheet'], - operation: ['create'], - }, - }, - description: 'The title of the spreadsheet', - }, - { - displayName: 'Sheets', - name: 'sheetsUi', - placeholder: 'Add Sheet', - type: 'fixedCollection', - typeOptions: { - multipleValues: true, - }, - default: {}, - displayOptions: { - show: { - resource: ['spreadsheet'], - operation: ['create'], - }, - }, - options: [ - { - name: 'sheetValues', - displayName: 'Sheet', - values: [ - { - displayName: 'Sheet Properties', - name: 'propertiesUi', - placeholder: 'Add Property', - type: 'collection', - default: {}, - options: [ - { - displayName: 'Hidden', - name: 'hidden', - type: 'boolean', - default: false, - description: 'Whether the Sheet should be hidden in the UI', - }, - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - description: 'Title of the property to create', - }, - ], - }, - ], - }, - ], - }, - { - displayName: 'Options', - name: 'options', - type: 'collection', - placeholder: 'Add Option', - default: {}, - displayOptions: { - show: { - resource: ['spreadsheet'], - operation: ['create'], - }, - }, - options: [ - { - displayName: 'Locale', - name: 'locale', - type: 'string', - default: '', - placeholder: 'en_US', - description: `The locale of the spreadsheet in one of the following formats: -