diff --git a/docker/images/n8n-custom/docker-entrypoint.sh b/docker/images/n8n-custom/docker-entrypoint.sh index 2c9c35eb48..2dd4dae105 100755 --- a/docker/images/n8n-custom/docker-entrypoint.sh +++ b/docker/images/n8n-custom/docker-entrypoint.sh @@ -8,9 +8,16 @@ fi if [ "$#" -gt 0 ]; then # Got started with arguments - shift - exec su-exec node ./packages/cli/bin/n8n "$@" + COMMAND=$1; + + if [[ "$COMMAND" == "n8n" ]]; then + shift + exec su-exec node ./packages/cli/bin/n8n "$@" + else + exec su-exec node "$@" + fi + else - # Got started without arguments - exec su-exec node ./packages/cli/bin/n8n +# Got started without arguments +exec su-exec node ./packages/cli/bin/n8n fi diff --git a/docker/images/n8n/README.md b/docker/images/n8n/README.md index 8088150821..e1b39d82d6 100644 --- a/docker/images/n8n/README.md +++ b/docker/images/n8n/README.md @@ -17,6 +17,7 @@ n8n is a free and open [fair-code](http://faircode.io) licensed node based Workf - [Securing n8n](#securing-n8n) - [Persist data](#persist-data) - [Passing Sensitive Data via File](#passing-sensitive-data-via-file) +- [Updating a Running docker-compose Instance](#updating-a-running-docker-compose-instance) - [Example Setup with Lets Encrypt](#example-setup-with-lets-encrypt) - [What does n8n mean and how do you pronounce it](#what-does-n8n-mean-and-how-do-you-pronounce-it) - [Support](#support) @@ -226,6 +227,18 @@ The following environment variables support file input: A basic step by step example setup of n8n with docker-compose and Lets Encrypt is available on the [Server Setup](https://docs.n8n.io/#/server-setup) page. +## Updating a running docker-compose instance + +``` +# Pull down the latest version from dockerhub +docker pull n8nio/n8n +# Stop current setup +sudo docker-compose stop +# Delete it (will only delete the docker-containers, data is stored separately) +sudo docker-compose rm +# Then start it again +sudo docker-compose up -d +``` ## Setting Timezone diff --git a/packages/nodes-base/credentials/GoogleCalendarOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleCalendarOAuth2Api.credentials.ts new file mode 100644 index 0000000000..cf052f0f52 --- /dev/null +++ b/packages/nodes-base/credentials/GoogleCalendarOAuth2Api.credentials.ts @@ -0,0 +1,25 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +const scopes = [ + 'https://www.googleapis.com/auth/calendar', + 'https://www.googleapis.com/auth/calendar.events', +]; + +export class GoogleCalendarOAuth2Api implements ICredentialType { + name = 'googleCalendarOAuth2Api'; + extends = [ + 'googleOAuth2Api', + ]; + displayName = 'Google Calendar OAuth2 API'; + properties = [ + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' '), + }, + ]; +} diff --git a/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts index facd5e8fac..8f658517b9 100644 --- a/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts @@ -3,11 +3,6 @@ import { NodePropertyTypes, } from 'n8n-workflow'; -const scopes = [ - 'https://www.googleapis.com/auth/calendar', - 'https://www.googleapis.com/auth/calendar.events', -]; - export class GoogleOAuth2Api implements ICredentialType { name = 'googleOAuth2Api'; extends = [ @@ -27,12 +22,6 @@ export class GoogleOAuth2Api implements ICredentialType { type: 'hidden' as NodePropertyTypes, default: 'https://oauth2.googleapis.com/token', }, - { - displayName: 'Scope', - name: 'scope', - type: 'hidden' as NodePropertyTypes, - default: scopes.join(' '), - }, { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', diff --git a/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts new file mode 100644 index 0000000000..f1e00a0ad9 --- /dev/null +++ b/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts @@ -0,0 +1,26 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +const scopes = [ + 'https://www.googleapis.com/auth/drive', + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/spreadsheets', +]; + +export class GoogleSheetsOAuth2Api implements ICredentialType { + name = 'googleSheetsOAuth2Api'; + extends = [ + 'googleOAuth2Api', + ]; + displayName = 'Google Sheets OAuth2 API'; + properties = [ + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' '), + }, + ]; +} diff --git a/packages/nodes-base/nodes/EditImage.node.ts b/packages/nodes-base/nodes/EditImage.node.ts index b08bd88f4e..88db92421a 100644 --- a/packages/nodes-base/nodes/EditImage.node.ts +++ b/packages/nodes-base/nodes/EditImage.node.ts @@ -432,6 +432,80 @@ export class EditImage implements INodeType { }, description: 'The color to use for the background when image gets rotated by anything which is not a multiple of 90..', }, + + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + hide: { + operation: [ + 'information', + ], + }, + }, + options: [ + { + displayName: 'File Name', + name: 'fileName', + type: 'string', + default: '', + description: 'File name to set in binary data.', + }, + { + displayName: 'Format', + name: 'format', + type: 'options', + options: [ + { + name: 'bmp', + value: 'bmp', + }, + { + name: 'gif', + value: 'gif', + }, + { + name: 'jpeg', + value: 'jpeg', + }, + { + name: 'png', + value: 'png', + }, + { + name: 'tiff', + value: 'tiff', + }, + ], + default: 'jpeg', + description: 'Set the output image format.', + }, + { + displayName: 'Quality', + name: 'quality', + type: 'number', + typeOptions: { + minValue: 0, + maxValue: 100, + }, + default: 100, + displayOptions: { + show: { + format: [ + 'jpeg', + 'png', + 'tiff', + ], + }, + }, + description: 'Sets the jpeg|png|tiff compression level from 0 to 100 (best).', + }, + + ], + }, ] }; @@ -442,6 +516,8 @@ export class EditImage implements INodeType { const operation = this.getNodeParameter('operation', 0) as string; const dataPropertyName = this.getNodeParameter('dataPropertyName') as string; + const options = this.getNodeParameter('options', {}) as IDataObject; + // TODO: Later should make so that it sends directly a valid buffer and the buffer.from stuff is not needed anymore if (item.binary === undefined) { return item; @@ -550,6 +626,24 @@ export class EditImage implements INodeType { Object.assign(newItem.binary, item.binary); } + if (options.quality !== undefined) { + gmInstance = gmInstance.quality(options.quality as number); + } + + if (options.format !== undefined) { + gmInstance = gmInstance.setFormat(options.format as string); + newItem.binary![dataPropertyName as string].fileExtension = options.format as string; + newItem.binary![dataPropertyName as string].mimeType = `image/${options.format}`; + const fileName = newItem.binary![dataPropertyName as string].fileName; + if (fileName && fileName.includes('.')) { + newItem.binary![dataPropertyName as string].fileName = fileName.split('.').slice(0, -1).join('.') + '.' + options.format; + } + } + + if (options.fileName !== undefined) { + newItem.binary![dataPropertyName as string].fileName = options.fileName as string; + } + return new Promise((resolve, reject) => { gmInstance .toBuffer((error: Error | null, buffer: Buffer) => { diff --git a/packages/nodes-base/nodes/Github/Github.node.ts b/packages/nodes-base/nodes/Github/Github.node.ts index 6e0df194ec..00133ec302 100644 --- a/packages/nodes-base/nodes/Github/Github.node.ts +++ b/packages/nodes-base/nodes/Github/Github.node.ts @@ -47,7 +47,7 @@ export class Github implements INodeType { displayOptions: { show: { authentication: [ - 'oauth2', + 'oAuth2', ], }, }, @@ -65,7 +65,7 @@ export class Github implements INodeType { }, { name: 'OAuth2', - value: 'oauth2', + value: 'oAuth2', }, ], default: 'accessToken', diff --git a/packages/nodes-base/nodes/Google/EventDescription.ts b/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Google/EventDescription.ts rename to packages/nodes-base/nodes/Google/Calendar/EventDescription.ts diff --git a/packages/nodes-base/nodes/Google/EventInterface.ts b/packages/nodes-base/nodes/Google/Calendar/EventInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Google/EventInterface.ts rename to packages/nodes-base/nodes/Google/Calendar/EventInterface.ts diff --git a/packages/nodes-base/nodes/Google/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Calendar/GenericFunctions.ts similarity index 91% rename from packages/nodes-base/nodes/Google/GenericFunctions.ts rename to packages/nodes-base/nodes/Google/Calendar/GenericFunctions.ts index b8c18429ef..b772a47d0c 100644 --- a/packages/nodes-base/nodes/Google/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GenericFunctions.ts @@ -1,11 +1,15 @@ -import { OptionsWithUri } from 'request'; +import { + OptionsWithUri, + } from 'request'; + import { IExecuteFunctions, IExecuteSingleFunctions, ILoadOptionsFunctions, } from 'n8n-core'; + import { - IDataObject + IDataObject, } from 'n8n-workflow'; export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise { // tslint:disable-line:no-any @@ -27,7 +31,7 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF delete options.body; } //@ts-ignore - return await this.helpers.requestOAuth2.call(this, 'googleOAuth2Api', options); + return await this.helpers.requestOAuth.call(this, 'googleCalendarOAuth2Api', options); } catch (error) { if (error.response && error.response.body && error.response.body.message) { // Try to return the error prettier diff --git a/packages/nodes-base/nodes/Google/GoogleCalendar.node.ts b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts similarity index 99% rename from packages/nodes-base/nodes/Google/GoogleCalendar.node.ts rename to packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts index c7eb35794e..1f185935d7 100644 --- a/packages/nodes-base/nodes/Google/GoogleCalendar.node.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts @@ -44,7 +44,7 @@ export class GoogleCalendar implements INodeType { outputs: ['main'], credentials: [ { - name: 'googleOAuth2Api', + name: 'googleCalendarOAuth2Api', required: true, }, ], diff --git a/packages/nodes-base/nodes/Google/googleCalendar.png b/packages/nodes-base/nodes/Google/Calendar/googleCalendar.png similarity index 100% rename from packages/nodes-base/nodes/Google/googleCalendar.png rename to packages/nodes-base/nodes/Google/Calendar/googleCalendar.png diff --git a/packages/nodes-base/nodes/Google/GoogleDrive.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts similarity index 99% rename from packages/nodes-base/nodes/Google/GoogleDrive.node.ts rename to packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts index 4adfbd239b..387a3d7bc0 100644 --- a/packages/nodes-base/nodes/Google/GoogleDrive.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts @@ -12,7 +12,7 @@ import { INodeType, } from 'n8n-workflow'; -import { getAuthenticationClient } from './GoogleApi'; +import { getAuthenticationClient } from '../GoogleApi'; export class GoogleDrive implements INodeType { diff --git a/packages/nodes-base/nodes/Google/GoogleDriveTrigger.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDriveTrigger.node.ts similarity index 100% rename from packages/nodes-base/nodes/Google/GoogleDriveTrigger.node.ts rename to packages/nodes-base/nodes/Google/Drive/GoogleDriveTrigger.node.ts diff --git a/packages/nodes-base/nodes/Google/googleDrive.png b/packages/nodes-base/nodes/Google/Drive/googleDrive.png similarity index 100% rename from packages/nodes-base/nodes/Google/googleDrive.png rename to packages/nodes-base/nodes/Google/Drive/googleDrive.png diff --git a/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts new file mode 100644 index 0000000000..dff54fa6a8 --- /dev/null +++ b/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts @@ -0,0 +1,129 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +import * as moment from 'moment-timezone'; + +import * as jwt from 'jsonwebtoken'; + +export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise { // tslint:disable-line:no-any + const authenticationMethod = this.getNodeParameter('authentication', 0, 'serviceAccount') as string; + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://sheets.googleapis.com${resource}`, + json: true + }; + try { + if (Object.keys(headers).length !== 0) { + options.headers = Object.assign({}, options.headers, headers); + } + if (Object.keys(body).length === 0) { + delete options.body; + } + + if (authenticationMethod === 'serviceAccount') { + const credentials = this.getCredentials('googleApi'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const { access_token } = await getAccessToken.call(this, credentials as IDataObject); + + options.headers!.Authorization = `Bearer ${access_token}`; + //@ts-ignore + return await this.helpers.request(options); + } else { + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'googleSheetsOAuth2Api', options); + } + } catch (error) { + if (error.response && error.response.body && error.response.body.message) { + // Try to return the error prettier + throw new Error(`Google Sheet error response [${error.statusCode}]: ${error.response.body.message}`); + } + throw error; + } +} + +export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + query.maxResults = 100; + + do { + responseData = await googleApiRequest.call(this, method, endpoint, body, query); + query.pageToken = responseData['nextPageToken']; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData['nextPageToken'] !== undefined && + responseData['nextPageToken'] !== '' + ); + + return returnData; +} + +function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IDataObject) : Promise { + //https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const scopes = [ + 'https://www.googleapis.com/auth/drive', + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/spreadsheets', + ]; + + const now = moment().unix(); + + const signature = jwt.sign( + { + 'iss': credentials.email as string, + 'sub': credentials.email as string, + 'scope': scopes.join(' '), + 'aud': `https://oauth2.googleapis.com/token`, + 'iat': now, + 'exp': now + 3600, + }, + credentials.privateKey as string, + { + algorithm: 'RS256', + header: { + 'kid': credentials.privateKey as string, + 'typ': 'JWT', + 'alg': 'RS256', + }, + } + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true + }; + + //@ts-ignore + return this.helpers.request(options); +} diff --git a/packages/nodes-base/nodes/Google/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts similarity index 80% rename from packages/nodes-base/nodes/Google/GoogleSheet.ts rename to packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts index 2d31c884d2..0766a81f57 100644 --- a/packages/nodes-base/nodes/Google/GoogleSheet.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts @@ -1,14 +1,20 @@ -import { IDataObject } from 'n8n-workflow'; -import { google, sheets_v4 } from 'googleapis'; -import { JWT } from 'google-auth-library'; -import { getAuthenticationClient } from './GoogleApi'; +import { + IDataObject, +} from 'n8n-workflow'; + +import { + IExecuteFunctions, + ILoadOptionsFunctions, + } from 'n8n-core'; + +import { + googleApiRequest, + } from './GenericFunctions'; import { utils as xlsxUtils, } from 'xlsx'; -const Sheets = google.sheets('v4'); // tslint:disable-line:variable-name - export interface ISheetOptions { scope: string[]; } @@ -46,18 +52,16 @@ export type ValueRenderOption = 'FORMATTED_VALUE' | 'FORMULA' | 'UNFORMATTED_VAL export class GoogleSheet { id: string; - credentials: IGoogleAuthCredentials; - scopes: string[]; + executeFunctions: IExecuteFunctions | ILoadOptionsFunctions; - constructor(spreadsheetId: string, credentials: IGoogleAuthCredentials, options?: ISheetOptions | undefined) { + constructor(spreadsheetId: string, executeFunctions: IExecuteFunctions | ILoadOptionsFunctions, options?: ISheetOptions | undefined) { // options = options || {}; if (!options) { options = {} as ISheetOptions; } + this.executeFunctions = executeFunctions; this.id = spreadsheetId; - this.credentials = credentials; - this.scopes = options.scope || ['https://www.googleapis.com/auth/spreadsheets']; } @@ -69,37 +73,29 @@ export class GoogleSheet { * @memberof GoogleSheet */ async clearData(range: string): Promise { - const client = await this.getAuthenticationClient(); - // @ts-ignore - const response = await Sheets.spreadsheets.values.clear( - { - auth: client, - spreadsheetId: this.id, - range, - } - ); + const body = { + spreadsheetId: this.id, + range, + }; - return response.data; + const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}:clear`, body); + + return response; } /** * Returns the cell values */ async getData(range: string, valueRenderMode: ValueRenderOption): Promise { - const client = await this.getAuthenticationClient(); - // @ts-ignore - const response = await Sheets.spreadsheets.values.get( - { - auth: client, - spreadsheetId: this.id, - range, - valueRenderOption: valueRenderMode, - } - ); + const query = { + valueRenderOption: valueRenderMode, + }; - return response.data.values as string[][] | undefined; + const response = await googleApiRequest.call(this.executeFunctions, 'GET', `/v4/spreadsheets/${this.id}/values/${range}`, {}, query); + + return response.values as string[][] | undefined; } @@ -107,39 +103,29 @@ export class GoogleSheet { * Returns the sheets in a Spreadsheet */ async spreadsheetGetSheets() { - const client = await this.getAuthenticationClient(); - // @ts-ignore - const response = await Sheets.spreadsheets.get( - { - auth: client, - spreadsheetId: this.id, - fields: 'sheets.properties' - } - ); + const query = { + fields: 'sheets.properties', + }; - return response.data; + const response = await googleApiRequest.call(this.executeFunctions, 'GET', `/v4/spreadsheets/${this.id}`, {}, query); + + return response; } /** * Sets values in one or more ranges of a spreadsheet. */ - async spreadsheetBatchUpdate(requests: sheets_v4.Schema$Request[]) { // tslint:disable-line:no-any - const client = await this.getAuthenticationClient(); + async spreadsheetBatchUpdate(requests: IDataObject[]) { // tslint:disable-line:no-any - // @ts-ignore - const response = await Sheets.spreadsheets.batchUpdate( - { - auth: client, - spreadsheetId: this.id, - requestBody: { - requests, - }, - } - ); + const body = { + requests + }; - return response.data; + const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}:batchUpdate`, body); + + return response; } @@ -147,21 +133,15 @@ export class GoogleSheet { * Sets the cell values */ async batchUpdate(updateData: ISheetUpdateData[], valueInputMode: ValueInputOption) { - const client = await this.getAuthenticationClient(); - // @ts-ignore - const response = await Sheets.spreadsheets.values.batchUpdate( - { - auth: client, - spreadsheetId: this.id, - valueInputOption: valueInputMode, - resource: { - data: updateData, - }, - } - ); + const body = { + data: updateData, + valueInputOption: valueInputMode, + }; - return response.data; + const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values:batchUpdate`, body); + + return response; } @@ -169,23 +149,15 @@ export class GoogleSheet { * Sets the cell values */ async setData(range: string, data: string[][], valueInputMode: ValueInputOption) { - const client = await this.getAuthenticationClient(); - // @ts-ignore - const response = await Sheets.spreadsheets.values.update( - { - // @ts-ignore - auth: client, - spreadsheetId: this.id, - range, - valueInputOption: valueInputMode, - resource: { - values: data - } - } - ); + const body = { + valueInputOption: valueInputMode, + values: data, + }; - return response.data; + const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}`, body); + + return response; } @@ -193,33 +165,21 @@ export class GoogleSheet { * Appends the cell values */ async appendData(range: string, data: string[][], valueInputMode: ValueInputOption) { - const client = await this.getAuthenticationClient(); - // @ts-ignore - const response = await Sheets.spreadsheets.values.append( - { - auth: client, - spreadsheetId: this.id, - range, - valueInputOption: valueInputMode, - resource: { - values: data - } - } - ); + const body = { + range, + values: data, + }; - return response.data; + const query = { + valueInputOption: valueInputMode, + }; + + const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}:append`, body, query); + + return response; } - - /** - * Returns the authentication client needed to access spreadsheet - */ - async getAuthenticationClient(): Promise { - return getAuthenticationClient(this.credentials.email, this.credentials.privateKey, this.scopes); - } - - /** * Returns the given sheet data in a strucutred way */ @@ -505,5 +465,4 @@ export class GoogleSheet { return setData; } - } diff --git a/packages/nodes-base/nodes/Google/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts similarity index 95% rename from packages/nodes-base/nodes/Google/GoogleSheets.node.ts rename to packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index 71e76698de..90e4e8b1fd 100644 --- a/packages/nodes-base/nodes/Google/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -1,6 +1,8 @@ -import { sheets_v4 } from 'googleapis'; -import { IExecuteFunctions } from 'n8n-core'; +import { + IExecuteFunctions, + } from 'n8n-core'; + import { IDataObject, ILoadOptionsFunctions, @@ -12,7 +14,6 @@ import { import { GoogleSheet, - IGoogleAuthCredentials, ILookupValues, ISheetUpdateData, IToDelete, @@ -30,7 +31,7 @@ export class GoogleSheets implements INodeType { description: 'Read, update and write data to Google Sheets', defaults: { name: 'Google Sheets', - color: '#995533', + color: '#0aa55c', }, inputs: ['main'], outputs: ['main'], @@ -38,9 +39,43 @@ export class GoogleSheets implements INodeType { { name: 'googleApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'serviceAccount', + ], + }, + }, + }, + { + 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', + }, { displayName: 'Operation', name: 'operation', @@ -541,18 +576,7 @@ export class GoogleSheets implements INodeType { async getSheets(this: ILoadOptionsFunctions): Promise { const spreadsheetId = this.getCurrentNodeParameter('sheetId') as string; - const credentials = this.getCredentials('googleApi'); - - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - - const googleCredentials = { - email: credentials.email, - privateKey: credentials.privateKey, - } as IGoogleAuthCredentials; - - const sheet = new GoogleSheet(spreadsheetId, googleCredentials); + const sheet = new GoogleSheet(spreadsheetId, this); const responseData = await sheet.spreadsheetGetSheets(); if (responseData === undefined) { @@ -579,18 +603,8 @@ export class GoogleSheets implements INodeType { async execute(this: IExecuteFunctions): Promise { const spreadsheetId = this.getNodeParameter('sheetId', 0) as string; - const credentials = this.getCredentials('googleApi'); - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - - const googleCredentials = { - email: credentials.email, - privateKey: credentials.privateKey, - } as IGoogleAuthCredentials; - - const sheet = new GoogleSheet(spreadsheetId, googleCredentials); + const sheet = new GoogleSheet(spreadsheetId, this); const operation = this.getNodeParameter('operation', 0) as string; @@ -638,7 +652,7 @@ export class GoogleSheets implements INodeType { // delete // ---------------------------------- - const requests: sheets_v4.Schema$Request[] = []; + const requests: IDataObject[] = []; const toDelete = this.getNodeParameter('toDelete', 0) as IToDelete; diff --git a/packages/nodes-base/nodes/Google/googlesheets.png b/packages/nodes-base/nodes/Google/Sheet/googlesheets.png similarity index 100% rename from packages/nodes-base/nodes/Google/googlesheets.png rename to packages/nodes-base/nodes/Google/Sheet/googlesheets.png diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index b4d8032b77..57fe569d22 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -110,7 +110,7 @@ export class Slack implements INodeType { displayOptions: { show: { authentication: [ - 'oauth2', + 'oAuth2', ], }, }, @@ -128,7 +128,7 @@ export class Slack implements INodeType { }, { name: 'OAuth2', - value: 'oauth2', + value: 'oAuth2', }, ], default: 'accessToken', diff --git a/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts b/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts index df16d49fd3..9d58c935b7 100644 --- a/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts +++ b/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts @@ -40,7 +40,7 @@ export class TrelloTrigger implements INodeType { webhooks: [ { name: 'setup', - httpMethod: 'GET', + httpMethod: 'HEAD', responseMode: 'onReceived', path: 'webhook', }, diff --git a/packages/nodes-base/nodes/Zulip/StreamDescription.ts b/packages/nodes-base/nodes/Zulip/StreamDescription.ts index a3f45fc02e..27c9c10443 100644 --- a/packages/nodes-base/nodes/Zulip/StreamDescription.ts +++ b/packages/nodes-base/nodes/Zulip/StreamDescription.ts @@ -33,11 +33,11 @@ export const streamOperations = [ value: 'getSubscribed', description: 'Get subscribed streams.', }, - // { - // name: 'Update', - // value: 'update', - // description: 'Update a stream.', - // }, + { + name: 'Update', + value: 'update', + description: 'Update a stream.', + }, ], default: 'create', description: 'The operation to perform.', diff --git a/packages/nodes-base/nodes/Zulip/UserDescription.ts b/packages/nodes-base/nodes/Zulip/UserDescription.ts index 5a30b9c0ae..8ca33bad14 100644 --- a/packages/nodes-base/nodes/Zulip/UserDescription.ts +++ b/packages/nodes-base/nodes/Zulip/UserDescription.ts @@ -33,11 +33,11 @@ export const userOperations = [ value: 'getAll', description: 'Get all users.', }, - // { - // name: 'Update', - // value: 'update', - // description: 'Update a user.', - // }, + { + name: 'Update', + value: 'update', + description: 'Update a user.', + }, ], default: 'create', description: 'The operation to perform.', diff --git a/packages/nodes-base/nodes/Zulip/Zulip.node.ts b/packages/nodes-base/nodes/Zulip/Zulip.node.ts index 144452864f..3c6261a373 100644 --- a/packages/nodes-base/nodes/Zulip/Zulip.node.ts +++ b/packages/nodes-base/nodes/Zulip/Zulip.node.ts @@ -263,16 +263,16 @@ export class Zulip implements INodeType { if (operation === 'create') { const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean; + const subscriptions = this.getNodeParameter('subscriptions', i) as IDataObject; + + body.subscriptions = JSON.stringify(subscriptions.properties); if (jsonParameters) { const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string; if (additionalFieldsJson !== '') { - if (validateJSON(additionalFieldsJson) !== undefined) { - Object.assign(body, JSON.parse(additionalFieldsJson)); - } else { throw new Error('Additional fields must be a valid JSON'); } @@ -342,10 +342,10 @@ export class Zulip implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (additionalFields.description) { - body.description = additionalFields.description as string; + body.description = JSON.stringify(additionalFields.description as string); } if (additionalFields.newName) { - body.new_name = additionalFields.newName as string; + body.new_name = JSON.stringify(additionalFields.newName as string); } if (additionalFields.isPrivate) { body.is_private = additionalFields.isPrivate as boolean; @@ -412,7 +412,7 @@ export class Zulip implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (additionalFields.fullName) { - body.full_name = additionalFields.fullName as string; + body.full_name = JSON.stringify(additionalFields.fullName as string); } if (additionalFields.isAdmin) { body.is_admin = additionalFields.isAdmin as boolean; diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 43782e74a2..31cb4a8da5 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -57,7 +57,9 @@ "dist/credentials/GithubOAuth2Api.credentials.js", "dist/credentials/GitlabApi.credentials.js", "dist/credentials/GoogleApi.credentials.js", + "dist/credentials/GoogleCalendarOAuth2Api.credentials.js", "dist/credentials/GoogleOAuth2Api.credentials.js", + "dist/credentials/GoogleSheetsOAuth2Api.credentials.js", "dist/credentials/GumroadApi.credentials.js", "dist/credentials/HarvestApi.credentials.js", "dist/credentials/HelpScoutOAuth2Api.credentials.js", @@ -182,9 +184,9 @@ "dist/nodes/Github/GithubTrigger.node.js", "dist/nodes/Gitlab/Gitlab.node.js", "dist/nodes/Gitlab/GitlabTrigger.node.js", - "dist/nodes/Google/GoogleCalendar.node.js", - "dist/nodes/Google/GoogleDrive.node.js", - "dist/nodes/Google/GoogleSheets.node.js", + "dist/nodes/Google/Calendar/GoogleCalendar.node.js", + "dist/nodes/Google/Drive/GoogleDrive.node.js", + "dist/nodes/Google/Sheet/GoogleSheets.node.js", "dist/nodes/GraphQL/GraphQL.node.js", "dist/nodes/Gumroad/GumroadTrigger.node.js", "dist/nodes/Harvest/Harvest.node.js", @@ -319,6 +321,7 @@ "gm": "^1.23.1", "googleapis": "~50.0.0", "imap-simple": "^4.3.0", + "jsonwebtoken": "^8.5.1", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", "lodash.unset": "^4.5.2",