From a380a9a3941e9059668aaf478555fee045827b1a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 4 May 2020 08:56:01 +0200 Subject: [PATCH 01/71] :zap: Add first basic code for external hooks --- packages/cli/commands/start.ts | 7 ++ packages/cli/config/index.ts | 7 ++ packages/cli/src/ExternalHooks.ts | 89 +++++++++++++++++++ packages/cli/src/Interfaces.ts | 5 ++ packages/cli/src/Server.ts | 7 ++ .../cli/src/externalHooksTemp/test-hooks.ts | 10 +++ packages/cli/src/index.ts | 1 + 7 files changed, 126 insertions(+) create mode 100644 packages/cli/src/ExternalHooks.ts create mode 100644 packages/cli/src/externalHooksTemp/test-hooks.ts diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts index 4e2efaacfd..65edec601d 100644 --- a/packages/cli/commands/start.ts +++ b/packages/cli/commands/start.ts @@ -12,6 +12,7 @@ import { ActiveWorkflowRunner, CredentialTypes, Db, + ExternalHooks, GenericHelpers, LoadNodesAndCredentials, NodeTypes, @@ -108,6 +109,12 @@ export class Start extends Command { const loadNodesAndCredentials = LoadNodesAndCredentials(); await loadNodesAndCredentials.init(); + // Load all external hooks + const externalHooks = ExternalHooks(); + await externalHooks.init(); + + // await externalHooks.run('credentials.new'); + // Add the found types to an instance other parts of the application can use const nodeTypes = NodeTypes(); await nodeTypes.init(loadNodesAndCredentials.nodeTypes); diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 69b3154064..5705a5fc7d 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -252,6 +252,13 @@ const config = convict({ }, }, + externalHookFiles: { + doc: 'Files containing external hooks', + format: String, + default: '', + env: 'EXTERNAL_HOOK_FILES' + }, + nodes: { exclude: { doc: 'Nodes not to load', diff --git a/packages/cli/src/ExternalHooks.ts b/packages/cli/src/ExternalHooks.ts new file mode 100644 index 0000000000..2c00a525c6 --- /dev/null +++ b/packages/cli/src/ExternalHooks.ts @@ -0,0 +1,89 @@ +import { + Db, + IDatabaseCollections, + IExternalHooks, +} from "./"; + +import * as config from '../config'; +// import { +// access as fsAccess, +// readdir as fsReaddir, +// readFile as fsReadFile, +// stat as fsStat, +// } from 'fs'; + +// TODO: Give different name +interface IHookData { + DbCollections: IDatabaseCollections; +} + +// export EXTERNAL_HOOK_FILES=/data/packages/cli/dist/src/externalHooksTemp/test-hooks.js + +class ExternalHooksClass implements IExternalHooks { + + externalHooks: { + [key: string]: Array<() => {}> + } = {}; + + + async init(): Promise { + console.log('ExternalHooks.init'); + + const externalHookFiles = config.get('externalHookFiles').split(','); + + console.log('externalHookFiles'); + console.log(externalHookFiles); + + for (let hookFilePath of externalHookFiles) { + hookFilePath = hookFilePath.trim(); + if (hookFilePath !== '') { + console.log(' --- load: ' + hookFilePath); + const hookFile = require(hookFilePath); + + for (const resource of Object.keys(hookFile)) { + // if (this.externalHooks[resource] === undefined) { + // this.externalHooks[resource] = {}; + // } + + for (const operation of Object.keys(hookFile[resource])) { + const hookString = `${resource}.${operation}`; + if (this.externalHooks[hookString] === undefined) { + this.externalHooks[hookString] = []; + } + + this.externalHooks[hookString].push.apply(this.externalHooks[hookString], hookFile[resource][operation]); + } + } + } + } + } + + async run(hookName: string): Promise { + console.log('RUN NOW: ' + hookName); + + const hookData: IHookData = { + DbCollections: Db.collections, + }; + + if (this.externalHooks[hookName] === undefined) { + return; + } + + for(const externalHookFunction of this.externalHooks[hookName]) { + externalHookFunction.call(hookData); + } + } + +} + + + +let externalHooksInstance: ExternalHooksClass | undefined; + +export function ExternalHooks(): ExternalHooksClass { + if (externalHooksInstance === undefined) { + externalHooksInstance = new ExternalHooksClass(); + } + + return externalHooksInstance; +} diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index 225e02885f..b54e1eb299 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -188,6 +188,11 @@ export interface IExecutingWorkflowData { workflowExecution?: PCancelable; } +export interface IExternalHooks { + init(): Promise; + run(hookName: string): Promise; +} + export interface IN8nConfig { database: IN8nConfigDatabase; endpoints: IN8nConfigEndpoints; diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index ad2a3c1de5..baf4f1aa58 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -19,6 +19,7 @@ import { ActiveWorkflowRunner, CredentialTypes, Db, + ExternalHooks, IActivationError, ICustomRequest, ICredentialsDb, @@ -33,6 +34,7 @@ import { IExecutionsListResponse, IExecutionsStopData, IExecutionsSummary, + IExternalHooks, IN8nUISettings, IPackageVersions, IWorkflowBase, @@ -93,6 +95,7 @@ class App { testWebhooks: TestWebhooks.TestWebhooks; endpointWebhook: string; endpointWebhookTest: string; + externalHooks: IExternalHooks; saveDataErrorExecution: string; saveDataSuccessExecution: string; saveManualExecutions: boolean; @@ -124,6 +127,8 @@ class App { this.protocol = config.get('protocol'); this.sslKey = config.get('ssl_key'); this.sslCert = config.get('ssl_cert'); + + this.externalHooks = ExternalHooks(); } @@ -689,6 +694,8 @@ class App { throw new ResponseHelper.ResponseError(`Credentials with the same type and name exist already.`, undefined, 400); } + await this.externalHooks.run('credentials.new'); + // Encrypt the data const credentials = new Credentials(incomingData.name, incomingData.type, incomingData.nodesAccess); credentials.setData(incomingData.data, encryptionKey); diff --git a/packages/cli/src/externalHooksTemp/test-hooks.ts b/packages/cli/src/externalHooksTemp/test-hooks.ts new file mode 100644 index 0000000000..2cb60decfc --- /dev/null +++ b/packages/cli/src/externalHooksTemp/test-hooks.ts @@ -0,0 +1,10 @@ +export = { + credentials: { + new: [ + () => { + // Here any additional code can run or the creation blocked + throw new Error('No additional credentials can be created.'); + }, + ], + }, +}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 0b7ae6ad0a..fba554c04e 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,4 +1,5 @@ export * from './CredentialTypes'; +export * from './ExternalHooks'; export * from './Interfaces'; export * from './LoadNodesAndCredentials'; export * from './NodeTypes'; From 0387671cae3084235619a20b76395f144df36f88 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 5 May 2020 01:23:54 +0200 Subject: [PATCH 02/71] :zap: Add additional external hooks and provide additional data --- packages/cli/config/index.ts | 2 +- packages/cli/src/ExternalHooks.ts | 48 ++++++++----------- packages/cli/src/Interfaces.ts | 6 ++- packages/cli/src/Server.ts | 18 +++++-- .../cli/src/externalHooksTemp/test-hooks.ts | 42 ++++++++++++++-- 5 files changed, 79 insertions(+), 37 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 5705a5fc7d..7ef5155a67 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -253,7 +253,7 @@ const config = convict({ }, externalHookFiles: { - doc: 'Files containing external hooks', + doc: 'Files containing external hooks. Multiple files can be separated by colon (":")', format: String, default: '', env: 'EXTERNAL_HOOK_FILES' diff --git a/packages/cli/src/ExternalHooks.ts b/packages/cli/src/ExternalHooks.ts index 2c00a525c6..c4f12301b4 100644 --- a/packages/cli/src/ExternalHooks.ts +++ b/packages/cli/src/ExternalHooks.ts @@ -1,21 +1,10 @@ import { Db, - IDatabaseCollections, + IExternalHookFunctions, IExternalHooks, -} from "./"; +} from './'; import * as config from '../config'; -// import { -// access as fsAccess, -// readdir as fsReaddir, -// readFile as fsReadFile, -// stat as fsStat, -// } from 'fs'; - -// TODO: Give different name -interface IHookData { - DbCollections: IDatabaseCollections; -} // export EXTERNAL_HOOK_FILES=/data/packages/cli/dist/src/externalHooksTemp/test-hooks.js @@ -29,39 +18,42 @@ class ExternalHooksClass implements IExternalHooks { async init(): Promise { console.log('ExternalHooks.init'); - const externalHookFiles = config.get('externalHookFiles').split(','); + const externalHookFiles = config.get('externalHookFiles').split(':'); console.log('externalHookFiles'); console.log(externalHookFiles); + // Load all the provided hook-files for (let hookFilePath of externalHookFiles) { hookFilePath = hookFilePath.trim(); if (hookFilePath !== '') { console.log(' --- load: ' + hookFilePath); - const hookFile = require(hookFilePath); + try { + const hookFile = require(hookFilePath); - for (const resource of Object.keys(hookFile)) { - // if (this.externalHooks[resource] === undefined) { - // this.externalHooks[resource] = {}; - // } + for (const resource of Object.keys(hookFile)) { + for (const operation of Object.keys(hookFile[resource])) { + // Save all the hook functions directly under their string + // format in an array + const hookString = `${resource}.${operation}`; + if (this.externalHooks[hookString] === undefined) { + this.externalHooks[hookString] = []; + } - for (const operation of Object.keys(hookFile[resource])) { - const hookString = `${resource}.${operation}`; - if (this.externalHooks[hookString] === undefined) { - this.externalHooks[hookString] = []; + this.externalHooks[hookString].push.apply(this.externalHooks[hookString], hookFile[resource][operation]); } - - this.externalHooks[hookString].push.apply(this.externalHooks[hookString], hookFile[resource][operation]); } + } catch (error) { + throw new Error(`Problem loading external hook file "${hookFilePath}": ${error.message}`); } } } } - async run(hookName: string): Promise { + async run(hookName: string, hookParameters?: any[]): Promise { // tslint:disable-line:no-any console.log('RUN NOW: ' + hookName); - const hookData: IHookData = { + const externalHookFunctions: IExternalHookFunctions = { DbCollections: Db.collections, }; @@ -70,7 +62,7 @@ class ExternalHooksClass implements IExternalHooks { } for(const externalHookFunction of this.externalHooks[hookName]) { - externalHookFunction.call(hookData); + await externalHookFunction.apply(externalHookFunctions, hookParameters); } } diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index b54e1eb299..0b9b9eca69 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -188,9 +188,13 @@ export interface IExecutingWorkflowData { workflowExecution?: PCancelable; } +export interface IExternalHookFunctions { + DbCollections: IDatabaseCollections; +} + export interface IExternalHooks { init(): Promise; - run(hookName: string): Promise; + run(hookName: string, hookParameters?: any[]): Promise; // tslint:disable-line:no-any } export interface IN8nConfig { diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index baf4f1aa58..a592c642c2 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -346,7 +346,7 @@ class App { // Creates a new workflow this.app.post('/rest/workflows', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { - const newWorkflowData = req.body; + const newWorkflowData = req.body as IWorkflowBase; newWorkflowData.name = newWorkflowData.name.trim(); newWorkflowData.createdAt = this.getCurrentDate(); @@ -354,6 +354,8 @@ class App { newWorkflowData.id = undefined; + await this.externalHooks.run('workflow.create', [newWorkflowData]); + // Save the workflow in DB const result = await Db.collections.Workflow!.save(newWorkflowData); @@ -429,9 +431,11 @@ class App { // Updates an existing workflow this.app.patch('/rest/workflows/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { - const newWorkflowData = req.body; + const newWorkflowData = req.body as IWorkflowBase; const id = req.params.id; + await this.externalHooks.run('workflow.update', [newWorkflowData]); + if (this.activeWorkflowRunner.isActive(id)) { // When workflow gets saved always remove it as the triggers could have been // changed and so the changes would not take effect @@ -497,6 +501,8 @@ class App { this.app.delete('/rest/workflows/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { const id = req.params.id; + await this.externalHooks.run('workflow.delete', [id]); + if (this.activeWorkflowRunner.isActive(id)) { // Before deleting a workflow deactivate it await this.activeWorkflowRunner.remove(id); @@ -658,6 +664,8 @@ class App { this.app.delete('/rest/credentials/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { const id = req.params.id; + await this.externalHooks.run('credentials.delete', [id]); + await Db.collections.Credentials!.delete({ id }); return true; @@ -672,6 +680,8 @@ class App { nodeAccess.date = this.getCurrentDate(); } + await this.externalHooks.run('credentials.create'); + const encryptionKey = await UserSettings.getEncryptionKey(); if (encryptionKey === undefined) { throw new Error('No encryption key got found to encrypt the credentials!'); @@ -694,8 +704,6 @@ class App { throw new ResponseHelper.ResponseError(`Credentials with the same type and name exist already.`, undefined, 400); } - await this.externalHooks.run('credentials.new'); - // Encrypt the data const credentials = new Credentials(incomingData.name, incomingData.type, incomingData.nodesAccess); credentials.setData(incomingData.data, encryptionKey); @@ -722,6 +730,8 @@ class App { const id = req.params.id; + await this.externalHooks.run('credentials.update', [id]); + if (incomingData.name === '') { throw new Error('Credentials have to have a name set!'); } diff --git a/packages/cli/src/externalHooksTemp/test-hooks.ts b/packages/cli/src/externalHooksTemp/test-hooks.ts index 2cb60decfc..f10000f1f0 100644 --- a/packages/cli/src/externalHooksTemp/test-hooks.ts +++ b/packages/cli/src/externalHooksTemp/test-hooks.ts @@ -1,10 +1,46 @@ +import { + IExternalHookFunctions, + IWorkflowBase, +} from '../'; + +// TODO: Move that to interfaces +interface IExternalHooks { + credentials?: { + create?: Array<{ (this: IExternalHookFunctions): Promise; }> + delete?: Array<{ (this: IExternalHookFunctions, credentialId: string): Promise; }> + update?: Array<{ (this: IExternalHookFunctions, credentialId: string): Promise; }> + }; + workflow?: { + create?: Array<{ (this: IExternalHookFunctions, workflowData: IWorkflowBase): Promise; }> + delete?: Array<{ (this: IExternalHookFunctions, workflowId: string): Promise; }> + update?: Array<{ (this: IExternalHookFunctions, workflowData: IWorkflowBase): Promise; }> + }; +} + export = { credentials: { - new: [ - () => { + create: [ + async function (this: IExternalHookFunctions) { + // console.log(this.DbCollections.Workflow); + // Here any additional code can run or the creation blocked throw new Error('No additional credentials can be created.'); }, ], }, -}; + workflow: { + update: [ + async function (this: IExternalHookFunctions, workflowData: IWorkflowBase) { + console.log('update workflow hook'); + + // const responseData = await this.DbCollections.Workflow!.findOne(workflowData.id); + // console.log('workflowData'); + // console.log(responseData); + // console.log(workflowData); + + // Here any additional code can run or the creation blocked + throw new Error('Workflow can not be updated.'); + }, + ], + }, +} as IExternalHooks; From 6e1254fd54a43e6a2126b1192336ad0ccb97db69 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 6 May 2020 00:59:58 +0200 Subject: [PATCH 03/71] :zap: Add additional external workflow hooks --- packages/cli/src/ExternalHooks.ts | 17 +++++--- packages/cli/src/Interfaces.ts | 21 ++++++++-- packages/cli/src/Server.ts | 11 ++--- .../cli/src/WorkflowExecuteAdditionalData.ts | 5 +++ packages/cli/src/WorkflowRunner.ts | 4 ++ .../cli/src/externalHooksTemp/test-hooks.ts | 41 +++++++++---------- 6 files changed, 65 insertions(+), 34 deletions(-) diff --git a/packages/cli/src/ExternalHooks.ts b/packages/cli/src/ExternalHooks.ts index c4f12301b4..9d195ccbad 100644 --- a/packages/cli/src/ExternalHooks.ts +++ b/packages/cli/src/ExternalHooks.ts @@ -1,23 +1,28 @@ import { Db, - IExternalHookFunctions, - IExternalHooks, + IExternalHooksFunctions, + IExternalHooksClass, } from './'; import * as config from '../config'; // export EXTERNAL_HOOK_FILES=/data/packages/cli/dist/src/externalHooksTemp/test-hooks.js -class ExternalHooksClass implements IExternalHooks { +class ExternalHooksClass implements IExternalHooksClass { externalHooks: { [key: string]: Array<() => {}> } = {}; + initDidRun = false; async init(): Promise { console.log('ExternalHooks.init'); + if (this.initDidRun === true) { + return; + } + const externalHookFiles = config.get('externalHookFiles').split(':'); console.log('externalHookFiles'); @@ -48,13 +53,15 @@ class ExternalHooksClass implements IExternalHooks { } } } + + this.initDidRun = true; } async run(hookName: string, hookParameters?: any[]): Promise { // tslint:disable-line:no-any console.log('RUN NOW: ' + hookName); - const externalHookFunctions: IExternalHookFunctions = { - DbCollections: Db.collections, + const externalHookFunctions: IExternalHooksFunctions = { + dbCollections: Db.collections, }; if (this.externalHooks[hookName] === undefined) { diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index 0b9b9eca69..5e76f2c4e2 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -188,11 +188,26 @@ export interface IExecutingWorkflowData { workflowExecution?: PCancelable; } -export interface IExternalHookFunctions { - DbCollections: IDatabaseCollections; +export interface IExternalHooks { + credentials?: { + create?: Array<{ (this: IExternalHooksFunctions, credentialsData: ICredentialsEncrypted): Promise; }> + delete?: Array<{ (this: IExternalHooksFunctions, credentialId: string): Promise; }> + update?: Array<{ (this: IExternalHooksFunctions, credentialsData: ICredentialsDb): Promise; }> + }; + workflow?: { + activate?: Array<{ (this: IExternalHooksFunctions, workflowData: IWorkflowDb): Promise; }> + create?: Array<{ (this: IExternalHooksFunctions, workflowData: IWorkflowBase): Promise; }> + delete?: Array<{ (this: IExternalHooksFunctions, workflowId: string): Promise; }> + execute?: Array<{ (this: IExternalHooksFunctions, workflowData: IWorkflowDb, mode: WorkflowExecuteMode): Promise; }> + update?: Array<{ (this: IExternalHooksFunctions, workflowData: IWorkflowDb): Promise; }> + }; } -export interface IExternalHooks { +export interface IExternalHooksFunctions { + dbCollections: IDatabaseCollections; +} + +export interface IExternalHooksClass { init(): Promise; run(hookName: string, hookParameters?: any[]): Promise; // tslint:disable-line:no-any } diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index a592c642c2..a36a2fd81e 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -48,7 +48,6 @@ import { WorkflowCredentials, WebhookHelpers, WorkflowExecuteAdditionalData, - WorkflowHelpers, WorkflowRunner, GenericHelpers, } from './'; @@ -477,6 +476,8 @@ class App { if (responseData.active === true) { // When the workflow is supposed to be active add it again try { + await this.externalHooks.run('workflow.activate', [responseData]); + await this.activeWorkflowRunner.add(id); } catch (error) { // If workflow could not be activated set it again to inactive @@ -680,8 +681,6 @@ class App { nodeAccess.date = this.getCurrentDate(); } - await this.externalHooks.run('credentials.create'); - const encryptionKey = await UserSettings.getEncryptionKey(); if (encryptionKey === undefined) { throw new Error('No encryption key got found to encrypt the credentials!'); @@ -709,6 +708,8 @@ class App { credentials.setData(incomingData.data, encryptionKey); const newCredentialsData = credentials.getDataToSave() as ICredentialsDb; + await this.externalHooks.run('credentials.create', [newCredentialsData]); + // Add special database related data newCredentialsData.createdAt = this.getCurrentDate(); newCredentialsData.updatedAt = this.getCurrentDate(); @@ -730,8 +731,6 @@ class App { const id = req.params.id; - await this.externalHooks.run('credentials.update', [id]); - if (incomingData.name === '') { throw new Error('Credentials have to have a name set!'); } @@ -770,6 +769,8 @@ class App { // Add special database related data newCredentialsData.updatedAt = this.getCurrentDate(); + await this.externalHooks.run('credentials.update', [newCredentialsData]); + // Update the credentials in DB await Db.collections.Credentials!.update(id, newCredentialsData); diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 9665080ebd..4b86357852 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -1,5 +1,6 @@ import { Db, + ExternalHooks, IExecutionDb, IExecutionFlattedDb, IPushDataExecutionFinished, @@ -302,6 +303,10 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi workflowData = workflowInfo.code; } + const externalHooks = ExternalHooks(); + await externalHooks.init(); + await externalHooks.run('workflow.execute', [workflowData, mode]); + const nodeTypes = NodeTypes(); const workflowName = workflowData ? workflowData.name : undefined; diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index 3a09606550..c3ce61cedc 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -1,5 +1,6 @@ import { ActiveExecutions, + ExternalHooks, IProcessMessageDataHook, ITransferNodeTypes, IWorkflowExecutionDataProcess, @@ -94,6 +95,9 @@ export class WorkflowRunner { * @memberof WorkflowRunner */ async run(data: IWorkflowExecutionDataProcess, loadStaticData?: boolean): Promise { + const externalHooks = ExternalHooks(); + await externalHooks.run('workflow.execute', [data.workflowData, data.executionMode]); + const executionsProcess = config.get('executions.process') as string; if (executionsProcess === 'main') { return this.runMainProcess(data, loadStaticData); diff --git a/packages/cli/src/externalHooksTemp/test-hooks.ts b/packages/cli/src/externalHooksTemp/test-hooks.ts index f10000f1f0..b49c0600b2 100644 --- a/packages/cli/src/externalHooksTemp/test-hooks.ts +++ b/packages/cli/src/externalHooksTemp/test-hooks.ts @@ -1,45 +1,44 @@ import { - IExternalHookFunctions, + WorkflowExecuteMode, +} from 'n8n-workflow'; + +import { + IExternalHooks, + IExternalHooksFunctions, IWorkflowBase, + IWorkflowDb, } from '../'; -// TODO: Move that to interfaces -interface IExternalHooks { - credentials?: { - create?: Array<{ (this: IExternalHookFunctions): Promise; }> - delete?: Array<{ (this: IExternalHookFunctions, credentialId: string): Promise; }> - update?: Array<{ (this: IExternalHookFunctions, credentialId: string): Promise; }> - }; - workflow?: { - create?: Array<{ (this: IExternalHookFunctions, workflowData: IWorkflowBase): Promise; }> - delete?: Array<{ (this: IExternalHookFunctions, workflowId: string): Promise; }> - update?: Array<{ (this: IExternalHookFunctions, workflowData: IWorkflowBase): Promise; }> - }; -} export = { credentials: { create: [ - async function (this: IExternalHookFunctions) { - // console.log(this.DbCollections.Workflow); - + async function (this: IExternalHooksFunctions) { // Here any additional code can run or the creation blocked - throw new Error('No additional credentials can be created.'); + // throw new Error('No additional credentials can be created.'); }, ], }, workflow: { + execute: [ + async function (this: IExternalHooksFunctions, workflowData: IWorkflowDb, mode: WorkflowExecuteMode) { + console.log('execute: ' + mode); + // if (mode === 'integrated') { + // throw new Error('Workflow can not be executed.'); + // } + } + ], update: [ - async function (this: IExternalHookFunctions, workflowData: IWorkflowBase) { + async function (this: IExternalHooksFunctions, workflowData: IWorkflowBase) { console.log('update workflow hook'); - // const responseData = await this.DbCollections.Workflow!.findOne(workflowData.id); + // const responseData = await this.dbCollections.Workflow!.findOne(workflowData.id); // console.log('workflowData'); // console.log(responseData); // console.log(workflowData); // Here any additional code can run or the creation blocked - throw new Error('Workflow can not be updated.'); + // throw new Error('Workflow can not be updated.'); }, ], }, From ffeb2f8b6241933f5829f9160189e92d79d0441e Mon Sep 17 00:00:00 2001 From: ALEXANDER MA COTE Date: Tue, 2 Jun 2020 17:24:01 -0400 Subject: [PATCH 04/71] :zap: Fix Switch Node description (#611) --- packages/nodes-base/nodes/Switch.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Switch.node.ts b/packages/nodes-base/nodes/Switch.node.ts index 3702a7c598..80b3845bca 100644 --- a/packages/nodes-base/nodes/Switch.node.ts +++ b/packages/nodes-base/nodes/Switch.node.ts @@ -32,7 +32,7 @@ export class Switch implements INodeType { { name: 'Expression', value: 'expression', - description: 'Expression decides how to route date.', + description: 'Expression decides how to route data.', }, { name: 'Rules', From a1da8425e41a5bffcb88e89028575036843ef557 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 3 Jun 2020 15:25:43 +0200 Subject: [PATCH 05/71] :bug: Fix operationName bug in GraphQL-Node --- packages/nodes-base/nodes/GraphQL/GraphQL.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts b/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts index ac4b1f80e3..e9ba60b6dd 100644 --- a/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts +++ b/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts @@ -246,7 +246,7 @@ export class GraphQL implements INodeType { } } if (requestOptions.body.operationName === '') { - requestOptions.body.operation = null; + requestOptions.body.operationName = null; } requestOptions.json = true; } else { From e19cd9c11818335d2da787f597bab6c16d789657 Mon Sep 17 00:00:00 2001 From: Tei1988 Date: Thu, 4 Jun 2020 02:40:39 +0900 Subject: [PATCH 06/71] :sparkles: Add support for scoped npm packages (#612) --- packages/cli/src/LoadNodesAndCredentials.ts | 34 ++++++++++++--------- packages/cli/src/Server.ts | 4 +-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/LoadNodesAndCredentials.ts b/packages/cli/src/LoadNodesAndCredentials.ts index 0d0eb21df3..f66fdd5e95 100644 --- a/packages/cli/src/LoadNodesAndCredentials.ts +++ b/packages/cli/src/LoadNodesAndCredentials.ts @@ -97,24 +97,28 @@ class LoadNodesAndCredentialsClass { * @memberof LoadNodesAndCredentialsClass */ async getN8nNodePackages(): Promise { - const packages: string[] = []; - for (const file of await fsReaddirAsync(this.nodeModulesPath)) { - if (file.indexOf('n8n-nodes-') !== 0) { - continue; - } - - // Check if it is really a folder - if (!(await fsStatAsync(path.join(this.nodeModulesPath, file))).isDirectory()) { - continue; - } - - packages.push(file); + const getN8nNodePackagesRecursive = async (relativePath: string): Promise => { + const results: string[] = []; + const nodeModulesPath = `${this.nodeModulesPath}/${relativePath}`; + for (const file of await fsReaddirAsync(nodeModulesPath)) { + const isN8nNodesPackage = file.indexOf('n8n-nodes-') === 0; + const isNpmScopedPackage = file.indexOf('@') === 0; + if (!isN8nNodesPackage && !isNpmScopedPackage) { + continue; + } + if (!(await fsStatAsync(nodeModulesPath)).isDirectory()) { + continue; + } + if (isN8nNodesPackage) { results.push(`${relativePath}${file}`); } + if (isNpmScopedPackage) { + results.push(...await getN8nNodePackagesRecursive(`${relativePath}${file}/`)); + } + } + return results; } - - return packages; + return getN8nNodePackagesRecursive(''); } - /** * Loads credentials from a file * diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index d078c8594e..6d071a85a0 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -597,8 +597,8 @@ class App { // Returns the node icon - this.app.get('/rest/node-icon/:nodeType', async (req: express.Request, res: express.Response): Promise => { - const nodeTypeName = req.params.nodeType; + this.app.get(['/rest/node-icon/:nodeType', '/rest/node-icon/:scope/:nodeType'], async (req: express.Request, res: express.Response): Promise => { + const nodeTypeName = `${req.params.scope ? `${req.params.scope}/` : ''}${req.params.nodeType}`; const nodeTypes = NodeTypes(); const nodeType = nodeTypes.getByName(nodeTypeName); From 35c149c7b1afa5e6b4380663f2d01ad7959747ca Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 3 Jun 2020 19:58:55 +0200 Subject: [PATCH 07/71] :zap: Fix small issue --- packages/cli/src/LoadNodesAndCredentials.ts | 4 ++-- packages/cli/src/Server.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/LoadNodesAndCredentials.ts b/packages/cli/src/LoadNodesAndCredentials.ts index f66fdd5e95..512c689233 100644 --- a/packages/cli/src/LoadNodesAndCredentials.ts +++ b/packages/cli/src/LoadNodesAndCredentials.ts @@ -113,9 +113,9 @@ class LoadNodesAndCredentialsClass { if (isNpmScopedPackage) { results.push(...await getN8nNodePackagesRecursive(`${relativePath}${file}/`)); } - } + } return results; - } + }; return getN8nNodePackagesRecursive(''); } diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 6d071a85a0..02b4afb29e 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -563,7 +563,7 @@ class App { const nodeTypes = NodeTypes(); - const loadDataInstance = new LoadNodeParameterOptions(nodeType, nodeTypes, credentials); + const loadDataInstance = new LoadNodeParameterOptions(nodeType, nodeTypes, credentials!); const workflowData = loadDataInstance.getWorkflowData() as IWorkflowBase; const workflowCredentials = await WorkflowCredentials(workflowData.nodes); From 49ea7e27390a1f96ee4ffddabfeb070cd7008692 Mon Sep 17 00:00:00 2001 From: Patrick Wellever <239789+pwellever@users.noreply.github.com> Date: Thu, 4 Jun 2020 03:36:10 -0400 Subject: [PATCH 08/71] :sparkles: Make server listen address configurable (#618) --- docs/configuration.md | 3 +++ docs/server-setup.md | 1 + packages/cli/config/index.ts | 6 ++++++ packages/cli/src/Server.ts | 5 +++-- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index ce57ef92df..63b8c95f12 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -14,6 +14,9 @@ Sets how n8n should be made available. # The port n8n should be made available on N8N_PORT=5678 +# The IP address n8n should listen on +N8N_LISTEN_ADDRESS=0.0.0.0 + # This ones are currently only important for the webhook URL creation. # So if "WEBHOOK_TUNNEL_URL" got set they do get ignored. It is however # encouraged to set them correctly anyway in case they will become diff --git a/docs/server-setup.md b/docs/server-setup.md index f2c830c48f..d34d076a6f 100644 --- a/docs/server-setup.md +++ b/docs/server-setup.md @@ -105,6 +105,7 @@ services: - N8N_BASIC_AUTH_PASSWORD - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME} - N8N_PORT=5678 + - N8N_LISTEN_ADDRESS=0.0.0.0 - N8N_PROTOCOL=https - NODE_ENV=production - WEBHOOK_TUNNEL_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/ diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 69b3154064..a8b1886277 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -169,6 +169,12 @@ const config = convict({ env: 'N8N_PORT', doc: 'HTTP port n8n can be reached' }, + listen_address: { + format: String, + default: '0.0.0.0', + env: 'N8N_LISTEN_ADDRESS', + doc: 'IP address n8n should listen on' + }, protocol: { format: ['http', 'https'], default: 'http', diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 02b4afb29e..a5beea41e2 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1294,6 +1294,7 @@ class App { export async function start(): Promise { const PORT = config.get('port'); + const ADDRESS = config.get('listen_address'); const app = new App(); @@ -1312,9 +1313,9 @@ export async function start(): Promise { server = http.createServer(app.app); } - server.listen(PORT, async () => { + server.listen(PORT, ADDRESS, async () => { const versions = await GenericHelpers.getVersions(); - console.log(`n8n ready on port ${PORT}`); + console.log(`n8n ready on ${ADDRESS}, port ${PORT}`); console.log(`Version: ${versions.cli}`); }); } From c38385342357d619466b63c2e59944f1ebdcd90e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 16:55:24 +0200 Subject: [PATCH 09/71] :zap: Small improvement to Twitter-Node --- packages/nodes-base/nodes/Twitter/TweetDescription.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Twitter/TweetDescription.ts b/packages/nodes-base/nodes/Twitter/TweetDescription.ts index 46e1760e6f..6434843bea 100644 --- a/packages/nodes-base/nodes/Twitter/TweetDescription.ts +++ b/packages/nodes-base/nodes/Twitter/TweetDescription.ts @@ -85,7 +85,7 @@ export const tweetFields = [ displayName: 'Binary Property', name: 'binaryPropertyName', type: 'string', - default: '', + default: 'data', description: 'Name of the binary properties which contain data which should be added to tweet as attachment', }, { From 1c683fec699b27f35bc00494c3dc97051fd4dd78 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 16:58:45 +0200 Subject: [PATCH 10/71] :bug: Fix issue that ReadPdf does not lose binary data --- packages/nodes-base/nodes/ReadPdf.node.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nodes-base/nodes/ReadPdf.node.ts b/packages/nodes-base/nodes/ReadPdf.node.ts index 52f149da97..2d41583c87 100644 --- a/packages/nodes-base/nodes/ReadPdf.node.ts +++ b/packages/nodes-base/nodes/ReadPdf.node.ts @@ -51,6 +51,7 @@ export class ReadPdf implements INodeType { const binaryData = Buffer.from(item.binary[binaryPropertyName].data, BINARY_ENCODING); return { + binary: item.binary, json: await pdf(binaryData) }; } From 20fdd70a9bdb2ec83f19cb51bbfda163032b048d Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:02:27 +0200 Subject: [PATCH 11/71] :books: Add version to breaking-changes file --- packages/cli/BREAKING-CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index e560133480..0af22307f3 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -3,7 +3,7 @@ This list shows all the versions which include breaking changes and how to upgrade. -## ??? +## 0.68.0 ### What changed? From 93c6107ef96969705a13f5a7c818be9266893e0f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:05:54 +0200 Subject: [PATCH 12/71] :bookmark: Release n8n-workflow@0.32.0 --- packages/workflow/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/workflow/package.json b/packages/workflow/package.json index fb851ce40b..969270199b 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.31.0", + "version": "0.32.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 7e88c35e3597ba5a322540bb333bb83402d1c0f9 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:07:09 +0200 Subject: [PATCH 13/71] :arrow_up: Set n8n-workflow@0.32.0 on n8n-core --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index c0616f5aeb..dd3fa99fc5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -45,7 +45,7 @@ "crypto-js": "3.1.9-1", "lodash.get": "^4.4.2", "mmmagic": "^0.5.2", - "n8n-workflow": "~0.31.0", + "n8n-workflow": "~0.32.0", "p-cancelable": "^2.0.0", "request": "^2.88.2", "request-promise-native": "^1.0.7" From 0abbcba6ca9b27c510c6c8706037281e1209091e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:07:44 +0200 Subject: [PATCH 14/71] :bookmark: Release n8n-core@0.35.0 --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index dd3fa99fc5..259ce7d4cb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.34.0", + "version": "0.35.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From d2d3316efcc7977176d1536a90ff59f87b9ff92b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:08:55 +0200 Subject: [PATCH 15/71] :arrow_up: Set n8n-core@0.35.0 and n8n-workflow@0.32.0 on n8n-nodes-base --- packages/nodes-base/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 31cb4a8da5..e14468d7e4 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -304,7 +304,7 @@ "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^24.9.0", - "n8n-workflow": "~0.31.0", + "n8n-workflow": "~0.32.0", "ts-jest": "^24.0.2", "tslint": "^5.17.0", "typescript": "~3.7.4" @@ -329,7 +329,7 @@ "moment-timezone": "^0.5.28", "mongodb": "^3.5.5", "mysql2": "^2.0.1", - "n8n-core": "~0.34.0", + "n8n-core": "~0.35.0", "nodemailer": "^6.4.6", "pdf-parse": "^1.1.1", "pg-promise": "^9.0.3", From 14cd01557d21378e3a6283ddd4303a6155bf67a6 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:09:21 +0200 Subject: [PATCH 16/71] :bookmark: Release n8n-nodes-base@0.63.0 --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index e14468d7e4..21ac01808e 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.62.1", + "version": "0.63.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 66c3013e56e7e647e302e44d4348f077c0e747e2 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:10:31 +0200 Subject: [PATCH 17/71] :arrow_up: Set n8n-workflow@0.32.0 on n8n-editor-ui --- packages/editor-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index ec505a172c..7e1b0f104c 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -64,7 +64,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.31.0", + "n8n-workflow": "~0.32.0", "node-sass": "^4.12.0", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", From 42d6630466912456f96d11be1f2be32bd1642222 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:11:39 +0200 Subject: [PATCH 18/71] :bookmark: Release n8n-editor-ui@0.46.0 --- packages/editor-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 7e1b0f104c..8543d2fdd0 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.45.0", + "version": "0.46.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 320a63a1be3f7fadeb1bc66063427b55e2ff0220 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:14:23 +0200 Subject: [PATCH 19/71] :arrow_up: Set n8n-core@0.35.0, n8n-editor-ui@0.46.0, n8n-nodes-base@0.63.0 and n8n-workflow@0.32.0 on n8n --- packages/cli/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 0b4e2fa9cb..49d969d01c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -99,10 +99,10 @@ "lodash.get": "^4.4.2", "mongodb": "^3.5.5", "mysql2": "^2.0.1", - "n8n-core": "~0.34.0", - "n8n-editor-ui": "~0.45.0", - "n8n-nodes-base": "~0.62.1", - "n8n-workflow": "~0.31.0", + "n8n-core": "~0.35.0", + "n8n-editor-ui": "~0.46.0", + "n8n-nodes-base": "~0.63.0", + "n8n-workflow": "~0.32.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^7.11.0", From c7a3a8679743c629ab6558dfe9839e1460fa6a80 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:14:57 +0200 Subject: [PATCH 20/71] :bookmark: Release n8n@0.68.0 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 49d969d01c..1f2dbde437 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.67.3", + "version": "0.68.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 75ea0ea91c153b9d2a73b3939f983945953baf96 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:30:43 +0200 Subject: [PATCH 21/71] :bug: Add missing file --- packages/cli/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/package.json b/packages/cli/package.json index 1f2dbde437..537dde163e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -47,6 +47,7 @@ }, "files": [ "bin", + "templates", "dist", "oclif.manifest.json" ], From f8f123efde4dc9f9665d0b15b078b2898ceebd29 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:31:21 +0200 Subject: [PATCH 22/71] :bookmark: Release n8n@0.68.1 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 537dde163e..2d1fee766f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.68.0", + "version": "0.68.1", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 381caa5103c9734bddf52d3fce62ab3f89f6feb1 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:46:43 +0200 Subject: [PATCH 23/71] :bug: Fix typo --- packages/nodes-base/nodes/Keap/Keap.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Keap/Keap.node.ts b/packages/nodes-base/nodes/Keap/Keap.node.ts index f67c07bbc4..3ba98cda7f 100644 --- a/packages/nodes-base/nodes/Keap/Keap.node.ts +++ b/packages/nodes-base/nodes/Keap/Keap.node.ts @@ -104,7 +104,7 @@ import * as moment from 'moment-timezone'; export class Keap implements INodeType { description: INodeTypeDescription = { displayName: 'Keap', - name: ' keap', + name: 'keap', icon: 'file:keap.png', group: ['input'], version: 1, From e0a8bf1bd5bb8b9bb1c1cca89540cd429bf6d546 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:47:19 +0200 Subject: [PATCH 24/71] :bookmark: Release n8n-nodes-base@0.63.1 --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 21ac01808e..a898058634 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.63.0", + "version": "0.63.1", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From a12510db094bce7ecf6d33237ef42e3d0f2d80f0 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:49:37 +0200 Subject: [PATCH 25/71] :arrow_up: Set n8n-nodes-base@0.63.1 on n8n --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 2d1fee766f..0bdd020d45 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -102,7 +102,7 @@ "mysql2": "^2.0.1", "n8n-core": "~0.35.0", "n8n-editor-ui": "~0.46.0", - "n8n-nodes-base": "~0.63.0", + "n8n-nodes-base": "~0.63.1", "n8n-workflow": "~0.32.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", From afe0d16a9a3a65c0e1e3b1d33f79dde091701752 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 4 Jun 2020 17:50:26 +0200 Subject: [PATCH 26/71] :bookmark: Release n8n@0.68.2 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 0bdd020d45..325a73d874 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.68.1", + "version": "0.68.2", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From d3829c90c238d7f5ae64c9dd135d235a69481b3d Mon Sep 17 00:00:00 2001 From: ricardo Date: Sat, 6 Jun 2020 14:57:42 -0400 Subject: [PATCH 27/71] :zap: Google Drive OAuth2 support --- .../GoogleDriveOAuth2Api.credentials.ts | 26 +++ .../nodes/Google/Drive/GenericFunctions.ts | 142 +++++++++++++++ .../nodes/Google/Drive/GoogleDrive.node.ts | 168 ++++++++++-------- packages/nodes-base/nodes/Google/GoogleApi.ts | 23 --- packages/nodes-base/package.json | 2 +- 5 files changed, 259 insertions(+), 102 deletions(-) create mode 100644 packages/nodes-base/credentials/GoogleDriveOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts delete mode 100644 packages/nodes-base/nodes/Google/GoogleApi.ts diff --git a/packages/nodes-base/credentials/GoogleDriveOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleDriveOAuth2Api.credentials.ts new file mode 100644 index 0000000000..77b0a9504c --- /dev/null +++ b/packages/nodes-base/credentials/GoogleDriveOAuth2Api.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.appdata', + 'https://www.googleapis.com/auth/drive.photos.readonly', +]; + +export class GoogleDriveOAuth2Api implements ICredentialType { + name = 'googleDriveOAuth2Api'; + extends = [ + 'googleOAuth2Api', + ]; + displayName = 'Google Drive OAuth2 API'; + properties = [ + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' '), + }, + ]; +} diff --git a/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts new file mode 100644 index 0000000000..9946cd802a --- /dev/null +++ b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts @@ -0,0 +1,142 @@ +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, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const authenticationMethod = this.getNodeParameter('authentication', 0, 'serviceAccount') as string; + + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://www.googleapis.com${resource}`, + json: true, + }; + options = Object.assign({}, options, option); + try { + 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, 'googleDriveOAuth2Api', options); + } + } catch (error) { + if (error.response && error.response.body && error.response.body.error) { + + let errorMessages; + + if (error.response.body.error.errors) { + // Try to return the error prettier + errorMessages = error.response.body.error.errors; + + errorMessages = errorMessages.map((errorItem: IDataObject) => errorItem.message); + + errorMessages = errorMessages.join('|'); + + } else if (error.response.body.error.message){ + errorMessages = error.response.body.error.message; + } + + throw new Error(`Google Drive error response [${error.statusCode}]: ${errorMessages}`); + } + 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.appdata', + 'https://www.googleapis.com/auth/drive.photos.readonly', + ]; + + 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/Drive/GoogleDrive.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts index 387a3d7bc0..8ddfd7c1fa 100644 --- a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts @@ -1,10 +1,8 @@ -import { google } from 'googleapis'; -const { Readable } = require('stream'); - import { BINARY_ENCODING, IExecuteFunctions, } from 'n8n-core'; + import { IDataObject, INodeTypeDescription, @@ -12,8 +10,9 @@ import { INodeType, } from 'n8n-workflow'; -import { getAuthenticationClient } from '../GoogleApi'; - +import { + googleApiRequest, +} from './GenericFunctions'; export class GoogleDrive implements INodeType { description: INodeTypeDescription = { @@ -34,9 +33,43 @@ export class GoogleDrive implements INodeType { { 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', @@ -813,26 +846,6 @@ export class GoogleDrive implements INodeType { const items = this.getInputData(); const returnData: IDataObject[] = []; - const credentials = this.getCredentials('googleApi'); - - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - - const scopes = [ - 'https://www.googleapis.com/auth/drive', - 'https://www.googleapis.com/auth/drive.appdata', - 'https://www.googleapis.com/auth/drive.photos.readonly', - ]; - - const client = await getAuthenticationClient(credentials.email as string, credentials.privateKey as string, scopes); - - const drive = google.drive({ - version: 'v3', - // @ts-ignore - auth: client, - }); - const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; @@ -857,22 +870,20 @@ export class GoogleDrive implements INodeType { const fileId = this.getNodeParameter('fileId', i) as string; - const copyOptions = { - fileId, + const body: IDataObject = { fields: queryFields, - requestBody: {} as IDataObject, }; const optionProperties = ['name', 'parents']; for (const propertyName of optionProperties) { if (options[propertyName] !== undefined) { - copyOptions.requestBody[propertyName] = options[propertyName]; + body[propertyName] = options[propertyName]; } } - const response = await drive.files.copy(copyOptions); + const response = await googleApiRequest.call(this, 'POST', `/drive/v3/files/${fileId}/copy`, body); - returnData.push(response.data as IDataObject); + returnData.push(response as IDataObject); } else if (operation === 'download') { // ---------------------------------- @@ -881,15 +892,13 @@ export class GoogleDrive implements INodeType { const fileId = this.getNodeParameter('fileId', i) as string; - const response = await drive.files.get( - { - fileId, - alt: 'media', - }, - { - responseType: 'arraybuffer', - }, - ); + 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; if (response.headers['content-type']) { @@ -912,7 +921,7 @@ export class GoogleDrive implements INodeType { const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i) as string; - const data = Buffer.from(response.data as string); + const data = Buffer.from(response.body as string); items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(data as unknown as Buffer, undefined, mimeType); @@ -988,20 +997,19 @@ export class GoogleDrive implements INodeType { const pageSize = this.getNodeParameter('limit', i) as number; - const res = await drive.files.list({ + const qs = { pageSize, orderBy: 'modifiedTime', fields: `nextPageToken, files(${queryFields})`, spaces: querySpaces, - corpora: queryCorpora, - driveId, q: queryString, - includeItemsFromAllDrives: (queryCorpora !== '' || driveId !== ''), // Actually depracated, - supportsAllDrives: (queryCorpora !== '' || driveId !== ''), // see https://developers.google.com/drive/api/v3/reference/files/list - // However until June 2020 still needs to be set, to avoid API errors. - }); + includeItemsFromAllDrives: (queryCorpora !== '' || driveId !== ''), + supportsAllDrives: (queryCorpora !== '' || driveId !== ''), + }; - const files = res!.data.files; + const response = await googleApiRequest.call(this, 'GET', `/drive/v3/files`, {}, qs); + + const files = response!.files; return [this.helpers.returnJsonArray(files as IDataObject[])]; @@ -1044,29 +1052,35 @@ export class GoogleDrive implements INodeType { const name = this.getNodeParameter('name', i) as string; const parents = this.getNodeParameter('parents', i) as string[]; - const response = await drive.files.create({ - requestBody: { - name, - originalFilename, - parents, - }, + let qs: IDataObject = { fields: queryFields, - media: { - mimeType, - body: ((buffer: Buffer) => { - const readableInstanceStream = new Readable({ - read() { - this.push(buffer); - this.push(null); - } - }); + uploadType: 'media', + }; - return readableInstanceStream; - })(body), + const requestOptions = { + headers: { + 'Content-Type': mimeType, + 'Content-Length': body.byteLength, }, - }); + encoding: null, + json: false, + }; - returnData.push(response.data as IDataObject); + let response = await googleApiRequest.call(this, 'POST', `/upload/drive/v3/files`, body, qs, undefined, requestOptions); + + body = { + mimeType, + name, + originalFilename, + }; + + qs = { + addParents: parents.join(','), + }; + + response = await googleApiRequest.call(this, 'PATCH', `/drive/v3/files/${JSON.parse(response).id}`, body, qs); + + returnData.push(response as IDataObject); } } else if (resource === 'folder') { @@ -1077,19 +1091,19 @@ export class GoogleDrive implements INodeType { const name = this.getNodeParameter('name', i) as string; - const fileMetadata = { + const body = { name, mimeType: 'application/vnd.google-apps.folder', parents: options.parents || [], }; - const response = await drive.files.create({ - // @ts-ignore - resource: fileMetadata, + const qs = { fields: queryFields, - }); + }; - returnData.push(response.data as IDataObject); + const response = await googleApiRequest.call(this, 'POST', '/drive/v3/files', body, qs); + + returnData.push(response as IDataObject); } } if (['file', 'folder'].includes(resource)) { @@ -1100,9 +1114,7 @@ export class GoogleDrive implements INodeType { const fileId = this.getNodeParameter('fileId', i) as string; - await drive.files.delete({ - fileId, - }); + const response = await googleApiRequest.call(this, 'DELETE', `/drive/v3/files/${fileId}`); // If we are still here it did succeed returnData.push({ diff --git a/packages/nodes-base/nodes/Google/GoogleApi.ts b/packages/nodes-base/nodes/Google/GoogleApi.ts deleted file mode 100644 index 64ba9f9bec..0000000000 --- a/packages/nodes-base/nodes/Google/GoogleApi.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { JWT } from 'google-auth-library'; -import { google } from 'googleapis'; - - -/** - * Returns the authentication client needed to access spreadsheet - */ -export async function getAuthenticationClient(email: string, privateKey: string, scopes: string[]): Promise { - const client = new google.auth.JWT( - email, - undefined, - privateKey, - scopes, - undefined - ); - - // TODO: Check later if this or the above should be cached - await client.authorize(); - - // @ts-ignore - return client; -} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index a898058634..0f85456439 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -58,6 +58,7 @@ "dist/credentials/GitlabApi.credentials.js", "dist/credentials/GoogleApi.credentials.js", "dist/credentials/GoogleCalendarOAuth2Api.credentials.js", + "dist/credentials/GoogleDriveOAuth2Api.credentials.js", "dist/credentials/GoogleOAuth2Api.credentials.js", "dist/credentials/GoogleSheetsOAuth2Api.credentials.js", "dist/credentials/GumroadApi.credentials.js", @@ -319,7 +320,6 @@ "formidable": "^1.2.1", "glob-promise": "^3.4.0", "gm": "^1.23.1", - "googleapis": "~50.0.0", "imap-simple": "^4.3.0", "jsonwebtoken": "^8.5.1", "lodash.get": "^4.4.2", From 5e93b37c6e800856c3d43053fdbe16b9e335cbe4 Mon Sep 17 00:00:00 2001 From: ricardo Date: Sun, 7 Jun 2020 17:29:13 -0400 Subject: [PATCH 28/71] :zap: Improvements --- packages/core/src/NodeExecuteFunctions.ts | 11 +- .../nodes/Github/GenericFunctions.ts | 2 +- packages/nodes-base/nodes/HttpRequest.node.ts | 1 + packages/nodes-base/nodes/OAuth.node.ts | 2 +- .../nodes/Twitter/GenericFunctions.ts | 32 +++ .../nodes/Twitter/TweetDescription.ts | 248 ++++++++++++++++-- .../nodes-base/nodes/Twitter/Twitter.node.ts | 143 +++++++++- packages/nodes-base/package.json | 1 + 8 files changed, 402 insertions(+), 38 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index f0d0d01751..d500d56f88 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -219,12 +219,17 @@ export function requestOAuth1(this: IAllExecuteFunctions, credentialsType: strin //@ts-ignore url: requestOptions.url, method: requestOptions.method, - data: requestOptions.body, + data: { ...requestOptions.qs, ...requestOptions.body }, json: requestOptions.json, }; - //@ts-ignore - newRequestOptions.form = oauth.authorize(newRequestOptions as RequestOptions, token); + if (Object.keys(requestOptions.qs).length !== 0) { + //@ts-ignore + newRequestOptions.qs = oauth.authorize(newRequestOptions as RequestOptions, token); + } else { + //@ts-ignore + newRequestOptions.form = oauth.authorize(newRequestOptions as RequestOptions, token); + } return this.helpers.request!(newRequestOptions) .catch(async (error: IResponseError) => { diff --git a/packages/nodes-base/nodes/Github/GenericFunctions.ts b/packages/nodes-base/nodes/Github/GenericFunctions.ts index 592367019e..de20d34062 100644 --- a/packages/nodes-base/nodes/Github/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Github/GenericFunctions.ts @@ -50,7 +50,7 @@ export async function githubApiRequest(this: IHookFunctions | IExecuteFunctions, const baseUrl = credentials!.server || 'https://api.github.com'; options.uri = `${baseUrl}${endpoint}`; - + //@ts-ignore return await this.helpers.requestOAuth2.call(this, 'githubOAuth2Api', options); } } catch (error) { diff --git a/packages/nodes-base/nodes/HttpRequest.node.ts b/packages/nodes-base/nodes/HttpRequest.node.ts index 499575a728..417d80eeea 100644 --- a/packages/nodes-base/nodes/HttpRequest.node.ts +++ b/packages/nodes-base/nodes/HttpRequest.node.ts @@ -801,6 +801,7 @@ export class HttpRequest implements INodeType { // Now that the options are all set make the actual http request if (oAuth2Api !== undefined) { + //@ts-ignore response = await this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions); } else { response = await this.helpers.request(requestOptions); diff --git a/packages/nodes-base/nodes/OAuth.node.ts b/packages/nodes-base/nodes/OAuth.node.ts index 685b07cc40..3966571cd1 100644 --- a/packages/nodes-base/nodes/OAuth.node.ts +++ b/packages/nodes-base/nodes/OAuth.node.ts @@ -137,7 +137,7 @@ export class OAuth implements INodeType { uri: url, json: true, }; - + //@ts-ignore const responseData = await this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions); return [this.helpers.returnJsonArray(responseData)]; } else { diff --git a/packages/nodes-base/nodes/Twitter/GenericFunctions.ts b/packages/nodes-base/nodes/Twitter/GenericFunctions.ts index d13ea1c548..1b793047c3 100644 --- a/packages/nodes-base/nodes/Twitter/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Twitter/GenericFunctions.ts @@ -42,3 +42,35 @@ export async function twitterApiRequest(this: IExecuteFunctions | IExecuteSingle throw error; } } + +export async function twitterApiRequestAllItems(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.count = 100; + do { + responseData = await twitterApiRequest.call(this, method, endpoint, body, query); + query.since_id = responseData.search_metadata.max_id; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData.search_metadata && + responseData.search_metadata.next_results + ); + + return returnData; +} + +export function chunks (buffer: Buffer, chunkSize: number) { + const result = []; + const len = buffer.length; + let i = 0; + + while (i < len) { + result.push(buffer.slice(i, i += chunkSize)); + } + + return result; +} + + diff --git a/packages/nodes-base/nodes/Twitter/TweetDescription.ts b/packages/nodes-base/nodes/Twitter/TweetDescription.ts index 46e1760e6f..54d3ccbfad 100644 --- a/packages/nodes-base/nodes/Twitter/TweetDescription.ts +++ b/packages/nodes-base/nodes/Twitter/TweetDescription.ts @@ -20,6 +20,11 @@ export const tweetOperations = [ value: 'create', description: 'Create a new tweet', }, + { + name: 'Search', + value: 'search', + description: 'Search tweets', + }, ], default: 'create', description: 'The operation to perform.', @@ -88,31 +93,31 @@ export const tweetFields = [ default: '', description: 'Name of the binary properties which contain data which should be added to tweet as attachment', }, - { - displayName: 'Category', - name: 'category', - type: 'options', - options: [ - { - name: 'Amplify Video', - value: 'amplifyVideo', - }, - { - name: 'Gif', - value: 'tweetGif', - }, - { - name: 'Image', - value: 'tweetImage', - }, - { - name: 'Video', - value: 'tweetVideo', - }, - ], - default: '', - description: 'The category that represents how the media will be used', - }, + // { + // displayName: 'Category', + // name: 'category', + // type: 'options', + // options: [ + // { + // name: 'Amplify Video', + // value: 'amplifyVideo', + // }, + // { + // name: 'Gif', + // value: 'tweetGif', + // }, + // { + // name: 'Image', + // value: 'tweetImage', + // }, + // { + // name: 'Video', + // value: 'tweetVideo', + // }, + // ], + // default: '', + // description: 'The category that represents how the media will be used', + // }, ], }, ], @@ -167,4 +172,197 @@ export const tweetFields = [ }, ] }, +/* -------------------------------------------------------------------------- */ +/* tweet:search */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Search Text', + name: 'searchText', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + required: true, + default: '', + displayOptions: { + show: { + operation: [ + 'search', + ], + resource: [ + 'tweet', + ], + }, + }, + description: `A UTF-8, URL-encoded search query of 500 characters maximum,
+ including operators. Queries may additionally be limited by complexity.
+ Check the searching examples here.`, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'search', + ], + resource: [ + 'tweet', + ], + }, + }, + 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: [ + 'search', + ], + resource: [ + 'tweet', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'search', + ], + resource: [ + 'tweet', + ], + }, + }, + options: [ + { + displayName: 'Include Entities', + name: 'includeEntities', + type: 'boolean', + default: false, + description: 'The entities node will not be included when set to false', + }, + { + displayName: 'Lang', + name: 'lang', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLanguages', + }, + default: '', + description: 'Restricts tweets to the given language, given by an ISO 639-1 code. Language detection is best-effort.', + }, + { + displayName: 'Location', + name: 'locationFieldsUi', + type: 'fixedCollection', + placeholder: 'Add Location', + default: {}, + description: `Subscriber location information.n`, + options: [ + { + name: 'locationFieldsValues', + displayName: 'Location', + values: [ + { + displayName: 'Latitude', + name: 'latitude', + type: 'string', + required: true, + description: 'The location latitude.', + default: '', + }, + { + displayName: 'Longitude', + name: 'longitude', + type: 'string', + required: true, + description: 'The location longitude.', + default: '', + }, + { + displayName: 'Radius', + name: 'radius', + type: 'options', + options: [ + { + name: 'Milles', + value: 'mi', + }, + { + name: 'Kilometers', + value: 'km', + }, + ], + required: true, + description: 'Returns tweets by users located within a given radius of the given latitude/longitude.', + default: '', + }, + { + displayName: 'Distance', + name: 'distance', + type: 'number', + typeOptions: { + minValue: 0, + }, + required: true, + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Result Type', + name: 'resultType', + type: 'options', + options: [ + { + name: 'Mixed', + value: 'mixed', + description: 'Include both popular and real time results in the response.', + }, + { + name: 'Recent', + value: 'recent', + description: 'Return only the most recent results in the response', + }, + { + name: 'Popular', + value: 'popular', + description: 'Return only the most popular results in the response.' + }, + ], + default: 'mixed', + description: 'Specifies what type of search results you would prefer to receive', + }, + { + displayName: 'Until', + name: 'until', + type: 'dateTime', + default: '', + description: 'Returns tweets created before the given date', + }, + ], + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index 09b1dcde93..376e9d25d8 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -1,6 +1,8 @@ import { IExecuteFunctions, + ILoadOptionsFunctions, + BINARY_ENCODING, } from 'n8n-core'; import { @@ -9,6 +11,7 @@ import { INodeExecutionData, INodeType, INodeTypeDescription, + INodePropertyOptions, } from 'n8n-workflow'; import { @@ -17,16 +20,16 @@ import { } from './TweetDescription'; import { + chunks, twitterApiRequest, + twitterApiRequestAllItems, } from './GenericFunctions'; import { ITweet, } from './TweetInterface'; -import { - snakeCase, -} from 'change-case'; +const ISO6391 = require('iso-639-1'); export class Twitter implements INodeType { description: INodeTypeDescription = { @@ -69,6 +72,26 @@ export class Twitter implements INodeType { ], }; + methods = { + loadOptions: { + // Get all the available languages to display them to user so that he can + // select them easily + async getLanguages(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const languages = ISO6391.getAllNames(); + for (const language of languages) { + const languageName = language; + const languageId = ISO6391.getCode(language); + returnData.push({ + name: languageName, + value: languageId, + }); + } + return returnData; + }, + }, + }; + async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; @@ -106,11 +129,74 @@ export class Twitter implements INodeType { continue; } - const attachmentBody = { - media_data: binaryData[binaryPropertyName].data, - media_category: snakeCase(attachment.category as string).toUpperCase(), - }; - const response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + let attachmentBody = {}; + let response: IDataObject = {}; + + if (binaryData[binaryPropertyName].mimeType.includes('image')) { + + const attachmentBody = { + media_data: binaryData[binaryPropertyName].data, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + } else { + + // https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-init + + attachmentBody = { + command: 'INIT', + total_bytes: Buffer.from(binaryData[binaryPropertyName].data, BINARY_ENCODING).byteLength, + media_type: binaryData[binaryPropertyName].mimeType, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + const mediaId = response.media_id_string; + + // break the data on 5mb chunks (max size that can be uploaded at once) + + const binaryParts = chunks(Buffer.from(binaryData[binaryPropertyName].data, BINARY_ENCODING), 5242880); + + let index = 0; + + for (const binaryPart of binaryParts) { + + //https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-append + + attachmentBody = { + name: binaryData[binaryPropertyName].fileName, + command: 'APPEND', + media_id: mediaId, + media_data: Buffer.from(binaryPart).toString('base64'), + segment_index: index, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + index++; + } + + //https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-finalize + + attachmentBody = { + command: 'FINALIZE', + media_id: mediaId, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + // data has not been uploaded yet, so wait for it to be ready + if (response.processing_info) { + const { check_after_secs } = (response.processing_info as IDataObject); + await new Promise((resolve, reject) => { + setTimeout(() => { + resolve(); + }, (check_after_secs as number) * 1000); + }); + } + } + mediaIds.push(response.media_id_string); } } @@ -133,6 +219,47 @@ export class Twitter implements INodeType { responseData = await twitterApiRequest.call(this, 'POST', '/statuses/update.json', body); } + // https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets + if (operation === 'search') { + const q = this.getNodeParameter('searchText', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const qs: IDataObject = { + q + }; + + if (additionalFields.includeEntities) { + qs.include_entities = additionalFields.includeEntities as boolean; + } + + if (additionalFields.resultType) { + qs.response_type = additionalFields.resultType as string; + } + + if (additionalFields.until) { + qs.until = additionalFields.until as string; + } + + if (additionalFields.lang) { + qs.lang = additionalFields.lang as string; + } + + if (additionalFields.locationFieldsUi) { + const locationUi = additionalFields.locationFieldsUi as IDataObject; + if (locationUi.locationFieldsValues) { + const values = locationUi.locationFieldsValues as IDataObject; + qs.geocode = `${values.latitude as string},${values.longitude as string},${values.distance}${values.radius}`; + } + } + + if (returnAll) { + responseData = await twitterApiRequestAllItems.call(this, 'statuses', 'GET', '/search/tweets.json', {}, qs); + } else { + qs.count = this.getNodeParameter('limit', 0) as number; + responseData = await twitterApiRequest.call(this, 'GET', '/search/tweets.json', {}, qs); + responseData = responseData.statuses; + } + } } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 31cb4a8da5..596ba1e499 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -321,6 +321,7 @@ "gm": "^1.23.1", "googleapis": "~50.0.0", "imap-simple": "^4.3.0", + "iso-639-1": "^2.1.3", "jsonwebtoken": "^8.5.1", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", From 9cae58c78798f8d9d2c152e4c5b6e57a20654778 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 00:34:15 +0200 Subject: [PATCH 29/71] :bug: Fix issue that nodes can not be opened in readOnly-Mode --- .../src/components/mixins/nodeBase.ts | 114 +++++++++--------- 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/packages/editor-ui/src/components/mixins/nodeBase.ts b/packages/editor-ui/src/components/mixins/nodeBase.ts index 03ad84db00..ba3661700a 100644 --- a/packages/editor-ui/src/components/mixins/nodeBase.ts +++ b/packages/editor-ui/src/components/mixins/nodeBase.ts @@ -29,12 +29,6 @@ export const nodeBase = mixins(nodeIndex).extend({ isMacOs (): boolean { return /(ipad|iphone|ipod|mac)/i.test(navigator.platform); }, - isReadOnly (): boolean { - if (['NodeViewExisting', 'NodeViewNew'].includes(this.$route.name as string)) { - return false; - } - return true; - }, nodeName (): string { return NODE_NAME_PREFIX + this.nodeIndex; }, @@ -276,63 +270,71 @@ export const nodeBase = mixins(nodeIndex).extend({ this.instance.addEndpoint(this.nodeName, newEndpointData); }); - if (this.isReadOnly === false) { - // Make nodes draggable - this.instance.draggable(this.nodeName, { - grid: [10, 10], - start: (params: { e: MouseEvent }) => { - if (params.e && !this.$store.getters.isNodeSelected(this.data.name)) { - // Only the node which gets dragged directly gets an event, for all others it is - // undefined. So check if the currently dragged node is selected and if not clear - // the drag-selection. - this.instance.clearDragSelection(); - this.$store.commit('resetSelectedNodes'); + // TODO: This caused problems with displaying old information + // https://github.com/jsplumb/katavorio/wiki + // https://jsplumb.github.io/jsplumb/home.html + // Make nodes draggable + this.instance.draggable(this.nodeName, { + grid: [10, 10], + start: (params: { e: MouseEvent }) => { + if (this.isReadOnly === true) { + // Do not allow to move nodes in readOnly mode + return false; + } + + if (params.e && !this.$store.getters.isNodeSelected(this.data.name)) { + // Only the node which gets dragged directly gets an event, for all others it is + // undefined. So check if the currently dragged node is selected and if not clear + // the drag-selection. + this.instance.clearDragSelection(); + this.$store.commit('resetSelectedNodes'); + } + + this.$store.commit('addActiveAction', 'dragActive'); + return true; + }, + stop: (params: { e: MouseEvent }) => { + if (this.$store.getters.isActionActive('dragActive')) { + const moveNodes = this.$store.getters.getSelectedNodes.slice(); + const selectedNodeNames = moveNodes.map((node: INodeUi) => node.name); + if (!selectedNodeNames.includes(this.data.name)) { + // If the current node is not in selected add it to the nodes which + // got moved manually + moveNodes.push(this.data); } - this.$store.commit('addActiveAction', 'dragActive'); - }, - stop: (params: { e: MouseEvent }) => { - if (this.$store.getters.isActionActive('dragActive')) { - const moveNodes = this.$store.getters.getSelectedNodes.slice(); - const selectedNodeNames = moveNodes.map((node: INodeUi) => node.name); - if (!selectedNodeNames.includes(this.data.name)) { - // If the current node is not in selected add it to the nodes which - // got moved manually - moveNodes.push(this.data); + // This does for some reason just get called once for the node that got clicked + // even though "start" and "drag" gets called for all. So lets do for now + // some dirty DOM query to get the new positions till I have more time to + // create a proper solution + let newNodePositon: XYPositon; + moveNodes.forEach((node: INodeUi) => { + const nodeElement = `node-${this.getNodeIndex(node.name)}`; + const element = document.getElementById(nodeElement); + if (element === null) { + return; } - // This does for some reason just get called once for the node that got clicked - // even though "start" and "drag" gets called for all. So lets do for now - // some dirty DOM query to get the new positions till I have more time to - // create a proper solution - let newNodePositon: XYPositon; - moveNodes.forEach((node: INodeUi) => { - const nodeElement = `node-${this.getNodeIndex(node.name)}`; - const element = document.getElementById(nodeElement); - if (element === null) { - return; - } + newNodePositon = [ + parseInt(element.style.left!.slice(0, -2), 10), + parseInt(element.style.top!.slice(0, -2), 10), + ]; - newNodePositon = [ - parseInt(element.style.left!.slice(0, -2), 10), - parseInt(element.style.top!.slice(0, -2), 10), - ]; + const updateInformation = { + name: node.name, + properties: { + // @ts-ignore, draggable does not have definitions + position: newNodePositon, + }, + }; - const updateInformation = { - name: node.name, - properties: { - // @ts-ignore, draggable does not have definitions - position: newNodePositon, - }, - }; + this.$store.commit('updateNodeProperties', updateInformation); + }); + } + }, + filter: '.node-description, .node-description .node-name, .node-description .node-subtitle', + }); - this.$store.commit('updateNodeProperties', updateInformation); - }); - } - }, - filter: '.node-description, .node-description .node-name, .node-description .node-subtitle', - }); - } }, isCtrlKeyPressed (e: MouseEvent | KeyboardEvent): boolean { From 709de6fd5f575fc5b21315b89320723800eb3b40 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 00:54:45 +0200 Subject: [PATCH 30/71] :zap: Some small improvements to Twitter-Node --- .../nodes/Twitter/TweetDescription.ts | 52 +----- .../nodes-base/nodes/Twitter/Twitter.node.ts | 166 +++++++++--------- 2 files changed, 87 insertions(+), 131 deletions(-) diff --git a/packages/nodes-base/nodes/Twitter/TweetDescription.ts b/packages/nodes-base/nodes/Twitter/TweetDescription.ts index 648e54616e..edd12bbf5e 100644 --- a/packages/nodes-base/nodes/Twitter/TweetDescription.ts +++ b/packages/nodes-base/nodes/Twitter/TweetDescription.ts @@ -75,54 +75,10 @@ export const tweetFields = [ options: [ { displayName: 'Attachments', - name: 'attachmentsUi', - placeholder: 'Add Attachments', - type: 'fixedCollection', - typeOptions: { - multipleValues: true, - }, - options: [ - { - name: 'attachment', - displayName: 'Attachment', - values: [ - { - displayName: 'Binary Property', - name: 'binaryPropertyName', - type: 'string', - default: 'data', - description: 'Name of the binary properties which contain data which should be added to tweet as attachment', - }, - // { - // displayName: 'Category', - // name: 'category', - // type: 'options', - // options: [ - // { - // name: 'Amplify Video', - // value: 'amplifyVideo', - // }, - // { - // name: 'Gif', - // value: 'tweetGif', - // }, - // { - // name: 'Image', - // value: 'tweetImage', - // }, - // { - // name: 'Video', - // value: 'tweetVideo', - // }, - // ], - // default: '', - // description: 'The category that represents how the media will be used', - // }, - ], - }, - ], - default: '', - description: 'Array of supported attachments to add to the message.', + name: 'attachments', + type: 'string', + default: 'data', + description: 'Name of the binary properties which contain
data which should be added to tweet as attachment.
Multiple ones can be comma separated.', }, { displayName: 'Display Coordinates', diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index 376e9d25d8..60f95e3798 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -109,96 +109,96 @@ export class Twitter implements INodeType { status: text, }; - if (additionalFields.attachmentsUi) { + if (additionalFields.attachments) { const mediaIds = []; - const attachmentsUi = additionalFields.attachmentsUi as IDataObject; + const attachments = additionalFields.attachments as string; const uploadUri = 'https://upload.twitter.com/1.1/media/upload.json'; - if (attachmentsUi.attachment) { - const attachtments = attachmentsUi.attachment as IDataObject[]; - for (const attachment of attachtments) { + const attachmentProperties: string[] = attachments.split(',').map((propertyName) => { + return propertyName.trim(); + }); - const binaryData = items[i].binary as IBinaryKeyData; - const binaryPropertyName = attachment.binaryPropertyName as string; + for (const binaryPropertyName of attachmentProperties) { - if (binaryData === undefined) { - throw new Error('No binary data set. So file can not be written!'); - } + const binaryData = items[i].binary as IBinaryKeyData; - if (!binaryData[binaryPropertyName]) { - continue; - } - - let attachmentBody = {}; - let response: IDataObject = {}; - - if (binaryData[binaryPropertyName].mimeType.includes('image')) { - - const attachmentBody = { - media_data: binaryData[binaryPropertyName].data, - }; - - response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); - - } else { - - // https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-init - - attachmentBody = { - command: 'INIT', - total_bytes: Buffer.from(binaryData[binaryPropertyName].data, BINARY_ENCODING).byteLength, - media_type: binaryData[binaryPropertyName].mimeType, - }; - - response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); - - const mediaId = response.media_id_string; - - // break the data on 5mb chunks (max size that can be uploaded at once) - - const binaryParts = chunks(Buffer.from(binaryData[binaryPropertyName].data, BINARY_ENCODING), 5242880); - - let index = 0; - - for (const binaryPart of binaryParts) { - - //https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-append - - attachmentBody = { - name: binaryData[binaryPropertyName].fileName, - command: 'APPEND', - media_id: mediaId, - media_data: Buffer.from(binaryPart).toString('base64'), - segment_index: index, - }; - - response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); - - index++; - } - - //https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-finalize - - attachmentBody = { - command: 'FINALIZE', - media_id: mediaId, - }; - - response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); - - // data has not been uploaded yet, so wait for it to be ready - if (response.processing_info) { - const { check_after_secs } = (response.processing_info as IDataObject); - await new Promise((resolve, reject) => { - setTimeout(() => { - resolve(); - }, (check_after_secs as number) * 1000); - }); - } - } - - mediaIds.push(response.media_id_string); + if (binaryData === undefined) { + throw new Error('No binary data set. So file can not be written!'); } + + if (!binaryData[binaryPropertyName]) { + continue; + } + + let attachmentBody = {}; + let response: IDataObject = {}; + + if (binaryData[binaryPropertyName].mimeType.includes('image')) { + + const attachmentBody = { + media_data: binaryData[binaryPropertyName].data, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + } else { + + // https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-init + + attachmentBody = { + command: 'INIT', + total_bytes: Buffer.from(binaryData[binaryPropertyName].data, BINARY_ENCODING).byteLength, + media_type: binaryData[binaryPropertyName].mimeType, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + const mediaId = response.media_id_string; + + // break the data on 5mb chunks (max size that can be uploaded at once) + + const binaryParts = chunks(Buffer.from(binaryData[binaryPropertyName].data, BINARY_ENCODING), 5242880); + + let index = 0; + + for (const binaryPart of binaryParts) { + + //https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-append + + attachmentBody = { + name: binaryData[binaryPropertyName].fileName, + command: 'APPEND', + media_id: mediaId, + media_data: Buffer.from(binaryPart).toString('base64'), + segment_index: index, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + index++; + } + + //https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-finalize + + attachmentBody = { + command: 'FINALIZE', + media_id: mediaId, + }; + + response = await twitterApiRequest.call(this, 'POST', '', attachmentBody, {}, uploadUri); + + // data has not been uploaded yet, so wait for it to be ready + if (response.processing_info) { + const { check_after_secs } = (response.processing_info as IDataObject); + await new Promise((resolve, reject) => { + setTimeout(() => { + resolve(); + }, (check_after_secs as number) * 1000); + }); + } + } + + mediaIds.push(response.media_id_string); } body.media_ids = mediaIds.join(','); From d2e738b5c7a9c7e8d09fda24047da236a071be38 Mon Sep 17 00:00:00 2001 From: Robarelli Date: Sun, 7 Jun 2020 23:34:10 -0600 Subject: [PATCH 31/71] Add MondayCom options for changing column values --- .../nodes/MondayCom/BoardItemDescription.ts | 157 ++++++++++++++++++ .../nodes/MondayCom/MondayCom.node.ts | 58 +++++++ 2 files changed, 215 insertions(+) diff --git a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts index c6725279ed..c3f81484f9 100644 --- a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts +++ b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts @@ -15,6 +15,16 @@ export const boardItemOperations = [ }, }, options: [ + { + name: 'Change Column Value', + value: 'changeColumnValue', + description: 'Change a column value for a board item', + }, + { + name: 'Change Multiple Column Values', + value: 'changeMultipleColumnValues', + description: 'Change multiple column values for a board item', + }, { name: 'Create', value: 'create', @@ -377,4 +387,151 @@ export const boardItemFields = [ default: 50, description: 'How many results to return.', }, +/* -------------------------------------------------------------------------- */ +/* boardItem:changeColumnValue */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Board ID', + name: 'boardId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getBoards', + }, + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: 'The unique identifier of the board.', + }, + { + displayName: 'Item ID', + name: 'itemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: `Item's ID` + }, + { + displayName: 'Column ID', + name: 'columnId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getColumns', + loadOptionsDependsOn: [ + 'boardId' + ], + }, + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: `The column's unique identifier.`, + }, + { + displayName: 'Value', + name: 'value', + type: 'json', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: 'The column value in JSON format.', + }, +/* -------------------------------------------------------------------------- */ +/* boardItem:changeMultipleColumnValues */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Board ID', + name: 'boardId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getBoards', + }, + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeMultipleColumnValues', + ], + }, + }, + description: 'The unique identifier of the board.', + }, + { + displayName: 'Item ID', + name: 'itemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeMultipleColumnValues', + ], + }, + }, + description: `Item's ID` + }, + { + displayName: 'Column Values', + name: 'columnValues', + type: 'json', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeMultipleColumnValues', + ], + }, + }, + description: 'The column fields and values in JSON format.', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts b/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts index f8e538e5cb..26ccdfa06e 100644 --- a/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts +++ b/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts @@ -487,6 +487,64 @@ export class MondayCom implements INodeType { responseData = await mondayComApiRequest.call(this, body); responseData = responseData.data.create_item; } + if (operation === 'changeColumnValue') { + const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const itemId = parseInt((this.getNodeParameter('itemId', i) as string), 10); + const columnId = this.getNodeParameter('columnId', i) as string; + const value = this.getNodeParameter('value', i) as string; + + const body: IGraphqlBody = { + query: + `mutation ($boardId: Int!, $itemId: Int!, $columnId: String!, $value: JSON!) { + change_column_value (board_id: $boardId, item_id: $itemId, column_id: $columnId, value: $value) { + id + } + }`, + variables: { + boardId, + itemId, + columnId, + }, + }; + + try { + JSON.parse(value); + } catch (e) { + throw new Error('Custom Values must be a valid JSON'); + } + body.variables.value = JSON.stringify(JSON.parse(value)); + + responseData = await mondayComApiRequest.call(this, body); + responseData = responseData.data.change_column_value; + } + if (operation === 'changeMultipleColumnValues') { + const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const itemId = parseInt((this.getNodeParameter('itemId', i) as string), 10); + const columnValues = this.getNodeParameter('columnValues', i) as string; + + const body: IGraphqlBody = { + query: + `mutation ($boardId: Int!, $itemId: Int!, $columnValues: JSON!) { + change_multiple_column_values (board_id: $boardId, item_id: $itemId, column_values: $columnValues) { + id + } + }`, + variables: { + boardId, + itemId, + }, + }; + + try { + JSON.parse(columnValues); + } catch (e) { + throw new Error('Custom Values must be a valid JSON'); + } + body.variables.columnValues = JSON.stringify(JSON.parse(columnValues)); + + responseData = await mondayComApiRequest.call(this, body); + responseData = responseData.data.change_multiple_column_values; + } if (operation === 'delete') { const itemId = parseInt((this.getNodeParameter('itemId', i) as string), 10); From 8e86443bdb88f0a77bb59f422844c25c2f425232 Mon Sep 17 00:00:00 2001 From: Robarelli Date: Sun, 7 Jun 2020 23:57:39 -0600 Subject: [PATCH 32/71] Add MondayCom option to add updates to an item. Make options alphabetical. --- .../nodes/MondayCom/BoardItemDescription.ts | 338 ++++++++++-------- .../nodes/MondayCom/MondayCom.node.ts | 60 ++-- 2 files changed, 231 insertions(+), 167 deletions(-) diff --git a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts index c3f81484f9..4f8cfccecc 100644 --- a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts +++ b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts @@ -15,6 +15,11 @@ export const boardItemOperations = [ }, }, options: [ + { + name: 'Add Update', + value: 'addUpdate', + description: `Add an update to an item.`, + }, { name: 'Change Column Value', value: 'changeColumnValue', @@ -58,6 +63,192 @@ export const boardItemOperations = [ export const boardItemFields = [ +/* -------------------------------------------------------------------------- */ +/* boardItem:addUpdate */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Item ID', + name: 'itemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'addUpdate', + ], + }, + }, + description: `Item's ID` + }, + { + displayName: 'Body', + name: 'value', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'addUpdate', + ], + }, + }, + description: 'The update text', + }, +/* -------------------------------------------------------------------------- */ +/* boardItem:changeColumnValue */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Board ID', + name: 'boardId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getBoards', + }, + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: 'The unique identifier of the board.', + }, + { + displayName: 'Item ID', + name: 'itemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: `Item's ID` + }, + { + displayName: 'Column ID', + name: 'columnId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getColumns', + loadOptionsDependsOn: [ + 'boardId' + ], + }, + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: `The column's unique identifier.`, + }, + { + displayName: 'Value', + name: 'value', + type: 'json', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeColumnValue', + ], + }, + }, + description: 'The column value in JSON format.', + }, +/* -------------------------------------------------------------------------- */ +/* boardItem:changeMultipleColumnValues */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Board ID', + name: 'boardId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getBoards', + }, + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeMultipleColumnValues', + ], + }, + }, + description: 'The unique identifier of the board.', + }, + { + displayName: 'Item ID', + name: 'itemId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeMultipleColumnValues', + ], + }, + }, + description: `Item's ID` + }, + { + displayName: 'Column Values', + name: 'columnValues', + type: 'json', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'boardItem', + ], + operation: [ + 'changeMultipleColumnValues', + ], + }, + }, + description: 'The column fields and values in JSON format.', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, /* -------------------------------------------------------------------------- */ /* boardItem:create */ /* -------------------------------------------------------------------------- */ @@ -387,151 +578,4 @@ export const boardItemFields = [ default: 50, description: 'How many results to return.', }, -/* -------------------------------------------------------------------------- */ -/* boardItem:changeColumnValue */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Board ID', - name: 'boardId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getBoards', - }, - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'boardItem', - ], - operation: [ - 'changeColumnValue', - ], - }, - }, - description: 'The unique identifier of the board.', - }, - { - displayName: 'Item ID', - name: 'itemId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'boardItem', - ], - operation: [ - 'changeColumnValue', - ], - }, - }, - description: `Item's ID` - }, - { - displayName: 'Column ID', - name: 'columnId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getColumns', - loadOptionsDependsOn: [ - 'boardId' - ], - }, - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'boardItem', - ], - operation: [ - 'changeColumnValue', - ], - }, - }, - description: `The column's unique identifier.`, - }, - { - displayName: 'Value', - name: 'value', - type: 'json', - required: true, - default: '', - displayOptions: { - show: { - resource: [ - 'boardItem', - ], - operation: [ - 'changeColumnValue', - ], - }, - }, - description: 'The column value in JSON format.', - }, -/* -------------------------------------------------------------------------- */ -/* boardItem:changeMultipleColumnValues */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Board ID', - name: 'boardId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getBoards', - }, - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'boardItem', - ], - operation: [ - 'changeMultipleColumnValues', - ], - }, - }, - description: 'The unique identifier of the board.', - }, - { - displayName: 'Item ID', - name: 'itemId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'boardItem', - ], - operation: [ - 'changeMultipleColumnValues', - ], - }, - }, - description: `Item's ID` - }, - { - displayName: 'Column Values', - name: 'columnValues', - type: 'json', - required: true, - default: '', - displayOptions: { - show: { - resource: [ - 'boardItem', - ], - operation: [ - 'changeMultipleColumnValues', - ], - }, - }, - description: 'The column fields and values in JSON format.', - typeOptions: { - alwaysOpenEditWindow: true, - }, - }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts b/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts index 26ccdfa06e..5dc7457081 100644 --- a/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts +++ b/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts @@ -455,37 +455,25 @@ export class MondayCom implements INodeType { } } if (resource === 'boardItem') { - if (operation === 'create') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); - const groupId = this.getNodeParameter('groupId', i) as string; - const itemName = this.getNodeParameter('name', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + if (operation === 'addUpdate') { + const itemId = parseInt((this.getNodeParameter('itemId', i) as string), 10); + const value = this.getNodeParameter('value', i) as string; const body: IGraphqlBody = { query: - `mutation ($boardId: Int!, $groupId: String!, $itemName: String!, $columnValues: JSON) { - create_item (board_id: $boardId, group_id: $groupId, item_name: $itemName, column_values: $columnValues) { + `mutation ($itemId: Int!, $value: String!) { + create_update (item_id: $itemId, body: $value) { id } }`, variables: { - boardId, - groupId, - itemName, + itemId, + value, }, }; - if (additionalFields.columnValues) { - try { - JSON.parse(additionalFields.columnValues as string); - } catch (e) { - throw new Error('Custom Values must be a valid JSON'); - } - body.variables.columnValues = JSON.stringify(JSON.parse(additionalFields.columnValues as string)); - } - responseData = await mondayComApiRequest.call(this, body); - responseData = responseData.data.create_item; + responseData = responseData.data.create_update; } if (operation === 'changeColumnValue') { const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); @@ -545,6 +533,38 @@ export class MondayCom implements INodeType { responseData = await mondayComApiRequest.call(this, body); responseData = responseData.data.change_multiple_column_values; } + if (operation === 'create') { + const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const groupId = this.getNodeParameter('groupId', i) as string; + const itemName = this.getNodeParameter('name', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IGraphqlBody = { + query: + `mutation ($boardId: Int!, $groupId: String!, $itemName: String!, $columnValues: JSON) { + create_item (board_id: $boardId, group_id: $groupId, item_name: $itemName, column_values: $columnValues) { + id + } + }`, + variables: { + boardId, + groupId, + itemName, + }, + }; + + if (additionalFields.columnValues) { + try { + JSON.parse(additionalFields.columnValues as string); + } catch (e) { + throw new Error('Custom Values must be a valid JSON'); + } + body.variables.columnValues = JSON.stringify(JSON.parse(additionalFields.columnValues as string)); + } + + responseData = await mondayComApiRequest.call(this, body); + responseData = responseData.data.create_item; + } if (operation === 'delete') { const itemId = parseInt((this.getNodeParameter('itemId', i) as string), 10); From 177c6f65eb8f578de7dd309ccfcd319cc150cc6f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 09:02:32 +0200 Subject: [PATCH 33/71] :zap: Remove no longer needed OAuth-Test-Files --- .../credentials/TestOAuth2Api.credentials.ts | 26 ---- packages/nodes-base/nodes/OAuth.node.ts | 147 ------------------ 2 files changed, 173 deletions(-) delete mode 100644 packages/nodes-base/credentials/TestOAuth2Api.credentials.ts delete mode 100644 packages/nodes-base/nodes/OAuth.node.ts diff --git a/packages/nodes-base/credentials/TestOAuth2Api.credentials.ts b/packages/nodes-base/credentials/TestOAuth2Api.credentials.ts deleted file mode 100644 index 2a350faecf..0000000000 --- a/packages/nodes-base/credentials/TestOAuth2Api.credentials.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - ICredentialType, - NodePropertyTypes, -} from 'n8n-workflow'; - -const scopes = [ - 'https://www.googleapis.com/auth/calendar', - 'https://www.googleapis.com/auth/calendar.events', -]; - -export class TestOAuth2Api implements ICredentialType { - name = 'testOAuth2Api'; - extends = [ - 'googleOAuth2Api', - ]; - displayName = 'Test OAuth2 API'; - properties = [ - { - displayName: 'Scope', - name: 'scope', - type: 'string' as NodePropertyTypes, - default: '', - placeholder: 'asdf', - }, - ]; -} diff --git a/packages/nodes-base/nodes/OAuth.node.ts b/packages/nodes-base/nodes/OAuth.node.ts deleted file mode 100644 index 3966571cd1..0000000000 --- a/packages/nodes-base/nodes/OAuth.node.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { OptionsWithUri } from 'request'; - -import { IExecuteFunctions } from 'n8n-core'; -import { - INodeExecutionData, - INodeType, - INodeTypeDescription, -} from 'n8n-workflow'; - -export class OAuth implements INodeType { - description: INodeTypeDescription = { - displayName: 'OAuth', - name: 'oauth', - icon: 'fa:code-branch', - group: ['input'], - version: 1, - description: 'Gets, sends data to Oauth API Endpoint and receives generic information.', - defaults: { - name: 'OAuth', - color: '#0033AA', - }, - inputs: ['main'], - outputs: ['main'], - credentials: [ - { - name: 'oAuth2Api', - required: true, - } - ], - properties: [ - { - displayName: 'Operation', - name: 'operation', - type: 'options', - options: [ - { - name: 'Get', - value: 'get', - description: 'Returns the OAuth token data.', - }, - { - name: 'Request', - value: 'request', - description: 'Make an OAuth signed requ.', - }, - ], - default: 'get', - description: 'The operation to perform.', - }, - { - displayName: 'Request Method', - name: 'requestMethod', - type: 'options', - displayOptions: { - show: { - operation: [ - 'request', - ], - }, - }, - options: [ - { - name: 'DELETE', - value: 'DELETE' - }, - { - name: 'GET', - value: 'GET' - }, - { - name: 'HEAD', - value: 'HEAD' - }, - { - name: 'PATCH', - value: 'PATCH' - }, - { - name: 'POST', - value: 'POST' - }, - { - name: 'PUT', - value: 'PUT' - }, - ], - default: 'GET', - description: 'The request method to use.', - }, - { - displayName: 'URL', - name: 'url', - type: 'string', - displayOptions: { - show: { - operation: [ - 'request', - ], - }, - }, - default: '', - placeholder: 'http://example.com/index.html', - description: 'The URL to make the request to.', - required: true, - }, - ] - }; - - async execute(this: IExecuteFunctions): Promise { - const credentials = this.getCredentials('oAuth2Api'); - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - - if (credentials.oauthTokenData === undefined) { - throw new Error('OAuth credentials not connected'); - } - - const operation = this.getNodeParameter('operation', 0) as string; - if (operation === 'get') { - // credentials.oauthTokenData has the refreshToken and accessToken available - // it would be nice to have credentials.getOAuthToken() which returns the accessToken - // and also handles an error case where if the token is to be refreshed, it does so - // without knowledge of the node. - - return [this.helpers.returnJsonArray(JSON.parse(credentials.oauthTokenData as string))]; - } else if (operation === 'request') { - const url = this.getNodeParameter('url', 0) as string; - const requestMethod = this.getNodeParameter('requestMethod', 0) as string; - - // Authorization Code Grant - const requestOptions: OptionsWithUri = { - headers: { - 'User-Agent': 'some-user', - }, - method: requestMethod, - uri: url, - json: true, - }; - //@ts-ignore - const responseData = await this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions); - return [this.helpers.returnJsonArray(responseData)]; - } else { - throw new Error('Unknown operation'); - } - } -} From 516a56ea320517816f0e8fa7888d516d0749365e Mon Sep 17 00:00:00 2001 From: Tanay Pant <7481165+tanay1337@users.noreply.github.com> Date: Mon, 8 Jun 2020 10:44:52 +0200 Subject: [PATCH 34/71] :books: Add breaking changes for 0.69.0 (#632) --- packages/cli/BREAKING-CHANGES.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index 0af22307f3..68be5ce947 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -3,6 +3,21 @@ This list shows all the versions which include breaking changes and how to upgrade. +## 0.69.0 + +### What changed? + +We have simplified how attachments are handled by the Twitter node. Rather than clicking on `Add Attachments` and having to specify the `Catergory`, you can now add attachments by just clicking on `Add Field` and selecting `Attachments`. There's no longer an option to specify the type of attachment you are adding. + +### When is action necessary? + +If you have used the Attachments option in your Twitter nodes. + +### How to upgrade: + +You'll need to re-create the attachments for the Twitter node. + + ## 0.68.0 ### What changed? From 6375934cb83fea7b2d91d78f036dd3249e2157e1 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 10:51:18 +0200 Subject: [PATCH 35/71] :bookmark: Release n8n-core@0.36.0 --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 259ce7d4cb..598ae14b23 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.35.0", + "version": "0.36.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From c3d2f1f5ea8d5737101e0b7d7f47438a833d1aa6 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 10:52:24 +0200 Subject: [PATCH 36/71] :arrow_up: Set n8n-core@0.36.0 on n8n-nodes-base --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 3f28db9dec..784affead3 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -330,7 +330,7 @@ "moment-timezone": "^0.5.28", "mongodb": "^3.5.5", "mysql2": "^2.0.1", - "n8n-core": "~0.35.0", + "n8n-core": "~0.36.0", "nodemailer": "^6.4.6", "pdf-parse": "^1.1.1", "pg-promise": "^9.0.3", From 768163a0b6d4e05432d74bcea130106db417f252 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 10:53:04 +0200 Subject: [PATCH 37/71] :bookmark: Release n8n-nodes-base@0.64.0 --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 784affead3..5cf4bb3eb9 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.63.1", + "version": "0.64.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 1159064d590c12c7db48023ca190ae111991a8fd Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 10:54:59 +0200 Subject: [PATCH 38/71] :arrow_up: Set n8n-workflow@0.33.0 on n8n-editor-ui --- packages/editor-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 8543d2fdd0..a89b070111 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -64,7 +64,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.32.0", + "n8n-workflow": "~0.33.0", "node-sass": "^4.12.0", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", From 734e6a8e870d3c25660bf42261f051b32ecac485 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 10:55:22 +0200 Subject: [PATCH 39/71] :bookmark: Release n8n-editor-ui@0.47.0 --- packages/editor-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index a89b070111..48caca7524 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.46.0", + "version": "0.47.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 7697a5703e7131afc694312349d544c20a2e8153 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 10:56:50 +0200 Subject: [PATCH 40/71] :arrow_up: Set n8n-core@0.36.0, n8n-editor-ui@0.47.0, n8n-nodes-base@0.64.0 and n8n-workflow@0.33.0 on n8n --- packages/cli/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 325a73d874..f447b5828d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -100,10 +100,10 @@ "lodash.get": "^4.4.2", "mongodb": "^3.5.5", "mysql2": "^2.0.1", - "n8n-core": "~0.35.0", - "n8n-editor-ui": "~0.46.0", - "n8n-nodes-base": "~0.63.1", - "n8n-workflow": "~0.32.0", + "n8n-core": "~0.36.0", + "n8n-editor-ui": "~0.47.0", + "n8n-nodes-base": "~0.64.0", + "n8n-workflow": "~0.33.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^7.11.0", From e6d70d6cc3ed11c8ced0b82f68e31610bbc58d56 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 10:57:23 +0200 Subject: [PATCH 41/71] :bookmark: Release n8n@0.69.0 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index f447b5828d..1b043c5494 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.68.2", + "version": "0.69.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 679660440ecccb396d675bfcbce12b39c044239e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 8 Jun 2020 11:11:04 +0200 Subject: [PATCH 42/71] :bookmark: Release n8n-workflow@0.33.0 --- packages/workflow/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 969270199b..068dc6d108 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.32.0", + "version": "0.33.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 3f9bdd2e4150184774d1de28883bc5d6d1837077 Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Mon, 8 Jun 2020 14:40:23 +0200 Subject: [PATCH 43/71] OAuth2 support --- .../HubspotOAuth2Api.credentials.ts | 59 +++++++++++++++++++ .../nodes/Hubspot/GenericFunctions.ts | 18 ++++-- .../nodes-base/nodes/Hubspot/Hubspot.node.ts | 37 +++++++++++- .../nodes/Hubspot/HubspotTrigger.node.ts | 35 +++++++++++ packages/nodes-base/package.json | 1 + 5 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts diff --git a/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts b/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts new file mode 100644 index 0000000000..775211cbc9 --- /dev/null +++ b/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts @@ -0,0 +1,59 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class HubspotOAuth2Api implements ICredentialType { + name = 'hubspotOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'Hubspot OAuth2 API'; + properties = [ + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://app.hubspot.com/oauth/authorize', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://api.hubapi.com/oauth/v1/token', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: 'grant_type=authorization_code', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'options' as NodePropertyTypes, + options: [ + { + name: 'Body', + value: 'body', + description: 'Send credentials in body', + }, + { + name: 'Header', + value: 'header', + description: 'Send credentials as Basic Auth header', + }, + ], + default: 'header', + description: 'Resource to consume.', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts index 969c392cdd..eb414b82aa 100644 --- a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts @@ -14,12 +14,8 @@ import { } from 'n8n-workflow'; export async function hubspotApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise { // tslint:disable-line:no-any + const authenticationMethod = this.getNodeParameter('authentication', 0); - const node = this.getNode(); - const credentialName = Object.keys(node.credentials!)[0]; - const credentials = this.getCredentials(credentialName); - - query!.hapikey = credentials!.apiKey as string; const options: OptionsWithUri = { method, qs: query, @@ -28,8 +24,18 @@ export async function hubspotApiRequest(this: IHookFunctions | IExecuteFunctions json: true, useQuerystring: true, }; + try { - return await this.helpers.request!(options); + if (authenticationMethod === 'accessToken') { + const credentials = this.getCredentials('hubspotApi'); + + options.qs.hapikey = credentials!.apiKey as string; + + return await this.helpers.request!(options); + } else { + // @ts-ignore + return await this.helpers.requestOAuth2!.call(this, 'hubspotOAuth2Api', options, 'Bearer'); + } } catch (error) { if (error.response && error.response.body && error.response.body.errors) { // Try to return the error prettier diff --git a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts index 6c1d7be400..bed4606042 100644 --- a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts +++ b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts @@ -73,9 +73,44 @@ export class Hubspot implements INodeType { { name: 'hubspotApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'hubspotOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + description: 'The method of authentication.', + }, { displayName: 'Resource', name: 'resource', diff --git a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts index 25028f88e8..c2ed3f3e0a 100644 --- a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts +++ b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts @@ -37,6 +37,24 @@ export class HubspotTrigger implements INodeType { { name: 'hubspotDeveloperApi', required: true, + displayOptions: { + show: { + authentication: [ + 'developerApi', + ], + }, + }, + }, + { + name: 'hubspotOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, }, ], webhooks: [ @@ -54,6 +72,23 @@ export class HubspotTrigger implements INodeType { }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Developer API', + value: 'developerApi', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'developerApi', + description: 'The method of authentication.', + }, { displayName: 'App ID', name: 'appId', diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 31cb4a8da5..d41b5bb4ba 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -68,6 +68,7 @@ "dist/credentials/HttpHeaderAuth.credentials.js", "dist/credentials/HubspotApi.credentials.js", "dist/credentials/HubspotDeveloperApi.credentials.js", + "dist/credentials/HubspotOAuth2Api.credentials.js", "dist/credentials/HunterApi.credentials.js", "dist/credentials/Imap.credentials.js", "dist/credentials/IntercomApi.credentials.js", From 918fba81cc300e58b6f6e43974e650e77a2f26c4 Mon Sep 17 00:00:00 2001 From: ricardo Date: Mon, 8 Jun 2020 16:13:17 -0400 Subject: [PATCH 44/71] :zap: Improve error message --- packages/nodes-base/nodes/Twitter/Twitter.node.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index 60f95e3798..94718973c5 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -133,7 +133,15 @@ export class Twitter implements INodeType { let attachmentBody = {}; let response: IDataObject = {}; - if (binaryData[binaryPropertyName].mimeType.includes('image')) { + const isAnimatedWebp = (Buffer.from(binaryData[binaryPropertyName].data, 'base64').toString().indexOf('ANMF') !== -1); + + const isImage = binaryData[binaryPropertyName].mimeType.includes('image'); + + if (isImage && isAnimatedWebp) { + throw new Error('Animated .webp images are not supported use .git instead'); + } + + if (isImage) { const attachmentBody = { media_data: binaryData[binaryPropertyName].data, From 62c835656671fab65187f2d861ffb5a98c7440db Mon Sep 17 00:00:00 2001 From: Erin McNulty Date: Tue, 9 Jun 2020 16:50:09 -0400 Subject: [PATCH 45/71] :twisted_rightwards_arrows: Fix Github Trigger node and add OAuth support (#643) * Fixed bug with small name change in credentials * Deleted Excess Commented Code * Changed the code to give user options --- .../nodes/Github/GithubTrigger.node.ts | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Github/GithubTrigger.node.ts b/packages/nodes-base/nodes/Github/GithubTrigger.node.ts index 2a14b5d3db..1360d7536a 100644 --- a/packages/nodes-base/nodes/Github/GithubTrigger.node.ts +++ b/packages/nodes-base/nodes/Github/GithubTrigger.node.ts @@ -34,7 +34,25 @@ export class GithubTrigger implements INodeType { { name: 'githubApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'githubOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, ], webhooks: [ { @@ -45,6 +63,23 @@ export class GithubTrigger implements INodeType { }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + description: 'The resource to operate on.', + }, { displayName: 'Repository Owner', name: 'owner', From 751d6934d7a04e6a56cc2da08217a64b603be2c7 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 9 Jun 2020 23:05:51 +0200 Subject: [PATCH 46/71] :bookmark: Release n8n-nodes-base@0.64.1 --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 5cf4bb3eb9..822978eaf4 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.64.0", + "version": "0.64.1", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From e8737b5c4e3e79104a298d6b582784b67965f178 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 9 Jun 2020 23:07:04 +0200 Subject: [PATCH 47/71] :arrow_up: Set n8n-nodes-base@0.64.1 on n8n --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 1b043c5494..3873eb6bdb 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -102,7 +102,7 @@ "mysql2": "^2.0.1", "n8n-core": "~0.36.0", "n8n-editor-ui": "~0.47.0", - "n8n-nodes-base": "~0.64.0", + "n8n-nodes-base": "~0.64.1", "n8n-workflow": "~0.33.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", From 4eff72504d0491443998db82b497f47a7ccb61b3 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 9 Jun 2020 23:07:51 +0200 Subject: [PATCH 48/71] :bookmark: Release n8n@0.69.1 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 3873eb6bdb..f3a747bfe2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.69.0", + "version": "0.69.1", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 60fa44722d96e6944e34899db3d3464f0475747e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 10 Jun 2020 11:46:12 +0200 Subject: [PATCH 49/71] :zap: Remove not working and not possible option from Github-Node --- packages/nodes-base/nodes/Github/Github.node.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/nodes-base/nodes/Github/Github.node.ts b/packages/nodes-base/nodes/Github/Github.node.ts index 00133ec302..035b381091 100644 --- a/packages/nodes-base/nodes/Github/Github.node.ts +++ b/packages/nodes-base/nodes/Github/Github.node.ts @@ -244,11 +244,6 @@ export class Github implements INodeType { }, }, options: [ - { - name: 'Get Emails', - value: 'getEmails', - description: 'Returns the email addresses of a user', - }, { name: 'Get Repositories', value: 'getRepositories', From d6144838281c3a9004d2eb440b74b27126628c44 Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Wed, 10 Jun 2020 12:57:13 +0200 Subject: [PATCH 50/71] trigger node fix --- .../nodes/Hubspot/HubspotTrigger.node.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts index c2ed3f3e0a..38fd678d58 100644 --- a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts +++ b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts @@ -281,7 +281,21 @@ export class HubspotTrigger implements INodeType { }; async webhook(this: IWebhookFunctions): Promise { - const credentials = this.getCredentials('hubspotDeveloperApi'); + + const authenticationMethod = this.getNodeParameter('authentication') as string; + + let credentials : IDataObject; + + if (authenticationMethod === 'hubspotDeveloperApi') { + credentials = this.getCredentials('hubspotDeveloperApi') as IDataObject; + } else { + credentials = this.getCredentials('hubspotOAuth2Api') as IDataObject; + } + + if (credentials === undefined) { + throw new Error('No credentials found!'); + } + const req = this.getRequestObject(); const bodyData = req.body; const headerData = this.getHeaderData(); From 57b3a2e05eb59bbfdbd53c39c64d49fb9d0ed150 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 10 Jun 2020 13:00:28 +0200 Subject: [PATCH 51/71] :zap: Cleanup of external hooks --- packages/cli/commands/execute.ts | 5 +++ packages/cli/commands/start.ts | 3 -- packages/cli/src/ExternalHooks.ts | 1 - packages/cli/src/Server.ts | 4 +- .../cli/src/externalHooksTemp/test-hooks.ts | 45 ------------------- 5 files changed, 7 insertions(+), 51 deletions(-) delete mode 100644 packages/cli/src/externalHooksTemp/test-hooks.ts diff --git a/packages/cli/commands/execute.ts b/packages/cli/commands/execute.ts index 7501b4aa9b..54016c5d55 100644 --- a/packages/cli/commands/execute.ts +++ b/packages/cli/commands/execute.ts @@ -10,6 +10,7 @@ import { import { ActiveExecutions, Db, + ExternalHooks, GenericHelpers, IWorkflowBase, IWorkflowExecutionDataProcess, @@ -103,6 +104,10 @@ export class Execute extends Command { // Wait till the n8n-packages have been read await loadNodesAndCredentialsPromise; + // Load all external hooks + const externalHooks = ExternalHooks(); + await externalHooks.init(); + // Add the found types to an instance other parts of the application can use const nodeTypes = NodeTypes(); await nodeTypes.init(loadNodesAndCredentials.nodeTypes); diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts index 65edec601d..bce82bf890 100644 --- a/packages/cli/commands/start.ts +++ b/packages/cli/commands/start.ts @@ -5,7 +5,6 @@ import { } from 'n8n-core'; import { Command, flags } from '@oclif/command'; const open = require('open'); -// import { dirname } from 'path'; import * as config from '../config'; import { @@ -113,8 +112,6 @@ export class Start extends Command { const externalHooks = ExternalHooks(); await externalHooks.init(); - // await externalHooks.run('credentials.new'); - // Add the found types to an instance other parts of the application can use const nodeTypes = NodeTypes(); await nodeTypes.init(loadNodesAndCredentials.nodeTypes); diff --git a/packages/cli/src/ExternalHooks.ts b/packages/cli/src/ExternalHooks.ts index 9d195ccbad..b2b84cd7c7 100644 --- a/packages/cli/src/ExternalHooks.ts +++ b/packages/cli/src/ExternalHooks.ts @@ -6,7 +6,6 @@ import { import * as config from '../config'; -// export EXTERNAL_HOOK_FILES=/data/packages/cli/dist/src/externalHooksTemp/test-hooks.js class ExternalHooksClass implements IExternalHooksClass { diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index a36a2fd81e..d302e3970a 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -34,7 +34,7 @@ import { IExecutionsListResponse, IExecutionsStopData, IExecutionsSummary, - IExternalHooks, + IExternalHooksClass, IN8nUISettings, IPackageVersions, IWorkflowBase, @@ -94,7 +94,7 @@ class App { testWebhooks: TestWebhooks.TestWebhooks; endpointWebhook: string; endpointWebhookTest: string; - externalHooks: IExternalHooks; + externalHooks: IExternalHooksClass; saveDataErrorExecution: string; saveDataSuccessExecution: string; saveManualExecutions: boolean; diff --git a/packages/cli/src/externalHooksTemp/test-hooks.ts b/packages/cli/src/externalHooksTemp/test-hooks.ts deleted file mode 100644 index b49c0600b2..0000000000 --- a/packages/cli/src/externalHooksTemp/test-hooks.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - WorkflowExecuteMode, -} from 'n8n-workflow'; - -import { - IExternalHooks, - IExternalHooksFunctions, - IWorkflowBase, - IWorkflowDb, -} from '../'; - - -export = { - credentials: { - create: [ - async function (this: IExternalHooksFunctions) { - // Here any additional code can run or the creation blocked - // throw new Error('No additional credentials can be created.'); - }, - ], - }, - workflow: { - execute: [ - async function (this: IExternalHooksFunctions, workflowData: IWorkflowDb, mode: WorkflowExecuteMode) { - console.log('execute: ' + mode); - // if (mode === 'integrated') { - // throw new Error('Workflow can not be executed.'); - // } - } - ], - update: [ - async function (this: IExternalHooksFunctions, workflowData: IWorkflowBase) { - console.log('update workflow hook'); - - // const responseData = await this.dbCollections.Workflow!.findOne(workflowData.id); - // console.log('workflowData'); - // console.log(responseData); - // console.log(workflowData); - - // Here any additional code can run or the creation blocked - // throw new Error('Workflow can not be updated.'); - }, - ], - }, -} as IExternalHooks; From ca32d21f869379ffa784960c185f7dd3e47143c8 Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 10 Jun 2020 17:37:01 -0400 Subject: [PATCH 52/71] :zap: Mautic extended --- .../MauticOAuth2Api.credentials.ts | 55 ++ .../nodes/Mautic/ContactDescription.ts | 562 +++++++++++++++++- .../nodes/Mautic/GenericFunctions.ts | 40 +- .../nodes-base/nodes/Mautic/Mautic.node.ts | 184 +++++- packages/nodes-base/package.json | 1 + 5 files changed, 809 insertions(+), 33 deletions(-) create mode 100644 packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts diff --git a/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts new file mode 100644 index 0000000000..8c52f9372c --- /dev/null +++ b/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts @@ -0,0 +1,55 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class MauticOAuth2Api implements ICredentialType { + name = 'mauticOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'Mautic OAuth2 API'; + properties = [ + { + displayName: 'URL', + name: 'url', + type: 'string' as NodePropertyTypes, + default: '', + placeholder: 'https://name.mautic.net', + }, + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'string' as NodePropertyTypes, + default: '', + placeholder: 'https://name.mautic.net/oauth/v2/authorize', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'string' as NodePropertyTypes, + default: '', + placeholder: 'https://name.mautic.net/oauth/v2/token', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'header', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Mautic/ContactDescription.ts b/packages/nodes-base/nodes/Mautic/ContactDescription.ts index b9eb9b0f42..1ea81acbc8 100644 --- a/packages/nodes-base/nodes/Mautic/ContactDescription.ts +++ b/packages/nodes-base/nodes/Mautic/ContactDescription.ts @@ -226,6 +226,94 @@ export const contactFields = [ }, }, options: [ + { + displayName: 'Address', + name: 'addressUi', + placeholder: 'Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressValues', + displayName: 'Address', + values: [ + { + displayName: 'Address Line 1', + name: 'address1', + type: 'string', + default: '', + }, + { + displayName: 'Address Line 2', + name: 'address2', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'zipCode', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'B2B or B2C', + name: 'b2bOrb2c', + type: 'options', + options: [ + { + name: 'B2B', + value: 'B2B', + }, + { + name: 'B2C', + value: 'B2C', + }, + ], + default: '', + }, + { + displayName: 'CRM ID', + name: 'crmId', + type: 'string', + default: '', + }, + { + displayName: 'Fax', + name: 'fax', + type: 'string', + default: '', + }, + { + displayName: 'Has Purchased', + name: 'hasPurchased', + type: 'boolean', + default: false, + }, { displayName: 'IP Address', name: 'ipAddress', @@ -240,6 +328,12 @@ export const contactFields = [ default: '', description: 'Date/time in UTC;', }, + { + displayName: 'Mobile', + name: 'mobile', + type: 'string', + default: '', + }, { displayName: 'Owner ID', name: 'ownerId', @@ -247,6 +341,112 @@ export const contactFields = [ default: '', description: 'ID of a Mautic user to assign this contact to', }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + }, + { + displayName: 'Prospect or Customer', + name: 'prospectOrCustomer', + type: 'options', + options: [ + { + name: 'Prospect', + value: 'Prospect', + }, + { + name: 'Customer', + value: 'Customer', + }, + ], + default: '', + }, + { + displayName: 'Sandbox', + name: 'sandbox', + type: 'boolean', + default: false, + }, + { + displayName: 'Stage', + name: 'stage', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getStages', + }, + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: '', + }, + { + displayName: 'Social Media', + name: 'socialMediaUi', + placeholder: 'Social Media', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'socialMediaValues', + displayName: 'Social Media', + values: [ + { + displayName: 'Facebook', + name: 'facebook', + type: 'string', + default: '', + }, + { + displayName: 'Foursquare', + name: 'foursquare', + type: 'string', + default: '', + }, + { + displayName: 'Instagram', + name: 'instagram', + type: 'string', + default: '', + }, + { + displayName: 'LinkedIn', + name: 'linkedIn', + type: 'string', + default: '', + }, + { + displayName: 'Skype', + name: 'skype', + type: 'string', + default: '', + }, + { + displayName: 'Twitter', + name: 'twitter', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + }, ], }, @@ -318,6 +518,103 @@ export const contactFields = [ default: '', description: 'Contact parameters', }, + { + displayName: 'Address', + name: 'addressUi', + placeholder: 'Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: {}, + options: [ + { + name: 'addressValues', + displayName: 'Address', + values: [ + { + displayName: 'Address Line 1', + name: 'address1', + type: 'string', + default: '', + }, + { + displayName: 'Address Line 2', + name: 'address2', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'zipCode', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'B2B or B2C', + name: 'b2bOrb2c', + type: 'options', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + options: [ + { + name: 'B2B', + value: 'B2B', + }, + { + name: 'B2C', + value: 'B2C', + }, + ], + default: '', + }, + { + displayName: 'CRM ID', + name: 'crmId', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + }, { displayName: 'Email', name: 'email', @@ -332,6 +629,19 @@ export const contactFields = [ default: '', description: 'Email address of the contact.', }, + { + displayName: 'Fax', + name: 'fax', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + }, { displayName: 'First Name', name: 'firstName', @@ -346,6 +656,47 @@ export const contactFields = [ default: '', description: 'First Name', }, + { + displayName: 'Has Purchased', + name: 'hasPurchased', + type: 'boolean', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: false, + }, + { + displayName: 'IP Address', + name: 'ipAddress', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + description: 'IP address to associate with the contact', + }, + { + displayName: 'Last Active', + name: 'lastActive', + type: 'dateTime', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + description: 'Date/time in UTC;', + }, { displayName: 'Last Name', name: 'lastName', @@ -360,6 +711,60 @@ export const contactFields = [ default: '', description: 'LastName', }, + { + displayName: 'Mobile', + name: 'mobile', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + }, + { + displayName: 'Owner ID', + name: 'ownerId', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + description: 'ID of a Mautic user to assign this contact to', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + }, + { + displayName: 'Position', + name: 'position', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + description: 'Position', + }, { displayName: 'Primary Company', name: 'company', @@ -378,9 +783,9 @@ export const contactFields = [ description: 'Primary company', }, { - displayName: 'Position', - name: 'position', - type: 'string', + displayName: 'Prospect or Customer', + name: 'prospectOrCustomer', + type: 'options', displayOptions: { show: { '/jsonParameters': [ @@ -388,8 +793,62 @@ export const contactFields = [ ], }, }, + options: [ + { + name: 'Prospect', + value: 'Prospect', + }, + { + name: 'Customer', + value: 'Customer', + }, + ], + default: '', + }, + { + displayName: 'Sandbox', + name: 'sandbox', + type: 'boolean', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: false, + }, + { + displayName: 'Stage', + name: 'stage', + type: 'options', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getStages', + }, + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getTags', + }, default: '', - description: 'Position', }, { displayName: 'Title', @@ -405,27 +864,94 @@ export const contactFields = [ default: '', description: 'Title', }, + { + displayName: 'Social Media', + name: 'socialMediaUi', + placeholder: 'Social Media', + type: 'fixedCollection', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'socialMediaValues', + displayName: 'Social Media', + values: [ + { + displayName: 'Facebook', + name: 'facebook', + type: 'string', + default: '', + }, + { + displayName: 'Foursquare', + name: 'foursquare', + type: 'string', + default: '', + }, + { + displayName: 'Instagram', + name: 'instagram', + type: 'string', + default: '', + }, + { + displayName: 'LinkedIn', + name: 'linkedIn', + type: 'string', + default: '', + }, + { + displayName: 'Skype', + name: 'skype', + type: 'string', + default: '', + }, + { + displayName: 'Twitter', + name: 'twitter', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, + default: '', + }, { displayName: 'IP Address', name: 'ipAddress', type: 'string', + displayOptions: { + show: { + '/jsonParameters': [ + false, + ], + }, + }, default: '', description: 'IP address to associate with the contact', }, - { - displayName: 'Last Active', - name: 'lastActive', - type: 'dateTime', - default: '', - description: 'Date/time in UTC;', - }, - { - displayName: 'Owner ID', - name: 'ownerId', - type: 'string', - default: '', - description: 'ID of a Mautic user to assign this contact to', - }, ], }, diff --git a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts index 6b179db7fd..9861690254 100644 --- a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts @@ -10,7 +10,6 @@ import { import { IDataObject, } from 'n8n-workflow'; -import { errors } from 'imap-simple'; interface OMauticErrorResponse { errors: Array<{ @@ -19,7 +18,7 @@ interface OMauticErrorResponse { }>; } -function getErrors(error: OMauticErrorResponse): string { +export function getErrors(error: OMauticErrorResponse): string { const returnErrors: string[] = []; for (const errorItem of error.errors) { @@ -31,23 +30,40 @@ function getErrors(error: OMauticErrorResponse): string { export async function mauticApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('mauticApi'); - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - const base64Key = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); + const authenticationMethod = this.getNodeParameter('authentication', 0, 'credentials') as string; + const options: OptionsWithUri = { - headers: { Authorization: `Basic ${base64Key}` }, + headers: {}, method, qs: query, - uri: uri || `${credentials.url}/api${endpoint}`, + uri: uri || `/api${endpoint}`, body, json: true }; - try { - const returnData = await this.helpers.request!(options); - if (returnData.error) { + try { + + let returnData; + + if (authenticationMethod === 'credentials') { + const credentials = this.getCredentials('mauticApi') as IDataObject; + + const base64Key = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); + + options.headers!.Authorization = `Basic ${base64Key}`; + + options.uri = `${credentials.url}${options.uri}`; + //@ts-ignore + returnData = await this.helpers.request(options); + } else { + const credentials = this.getCredentials('mauticOAuth2Api') as IDataObject; + + options.uri = `${credentials.url}${options.uri}`; + //@ts-ignore + returnData = await this.helpers.requestOAuth2.call(this, 'mauticOAuth2Api', options); + } + + if (returnData.errors) { // They seem to to sometimes return 200 status but still error. throw new Error(getErrors(returnData)); } diff --git a/packages/nodes-base/nodes/Mautic/Mautic.node.ts b/packages/nodes-base/nodes/Mautic/Mautic.node.ts index 50abcda83e..7bdafe7605 100644 --- a/packages/nodes-base/nodes/Mautic/Mautic.node.ts +++ b/packages/nodes-base/nodes/Mautic/Mautic.node.ts @@ -1,5 +1,3 @@ -import { snakeCase } from 'change-case'; - import { IExecuteFunctions, } from 'n8n-core'; @@ -15,12 +13,18 @@ import { mauticApiRequest, mauticApiRequestAllItems, validateJSON, + getErrors, } from './GenericFunctions'; + import { contactFields, contactOperations, } from './ContactDescription'; +import { + snakeCase, + } from 'change-case'; + export class Mautic implements INodeType { description: INodeTypeDescription = { displayName: 'Mautic', @@ -40,9 +44,43 @@ export class Mautic implements INodeType { { name: 'mauticApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'credentials', + ], + }, + }, + }, + { + name: 'mauticOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Credentials', + value: 'credentials', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'credentials', + }, { displayName: 'Resource', name: 'resource', @@ -77,6 +115,32 @@ export class Mautic implements INodeType { } return returnData; }, + // Get all the available tags to display them to user so that he can + // select them easily + async getTags(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const tags = await mauticApiRequestAllItems.call(this, 'tags', 'GET', '/tags'); + for (const tag of tags) { + returnData.push({ + name: tag.tag, + value: tag.tag, + }); + } + return returnData; + }, + // Get all the available stages to display them to user so that he can + // select them easily + async getStages(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const stages = await mauticApiRequestAllItems.call(this, 'stages', 'GET', '/stages'); + for (const stage of stages) { + returnData.push({ + name: stage.name, + value: stage.id, + }); + } + return returnData; + }, }, }; @@ -124,6 +188,62 @@ export class Mautic implements INodeType { if (additionalFields.ownerId) { body.ownerId = additionalFields.ownerId as string; } + if (additionalFields.addressUi) { + const addressValues = (additionalFields.addressUi as IDataObject).addressValues as IDataObject; + if (addressValues) { + body.address1 = addressValues.address1 as string; + body.address2 = addressValues.address2 as string; + body.city = addressValues.city as string; + body.state = addressValues.state as string; + body.country = addressValues.country as string; + body.zipcode = addressValues.zipCode as string; + } + } + if (additionalFields.socialMediaUi) { + const socialMediaValues = (additionalFields.socialMediaUi as IDataObject).socialMediaValues as IDataObject; + if (socialMediaValues) { + body.facebook = socialMediaValues.facebook as string; + body.foursquare = socialMediaValues.foursquare as string; + body.instagram = socialMediaValues.instagram as string; + body.linkedin = socialMediaValues.linkedIn as string; + body.skype = socialMediaValues.skype as string; + body.twitter = socialMediaValues.twitter as string; + } + } + if (additionalFields.b2bOrb2c) { + body.b2b_or_b2c = additionalFields.b2bOrb2c as string; + } + if (additionalFields.crmId) { + body.crm_id = additionalFields.crmId as string; + } + if (additionalFields.fax) { + body.fax = additionalFields.fax as string; + } + if (additionalFields.hasPurchased) { + body.haspurchased = additionalFields.hasPurchased as boolean; + } + if (additionalFields.mobile) { + body.mobile = additionalFields.mobile as string; + } + if (additionalFields.phone) { + body.phone = additionalFields.phone as string; + } + if (additionalFields.prospectOrCustomer) { + body.prospect_or_customer = additionalFields.prospectOrCustomer as string; + } + if (additionalFields.sandbox) { + body.sandbox = additionalFields.sandbox as boolean; + } + if (additionalFields.stage) { + body.stage = additionalFields.stage as string; + } + if (additionalFields.tags) { + body.tags = additionalFields.tags as string; + } + if (additionalFields.website) { + body.website = additionalFields.website as string; + } + responseData = await mauticApiRequest.call(this, 'POST', '/contacts/new', body); responseData = responseData.contact; } @@ -167,6 +287,61 @@ export class Mautic implements INodeType { if (updateFields.ownerId) { body.ownerId = updateFields.ownerId as string; } + if (updateFields.addressUi) { + const addressValues = (updateFields.addressUi as IDataObject).addressValues as IDataObject; + if (addressValues) { + body.address1 = addressValues.address1 as string; + body.address2 = addressValues.address2 as string; + body.city = addressValues.city as string; + body.state = addressValues.state as string; + body.country = addressValues.country as string; + body.zipcode = addressValues.zipCode as string; + } + } + if (updateFields.socialMediaUi) { + const socialMediaValues = (updateFields.socialMediaUi as IDataObject).socialMediaValues as IDataObject; + if (socialMediaValues) { + body.facebook = socialMediaValues.facebook as string; + body.foursquare = socialMediaValues.foursquare as string; + body.instagram = socialMediaValues.instagram as string; + body.linkedin = socialMediaValues.linkedIn as string; + body.skype = socialMediaValues.skype as string; + body.twitter = socialMediaValues.twitter as string; + } + } + if (updateFields.b2bOrb2c) { + body.b2b_or_b2c = updateFields.b2bOrb2c as string; + } + if (updateFields.crmId) { + body.crm_id = updateFields.crmId as string; + } + if (updateFields.fax) { + body.fax = updateFields.fax as string; + } + if (updateFields.hasPurchased) { + body.haspurchased = updateFields.hasPurchased as boolean; + } + if (updateFields.mobile) { + body.mobile = updateFields.mobile as string; + } + if (updateFields.phone) { + body.phone = updateFields.phone as string; + } + if (updateFields.prospectOrCustomer) { + body.prospect_or_customer = updateFields.prospectOrCustomer as string; + } + if (updateFields.sandbox) { + body.sandbox = updateFields.sandbox as boolean; + } + if (updateFields.stage) { + body.stage = updateFields.stage as string; + } + if (updateFields.tags) { + body.tags = updateFields.tags as string; + } + if (updateFields.website) { + body.website = updateFields.website as string; + } responseData = await mauticApiRequest.call(this, 'PATCH', `/contacts/${contactId}/edit`, body); responseData = responseData.contact; } @@ -193,6 +368,9 @@ export class Mautic implements INodeType { qs.limit = this.getNodeParameter('limit', i) as number; qs.start = 0; responseData = await mauticApiRequest.call(this, 'GET', '/contacts', {}, qs); + if (responseData.errors) { + throw new Error(getErrors(responseData)); + } responseData = responseData.contacts; responseData = Object.values(responseData); } diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 2a6df1e4e3..63e51f078a 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -85,6 +85,7 @@ "dist/credentials/MandrillApi.credentials.js", "dist/credentials/MattermostApi.credentials.js", "dist/credentials/MauticApi.credentials.js", + "dist/credentials/MauticOAuth2Api.credentials.js", "dist/credentials/MicrosoftExcelOAuth2Api.credentials.js", "dist/credentials/MicrosoftOAuth2Api.credentials.js", "dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js", From 677ffec6f9b817097ce7ed875b5dddf4da14a3ac Mon Sep 17 00:00:00 2001 From: shraddha shaligram Date: Thu, 11 Jun 2020 15:57:32 -0700 Subject: [PATCH 53/71] add messagebird API improve code readability fixed a minor error --- .../credentials/MessageBirdApi.credentials.ts | 14 + .../nodes/MessageBird/GenericFunctions.ts | 68 ++ .../nodes/MessageBird/MessageBird.node.ts | 354 +++++++++ .../nodes/MessageBird/messagebird.png | Bin 0 -> 1305 bytes packages/nodes-base/package.json | 720 +++++++++--------- 5 files changed, 797 insertions(+), 359 deletions(-) create mode 100644 packages/nodes-base/credentials/MessageBirdApi.credentials.ts create mode 100644 packages/nodes-base/nodes/MessageBird/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/MessageBird/MessageBird.node.ts create mode 100644 packages/nodes-base/nodes/MessageBird/messagebird.png diff --git a/packages/nodes-base/credentials/MessageBirdApi.credentials.ts b/packages/nodes-base/credentials/MessageBirdApi.credentials.ts new file mode 100644 index 0000000000..e67c6a0c9e --- /dev/null +++ b/packages/nodes-base/credentials/MessageBirdApi.credentials.ts @@ -0,0 +1,14 @@ +import { ICredentialType, NodePropertyTypes } from 'n8n-workflow'; + +export class MessageBirdApi implements ICredentialType { + name = 'messageBirdApi'; + displayName = 'MessageBird API'; + properties = [ + { + displayName: 'API Key', + name: 'accessKey', + type: 'string' as NodePropertyTypes, + default: '' + } + ]; +} diff --git a/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts b/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts new file mode 100644 index 0000000000..ff1df3b265 --- /dev/null +++ b/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts @@ -0,0 +1,68 @@ +import { IExecuteFunctions, IHookFunctions } from 'n8n-core'; +import { OptionsWithUri } from 'request'; + +import { IDataObject } from 'n8n-workflow'; + +/** + * Make an API request to Message Bird + * + * @param {IHookFunctions} this + * @param {string} method + * @param {string} url + * @param {object} body + * @returns {Promise} + */ +export async function messageBirdApiRequest( + this: IHookFunctions | IExecuteFunctions, + method: string, + resource: string, + body: IDataObject, + query?: IDataObject +): Promise { + const credentials = this.getCredentials('messageBirdApi'); + if (credentials === undefined) { + throw new Error('No credentials returned!'); + } + + if (query === undefined) { + query = {}; + } + let token; + token = token = `AccessKey ${credentials.accessKey}`; + + const options: OptionsWithUri = { + headers: { + Accept: 'application/json', + Authorization: token + }, + method, + qs: query, + body, + uri: `https://rest.messagebird.com${resource}`, + json: true + }; + + try { + return await this.helpers.request(options); + } catch (error) { + if (error.statusCode === 401) { + throw new Error('The Message Bird credentials are not valid!'); + } + + if (error.response && error.response.body && error.response.body.errors) { + // Try to return the error prettier + let errorMessage; + for (let i = 0; i < error.response.body.errors.length; i++) { + errorMessage = + errorMessage + + `Message Bird Error response [${error.statusCode}]: ${error.response.body.errors[i].description}`; + } + throw new Error(errorMessage); + } + + // If that data does not exist for some reason return the actual error + throw new Error( + `Message Bird error ${error.response.body.errors[0].description}` + ); + } +} diff --git a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts new file mode 100644 index 0000000000..cb0960f1e0 --- /dev/null +++ b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts @@ -0,0 +1,354 @@ +import { IExecuteFunctions } from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType +} from 'n8n-workflow'; + +import { messageBirdApiRequest } from './GenericFunctions'; + +export class MessageBird implements INodeType { + description: INodeTypeDescription = { + displayName: 'MessageBird', + name: 'messageBird', + icon: 'file:messagebird.png', + group: ['transform'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Sending SMS', + defaults: { + name: 'MessageBird', + color: '#2481d7' + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'messageBirdApi', + required: true + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'SMS', + value: 'sms' + } + ], + default: 'sms', + description: 'The resource to operate on.' + }, + + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: ['sms'] + } + }, + options: [ + { + name: 'Send', + value: 'send', + description: 'Send text messages (SMS)' + } + ], + default: 'send', + description: 'The operation to perform.' + }, + + // ---------------------------------- + // sms:send + // ---------------------------------- + { + displayName: 'From', + name: 'originator', + type: 'string', + default: '', + placeholder: '14155238886', + required: true, + displayOptions: { + show: { + operation: ['send'], + resource: ['sms'] + } + }, + description: 'The number from which to send the message' + }, + { + displayName: 'To', + name: 'recipients', + type: 'string', + default: '', + placeholder: '14155238886/+14155238886', + required: true, + displayOptions: { + show: { + operation: ['send'], + resource: ['sms'] + } + }, + description: 'all recipients separated by commas' + }, + + { + displayName: 'Message', + name: 'message', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: ['send'], + resource: ['sms'] + } + }, + description: 'The message to be send' + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Fields', + default: {}, + options: [ + //date-time format + { + displayName: 'Created Date-time', + name: 'createdDatetime', + type: 'dateTime', + placeholder: '2011-08-30T09:30:16.768-04:00', + default: '', + description: + 'The date and time of the creation of the message in RFC3339 format (Y-m-dTH:i:sP).' + }, + { + displayName: 'Datacoding', + name: 'datacoding', + type: 'string', + default: '', + description: + 'Using unicode will limit the maximum number of characters to 70 instead of 160' + }, + { + displayName: 'Gateway', + name: 'gateway', + type: 'number', + default: '', + description: 'The SMS route that is used to send the message.' + }, + { + displayName: 'Group Ids', + name: 'groupIds', + placeholder: '1,2', + type: 'string', + default: '', + description: + 'group ids separated by commas, If provided recipients can be omitted' + }, + { + displayName: 'Mclass', + name: 'mclass', + type: 'options', + placeholder: 'permissible values from 0-3', + options: [ + { + name: '0', + value: '0' + }, + { + name: '1', + value: '1' + }, + { + name: '2', + value: '2' + }, + { + name: '3', + value: '3' + } + ], + default: '', + description: + 'Indicated the message type. 1 is a normal message, 0 is a flash message.' + }, + { + displayName: 'Reference', + name: 'reference', + type: 'string', + default: '', + description: 'A client reference.' + }, + { + displayName: 'Report Url', + name: 'reportUrl', + type: 'string', + default: '', + description: + 'The status report URL to be used on a per-message basis.
Reference is required for a status report webhook to be sent.' + }, + //date-time format + { + displayName: 'Scheduled Date-time', + name: 'scheduledDatetime', + type: 'dateTime', + default: '', + placeholder: '2011-08-30T09:30:16.768-04:00', + description: + 'The scheduled date and time of the message in RFC3339 format (Y-m-dTH:i:sP).' + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'sms', + value: 'sms' + }, + { + name: 'binary', + value: 'binary' + }, + { + name: 'flash', + value: 'flash' + } + ], + default: '', + description: + 'The type of message.
Values can be: sms, binary, or flash.' + }, + //hash + { + displayName: 'Type Details', + name: 'typeDetails', + type: 'string', + default: '', + description: + 'A hash with extra information.
Is only used when a binary message is sent.' + }, + { + displayName: 'Validity', + name: 'validity', + type: 'number', + default: '', + typeOptions: { + minValue: 1 + }, + description: 'The amount of seconds that the message is valid.' + } + ] + } + ] + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + + let operation: string; + let resource: string; + + // For POST + let bodyRequest: IDataObject; + // For Query string + let qs: IDataObject; + + let requestMethod; + + for (let i = 0; i < items.length; i++) { + qs = {}; + + resource = this.getNodeParameter('resource', i) as string; + operation = this.getNodeParameter('operation', i) as string; + + if (resource === 'sms') { + //https://developers.messagebird.com/api/sms-messaging/#sms-api + if (operation === 'send') { + // ---------------------------------- + // sms:send + // ---------------------------------- + + requestMethod = 'POST'; + const originator = this.getNodeParameter('originator', i) as string; + const body = this.getNodeParameter('message', i) as string; + + bodyRequest = { + recipients: [], + originator, + body + }; + const additionalFields = this.getNodeParameter( + 'additionalFields', + i + ) as IDataObject; + + if (additionalFields.groupIds) { + bodyRequest.groupIds = additionalFields.groupIds as string; + } + if (additionalFields.type) { + bodyRequest.type = additionalFields.type as string; + } + if (additionalFields.reference) { + bodyRequest.reference = additionalFields.reference as string; + } + if (additionalFields.reportUrl) { + bodyRequest.reportUrl = additionalFields.reportUrl as string; + } + if (additionalFields.validity) { + bodyRequest.validity = additionalFields.reportUrl as number; + } + if (additionalFields.gateway) { + bodyRequest.gateway = additionalFields.gateway as string; + } + if (additionalFields.typeDetails) { + bodyRequest.typeDetails = additionalFields.typeDetails as string; + } + if (additionalFields.datacoding) { + bodyRequest.datacoding = additionalFields.datacoding as string; + } + if (additionalFields.mclass) { + bodyRequest.mclass = additionalFields.mclass as number; + } + if (additionalFields.scheduledDatetime) { + bodyRequest.scheduledDatetime = additionalFields.scheduledDatetime as string; + } + if (additionalFields.createdDatetime) { + bodyRequest.createdDatetime = additionalFields.createdDatetime as string; + } + + const receivers = this.getNodeParameter('recipients', i) as string; + + bodyRequest.recipients = receivers.split(',').map(item => { + return parseInt(item, 10); + }); + } else { + throw new Error(`The operation "${operation}" is not known!`); + } + } else { + throw new Error(`The resource "${resource}" is not known!`); + } + + const responseData = await messageBirdApiRequest.call( + this, + requestMethod, + '/messages', + bodyRequest, + qs + ); + + returnData.push(responseData as IDataObject); + } + + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/MessageBird/messagebird.png b/packages/nodes-base/nodes/MessageBird/messagebird.png new file mode 100644 index 0000000000000000000000000000000000000000..006b762950d6604ab0e58a3ebf90e984f04907f6 GIT binary patch literal 1305 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xg+bI6B3hk`{3u+q^6EQYb$Fqb0`Z}TW&b_ z-}^zkqPOjSoVabPclqS!dv2C(uD$;xw(Q>QzqM&~pR4cBRJYJmeeF@1E!)8ShQay( z-v%)C+MTap-D$>irE9Gdwtt&E^F~OQ4%<4-R^B*+t+V@d4m{vFV|PYphuMeSz6BZV zI!Dg3d@^9G{r=#y+z-Pktq1y_Hm^UBV~`uYa2pR_*`cd~Mn;z-!?$b;KXCBv%-8YN zZM*-o^>ZglY3epIiN9If%Im6qd;Thh?b;h-R8F#9dM1*|JbxG0@^vr%7oJwj;9Zg@ zJ=2?KNy{stibCNK(Zg#!e`vA<3vRg_D_!w_@`WeuGu)4ockQ1YS7^3>3%e1zH0^7I z`J9~Q>DwI^+cU0F+$*a1@Y#~e8}|}3{)^sWvdd_Tz543=^FJT1#;wq465jjaEyLxzEI2Nx`2e|Nhi>6UA@9h-Qwmce%K z$TJ~@9u6EC>zZ_zuKfM#C;PrVJa+st)sCVo-LHOB|I&Z6c`NIl71yLJZt=9fyRt88 zQP9q1B3fMG+s?Z_tk7>sd+_*qV*K4x)yEDMq$L!7xh52G;Cu6h2jMTH7A2Y{2T9Ig z_CI`^H>*G{^uFy5vpo50b_Q8(cE4TMzWhGxiuU2hoBq{!Hm*A0wB-A<=kE?MJMY%G zeSqPj#hiT$eGa~3*dr1!@5Xb_zQ9FYceZ_ck`?>P^Zxe;2|b=!9YLNFf9>Xc*sJ8f z_9avJr(J!EeEc!EdYbA>n4Lk*jUt-nsCK2}_KiCcQ_ zwcR%5$KNb$hpVD{F0v+cN64?YdVdmE zSm~eo!`Kkg_J1y0^#<<37d!Yie@;7W%&oI3OqSP5I||Jl(eT z+b+pn{PRl=S4cd59`&qYY1LJkJfHo_n-)#9toW}~(t74_)5=9#ZKisE*}c<{(S7#< zW9F4PA`u)F+3I_OR)7CfAz_v8_&fDS?UXk*9r2S|WThW|t$Nz_NUSDWXWctqztxw| zZ{EJ}{kmI4X}^lQ1+ybxwlZPW)%M51ZrULboFyt I=akR{0A)r^>i_@% literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 822978eaf4..37b9adecf2 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,363 +1,365 @@ { - "name": "n8n-nodes-base", - "version": "0.64.1", - "description": "Base nodes of n8n", - "license": "SEE LICENSE IN LICENSE.md", - "homepage": "https://n8n.io", - "author": { - "name": "Jan Oberhauser", - "email": "jan@n8n.io" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/n8n-io/n8n.git" - }, - "main": "dist/src/index", - "types": "dist/src/index.d.ts", - "scripts": { - "dev": "npm run watch", - "build": "tsc && gulp", - "tslint": "tslint -p tsconfig.json -c tslint.json", - "watch": "tsc --watch", - "test": "jest" - }, - "files": [ - "dist" + "name": "n8n-nodes-base", + "version": "0.64.1", + "description": "Base nodes of n8n", + "license": "SEE LICENSE IN LICENSE.md", + "homepage": "https://n8n.io", + "author": { + "name": "Jan Oberhauser", + "email": "jan@n8n.io" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/n8n-io/n8n.git" + }, + "main": "dist/src/index", + "types": "dist/src/index.d.ts", + "scripts": { + "dev": "npm run watch", + "build": "tsc && gulp", + "tslint": "tslint -p tsconfig.json -c tslint.json", + "watch": "tsc --watch", + "test": "jest" + }, + "files": [ + "dist" + ], + "n8n": { + "credentials": [ + "dist/credentials/ActiveCampaignApi.credentials.js", + "dist/credentials/AgileCrmApi.credentials.js", + "dist/credentials/AcuitySchedulingApi.credentials.js", + "dist/credentials/AirtableApi.credentials.js", + "dist/credentials/Amqp.credentials.js", + "dist/credentials/AsanaApi.credentials.js", + "dist/credentials/Aws.credentials.js", + "dist/credentials/AffinityApi.credentials.js", + "dist/credentials/BannerbearApi.credentials.js", + "dist/credentials/BitbucketApi.credentials.js", + "dist/credentials/BitlyApi.credentials.js", + "dist/credentials/ChargebeeApi.credentials.js", + "dist/credentials/ClearbitApi.credentials.js", + "dist/credentials/ClickUpApi.credentials.js", + "dist/credentials/ClockifyApi.credentials.js", + "dist/credentials/CockpitApi.credentials.js", + "dist/credentials/CodaApi.credentials.js", + "dist/credentials/CopperApi.credentials.js", + "dist/credentials/CalendlyApi.credentials.js", + "dist/credentials/DisqusApi.credentials.js", + "dist/credentials/DriftApi.credentials.js", + "dist/credentials/DropboxApi.credentials.js", + "dist/credentials/EventbriteApi.credentials.js", + "dist/credentials/FacebookGraphApi.credentials.js", + "dist/credentials/FreshdeskApi.credentials.js", + "dist/credentials/FileMaker.credentials.js", + "dist/credentials/FlowApi.credentials.js", + "dist/credentials/GithubApi.credentials.js", + "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", + "dist/credentials/HttpBasicAuth.credentials.js", + "dist/credentials/HttpDigestAuth.credentials.js", + "dist/credentials/HttpHeaderAuth.credentials.js", + "dist/credentials/HubspotApi.credentials.js", + "dist/credentials/HubspotDeveloperApi.credentials.js", + "dist/credentials/HunterApi.credentials.js", + "dist/credentials/Imap.credentials.js", + "dist/credentials/IntercomApi.credentials.js", + "dist/credentials/InvoiceNinjaApi.credentials.js", + "dist/credentials/JiraSoftwareCloudApi.credentials.js", + "dist/credentials/JiraSoftwareServerApi.credentials.js", + "dist/credentials/JotFormApi.credentials.js", + "dist/credentials/KeapOAuth2Api.credentials.js", + "dist/credentials/LinkFishApi.credentials.js", + "dist/credentials/MailchimpApi.credentials.js", + "dist/credentials/MailgunApi.credentials.js", + "dist/credentials/MailjetEmailApi.credentials.js", + "dist/credentials/MailjetSmsApi.credentials.js", + "dist/credentials/MandrillApi.credentials.js", + "dist/credentials/MattermostApi.credentials.js", + "dist/credentials/MauticApi.credentials.js", + "dist/credentials/MessageBirdApi.credentials.js", + "dist/credentials/MicrosoftExcelOAuth2Api.credentials.js", + "dist/credentials/MicrosoftOAuth2Api.credentials.js", + "dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js", + "dist/credentials/MoceanApi.credentials.js", + "dist/credentials/MondayComApi.credentials.js", + "dist/credentials/MongoDb.credentials.js", + "dist/credentials/Msg91Api.credentials.js", + "dist/credentials/MySql.credentials.js", + "dist/credentials/NextCloudApi.credentials.js", + "dist/credentials/OAuth1Api.credentials.js", + "dist/credentials/OAuth2Api.credentials.js", + "dist/credentials/OpenWeatherMapApi.credentials.js", + "dist/credentials/PagerDutyApi.credentials.js", + "dist/credentials/PayPalApi.credentials.js", + "dist/credentials/PipedriveApi.credentials.js", + "dist/credentials/Postgres.credentials.js", + "dist/credentials/Redis.credentials.js", + "dist/credentials/RocketchatApi.credentials.js", + "dist/credentials/RundeckApi.credentials.js", + "dist/credentials/ShopifyApi.credentials.js", + "dist/credentials/SalesforceOAuth2Api.credentials.js", + "dist/credentials/SlackApi.credentials.js", + "dist/credentials/SlackOAuth2Api.credentials.js", + "dist/credentials/Sms77Api.credentials.js", + "dist/credentials/Smtp.credentials.js", + "dist/credentials/StripeApi.credentials.js", + "dist/credentials/SalesmateApi.credentials.js", + "dist/credentials/SegmentApi.credentials.js", + "dist/credentials/SurveyMonkeyApi.credentials.js", + "dist/credentials/TelegramApi.credentials.js", + "dist/credentials/TodoistApi.credentials.js", + "dist/credentials/TrelloApi.credentials.js", + "dist/credentials/TwilioApi.credentials.js", + "dist/credentials/TwitterOAuth1Api.credentials.js", + "dist/credentials/TypeformApi.credentials.js", + "dist/credentials/TogglApi.credentials.js", + "dist/credentials/UpleadApi.credentials.js", + "dist/credentials/VeroApi.credentials.js", + "dist/credentials/WebflowApi.credentials.js", + "dist/credentials/WooCommerceApi.credentials.js", + "dist/credentials/WordpressApi.credentials.js", + "dist/credentials/ZendeskApi.credentials.js", + "dist/credentials/ZohoOAuth2Api.credentials.js", + "dist/credentials/ZulipApi.credentials.js" ], - "n8n": { - "credentials": [ - "dist/credentials/ActiveCampaignApi.credentials.js", - "dist/credentials/AgileCrmApi.credentials.js", - "dist/credentials/AcuitySchedulingApi.credentials.js", - "dist/credentials/AirtableApi.credentials.js", - "dist/credentials/Amqp.credentials.js", - "dist/credentials/AsanaApi.credentials.js", - "dist/credentials/Aws.credentials.js", - "dist/credentials/AffinityApi.credentials.js", - "dist/credentials/BannerbearApi.credentials.js", - "dist/credentials/BitbucketApi.credentials.js", - "dist/credentials/BitlyApi.credentials.js", - "dist/credentials/ChargebeeApi.credentials.js", - "dist/credentials/ClearbitApi.credentials.js", - "dist/credentials/ClickUpApi.credentials.js", - "dist/credentials/ClockifyApi.credentials.js", - "dist/credentials/CockpitApi.credentials.js", - "dist/credentials/CodaApi.credentials.js", - "dist/credentials/CopperApi.credentials.js", - "dist/credentials/CalendlyApi.credentials.js", - "dist/credentials/DisqusApi.credentials.js", - "dist/credentials/DriftApi.credentials.js", - "dist/credentials/DropboxApi.credentials.js", - "dist/credentials/EventbriteApi.credentials.js", - "dist/credentials/FacebookGraphApi.credentials.js", - "dist/credentials/FreshdeskApi.credentials.js", - "dist/credentials/FileMaker.credentials.js", - "dist/credentials/FlowApi.credentials.js", - "dist/credentials/GithubApi.credentials.js", - "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", - "dist/credentials/HttpBasicAuth.credentials.js", - "dist/credentials/HttpDigestAuth.credentials.js", - "dist/credentials/HttpHeaderAuth.credentials.js", - "dist/credentials/HubspotApi.credentials.js", - "dist/credentials/HubspotDeveloperApi.credentials.js", - "dist/credentials/HunterApi.credentials.js", - "dist/credentials/Imap.credentials.js", - "dist/credentials/IntercomApi.credentials.js", - "dist/credentials/InvoiceNinjaApi.credentials.js", - "dist/credentials/JiraSoftwareCloudApi.credentials.js", - "dist/credentials/JiraSoftwareServerApi.credentials.js", - "dist/credentials/JotFormApi.credentials.js", - "dist/credentials/KeapOAuth2Api.credentials.js", - "dist/credentials/LinkFishApi.credentials.js", - "dist/credentials/MailchimpApi.credentials.js", - "dist/credentials/MailgunApi.credentials.js", - "dist/credentials/MailjetEmailApi.credentials.js", - "dist/credentials/MailjetSmsApi.credentials.js", - "dist/credentials/MandrillApi.credentials.js", - "dist/credentials/MattermostApi.credentials.js", - "dist/credentials/MauticApi.credentials.js", - "dist/credentials/MicrosoftExcelOAuth2Api.credentials.js", - "dist/credentials/MicrosoftOAuth2Api.credentials.js", - "dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js", - "dist/credentials/MoceanApi.credentials.js", - "dist/credentials/MondayComApi.credentials.js", - "dist/credentials/MongoDb.credentials.js", - "dist/credentials/Msg91Api.credentials.js", - "dist/credentials/MySql.credentials.js", - "dist/credentials/NextCloudApi.credentials.js", - "dist/credentials/OAuth1Api.credentials.js", - "dist/credentials/OAuth2Api.credentials.js", - "dist/credentials/OpenWeatherMapApi.credentials.js", - "dist/credentials/PagerDutyApi.credentials.js", - "dist/credentials/PayPalApi.credentials.js", - "dist/credentials/PipedriveApi.credentials.js", - "dist/credentials/Postgres.credentials.js", - "dist/credentials/Redis.credentials.js", - "dist/credentials/RocketchatApi.credentials.js", - "dist/credentials/RundeckApi.credentials.js", - "dist/credentials/ShopifyApi.credentials.js", - "dist/credentials/SalesforceOAuth2Api.credentials.js", - "dist/credentials/SlackApi.credentials.js", - "dist/credentials/SlackOAuth2Api.credentials.js", - "dist/credentials/Sms77Api.credentials.js", - "dist/credentials/Smtp.credentials.js", - "dist/credentials/StripeApi.credentials.js", - "dist/credentials/SalesmateApi.credentials.js", - "dist/credentials/SegmentApi.credentials.js", - "dist/credentials/SurveyMonkeyApi.credentials.js", - "dist/credentials/TelegramApi.credentials.js", - "dist/credentials/TodoistApi.credentials.js", - "dist/credentials/TrelloApi.credentials.js", - "dist/credentials/TwilioApi.credentials.js", - "dist/credentials/TwitterOAuth1Api.credentials.js", - "dist/credentials/TypeformApi.credentials.js", - "dist/credentials/TogglApi.credentials.js", - "dist/credentials/UpleadApi.credentials.js", - "dist/credentials/VeroApi.credentials.js", - "dist/credentials/WebflowApi.credentials.js", - "dist/credentials/WooCommerceApi.credentials.js", - "dist/credentials/WordpressApi.credentials.js", - "dist/credentials/ZendeskApi.credentials.js", - "dist/credentials/ZohoOAuth2Api.credentials.js", - "dist/credentials/ZulipApi.credentials.js" - ], - "nodes": [ - "dist/nodes/ActiveCampaign/ActiveCampaign.node.js", - "dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js", - "dist/nodes/AgileCrm/AgileCrm.node.js", - "dist/nodes/Airtable/Airtable.node.js", - "dist/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.js", - "dist/nodes/Amqp/Amqp.node.js", - "dist/nodes/Amqp/AmqpTrigger.node.js", - "dist/nodes/Asana/Asana.node.js", - "dist/nodes/Asana/AsanaTrigger.node.js", - "dist/nodes/Affinity/Affinity.node.js", - "dist/nodes/Affinity/AffinityTrigger.node.js", - "dist/nodes/Aws/AwsLambda.node.js", - "dist/nodes/Aws/S3/AwsS3.node.js", - "dist/nodes/Aws/AwsSes.node.js", - "dist/nodes/Aws/AwsSns.node.js", - "dist/nodes/Aws/AwsSnsTrigger.node.js", - "dist/nodes/Bannerbear/Bannerbear.node.js", - "dist/nodes/Bitbucket/BitbucketTrigger.node.js", - "dist/nodes/Bitly/Bitly.node.js", - "dist/nodes/Calendly/CalendlyTrigger.node.js", - "dist/nodes/Chargebee/Chargebee.node.js", - "dist/nodes/Chargebee/ChargebeeTrigger.node.js", - "dist/nodes/Clearbit/Clearbit.node.js", - "dist/nodes/ClickUp/ClickUp.node.js", - "dist/nodes/ClickUp/ClickUpTrigger.node.js", - "dist/nodes/Clockify/ClockifyTrigger.node.js", - "dist/nodes/Cockpit/Cockpit.node.js", - "dist/nodes/Coda/Coda.node.js", - "dist/nodes/Copper/CopperTrigger.node.js", - "dist/nodes/Cron.node.js", - "dist/nodes/Crypto.node.js", - "dist/nodes/DateTime.node.js", - "dist/nodes/Discord/Discord.node.js", - "dist/nodes/Disqus/Disqus.node.js", - "dist/nodes/Drift/Drift.node.js", - "dist/nodes/Dropbox/Dropbox.node.js", - "dist/nodes/EditImage.node.js", - "dist/nodes/EmailReadImap.node.js", - "dist/nodes/EmailSend.node.js", - "dist/nodes/ErrorTrigger.node.js", - "dist/nodes/Eventbrite/EventbriteTrigger.node.js", - "dist/nodes/ExecuteCommand.node.js", - "dist/nodes/ExecuteWorkflow.node.js", - "dist/nodes/Facebook/FacebookGraphApi.node.js", - "dist/nodes/FileMaker/FileMaker.node.js", - "dist/nodes/Freshdesk/Freshdesk.node.js", - "dist/nodes/Flow/Flow.node.js", - "dist/nodes/Flow/FlowTrigger.node.js", - "dist/nodes/Function.node.js", - "dist/nodes/FunctionItem.node.js", - "dist/nodes/Github/Github.node.js", - "dist/nodes/Github/GithubTrigger.node.js", - "dist/nodes/Gitlab/Gitlab.node.js", - "dist/nodes/Gitlab/GitlabTrigger.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", - "dist/nodes/HelpScout/HelpScout.node.js", - "dist/nodes/HelpScout/HelpScoutTrigger.node.js", - "dist/nodes/HtmlExtract/HtmlExtract.node.js", - "dist/nodes/HttpRequest.node.js", - "dist/nodes/Hubspot/Hubspot.node.js", - "dist/nodes/Hubspot/HubspotTrigger.node.js", - "dist/nodes/Hunter/Hunter.node.js", - "dist/nodes/If.node.js", - "dist/nodes/Intercom/Intercom.node.js", - "dist/nodes/Interval.node.js", - "dist/nodes/InvoiceNinja/InvoiceNinja.node.js", - "dist/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.js", - "dist/nodes/Jira/Jira.node.js", - "dist/nodes/JotForm/JotFormTrigger.node.js", - "dist/nodes/Keap/Keap.node.js", - "dist/nodes/Keap/KeapTrigger.node.js", - "dist/nodes/LinkFish/LinkFish.node.js", - "dist/nodes/Mailchimp/Mailchimp.node.js", - "dist/nodes/Mailchimp/MailchimpTrigger.node.js", - "dist/nodes/Mailgun/Mailgun.node.js", - "dist/nodes/Mailjet/Mailjet.node.js", - "dist/nodes/Mailjet/MailjetTrigger.node.js", - "dist/nodes/Mandrill/Mandrill.node.js", - "dist/nodes/Mattermost/Mattermost.node.js", - "dist/nodes/Mautic/Mautic.node.js", - "dist/nodes/Mautic/MauticTrigger.node.js", - "dist/nodes/Merge.node.js", - "dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js", - "dist/nodes/Microsoft/OneDrive/MicrosoftOneDrive.node.js", - "dist/nodes/MoveBinaryData.node.js", - "dist/nodes/Mocean/Mocean.node.js", - "dist/nodes/MondayCom/MondayCom.node.js", - "dist/nodes/MongoDb/MongoDb.node.js", - "dist/nodes/MoveBinaryData.node.js", - "dist/nodes/Msg91/Msg91.node.js", - "dist/nodes/MySql/MySql.node.js", - "dist/nodes/NextCloud/NextCloud.node.js", - "dist/nodes/NoOp.node.js", - "dist/nodes/OpenWeatherMap.node.js", - "dist/nodes/PagerDuty/PagerDuty.node.js", - "dist/nodes/PayPal/PayPal.node.js", - "dist/nodes/PayPal/PayPalTrigger.node.js", - "dist/nodes/Pipedrive/Pipedrive.node.js", - "dist/nodes/Pipedrive/PipedriveTrigger.node.js", - "dist/nodes/Postgres/Postgres.node.js", - "dist/nodes/ReadBinaryFile.node.js", - "dist/nodes/ReadBinaryFiles.node.js", - "dist/nodes/ReadPdf.node.js", - "dist/nodes/Redis/Redis.node.js", - "dist/nodes/RenameKeys.node.js", - "dist/nodes/Rocketchat/Rocketchat.node.js", - "dist/nodes/RssFeedRead.node.js", - "dist/nodes/Rundeck/Rundeck.node.js", - "dist/nodes/Salesforce/Salesforce.node.js", - "dist/nodes/Set.node.js", - "dist/nodes/Shopify/Shopify.node.js", - "dist/nodes/Shopify/ShopifyTrigger.node.js", - "dist/nodes/Slack/Slack.node.js", - "dist/nodes/Sms77/Sms77.node.js", - "dist/nodes/SplitInBatches.node.js", - "dist/nodes/SpreadsheetFile.node.js", - "dist/nodes/SseTrigger.node.js", - "dist/nodes/Start.node.js", - "dist/nodes/Stripe/StripeTrigger.node.js", - "dist/nodes/Switch.node.js", - "dist/nodes/Salesmate/Salesmate.node.js", - "dist/nodes/Segment/Segment.node.js", - "dist/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.js", - "dist/nodes/Telegram/Telegram.node.js", - "dist/nodes/Telegram/TelegramTrigger.node.js", - "dist/nodes/Todoist/Todoist.node.js", - "dist/nodes/Toggl/TogglTrigger.node.js", - "dist/nodes/Trello/Trello.node.js", - "dist/nodes/Trello/TrelloTrigger.node.js", - "dist/nodes/Twilio/Twilio.node.js", - "dist/nodes/Twitter/Twitter.node.js", - "dist/nodes/Typeform/TypeformTrigger.node.js", - "dist/nodes/Uplead/Uplead.node.js", - "dist/nodes/Vero/Vero.node.js", - "dist/nodes/Webflow/WebflowTrigger.node.js", - "dist/nodes/Webhook.node.js", - "dist/nodes/Wordpress/Wordpress.node.js", - "dist/nodes/WooCommerce/WooCommerce.node.js", - "dist/nodes/WooCommerce/WooCommerceTrigger.node.js", - "dist/nodes/WriteBinaryFile.node.js", - "dist/nodes/Xml.node.js", - "dist/nodes/Zendesk/Zendesk.node.js", - "dist/nodes/Zendesk/ZendeskTrigger.node.js", - "dist/nodes/Zoho/ZohoCrm.node.js", - "dist/nodes/Zulip/Zulip.node.js" - ] + "nodes": [ + "dist/nodes/ActiveCampaign/ActiveCampaign.node.js", + "dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js", + "dist/nodes/AgileCrm/AgileCrm.node.js", + "dist/nodes/Airtable/Airtable.node.js", + "dist/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.js", + "dist/nodes/Amqp/Amqp.node.js", + "dist/nodes/Amqp/AmqpTrigger.node.js", + "dist/nodes/Asana/Asana.node.js", + "dist/nodes/Asana/AsanaTrigger.node.js", + "dist/nodes/Affinity/Affinity.node.js", + "dist/nodes/Affinity/AffinityTrigger.node.js", + "dist/nodes/Aws/AwsLambda.node.js", + "dist/nodes/Aws/S3/AwsS3.node.js", + "dist/nodes/Aws/AwsSes.node.js", + "dist/nodes/Aws/AwsSns.node.js", + "dist/nodes/Aws/AwsSnsTrigger.node.js", + "dist/nodes/Bannerbear/Bannerbear.node.js", + "dist/nodes/Bitbucket/BitbucketTrigger.node.js", + "dist/nodes/Bitly/Bitly.node.js", + "dist/nodes/Calendly/CalendlyTrigger.node.js", + "dist/nodes/Chargebee/Chargebee.node.js", + "dist/nodes/Chargebee/ChargebeeTrigger.node.js", + "dist/nodes/Clearbit/Clearbit.node.js", + "dist/nodes/ClickUp/ClickUp.node.js", + "dist/nodes/ClickUp/ClickUpTrigger.node.js", + "dist/nodes/Clockify/ClockifyTrigger.node.js", + "dist/nodes/Cockpit/Cockpit.node.js", + "dist/nodes/Coda/Coda.node.js", + "dist/nodes/Copper/CopperTrigger.node.js", + "dist/nodes/Cron.node.js", + "dist/nodes/Crypto.node.js", + "dist/nodes/DateTime.node.js", + "dist/nodes/Discord/Discord.node.js", + "dist/nodes/Disqus/Disqus.node.js", + "dist/nodes/Drift/Drift.node.js", + "dist/nodes/Dropbox/Dropbox.node.js", + "dist/nodes/EditImage.node.js", + "dist/nodes/EmailReadImap.node.js", + "dist/nodes/EmailSend.node.js", + "dist/nodes/ErrorTrigger.node.js", + "dist/nodes/Eventbrite/EventbriteTrigger.node.js", + "dist/nodes/ExecuteCommand.node.js", + "dist/nodes/ExecuteWorkflow.node.js", + "dist/nodes/Facebook/FacebookGraphApi.node.js", + "dist/nodes/FileMaker/FileMaker.node.js", + "dist/nodes/Freshdesk/Freshdesk.node.js", + "dist/nodes/Flow/Flow.node.js", + "dist/nodes/Flow/FlowTrigger.node.js", + "dist/nodes/Function.node.js", + "dist/nodes/FunctionItem.node.js", + "dist/nodes/Github/Github.node.js", + "dist/nodes/Github/GithubTrigger.node.js", + "dist/nodes/Gitlab/Gitlab.node.js", + "dist/nodes/Gitlab/GitlabTrigger.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", + "dist/nodes/HelpScout/HelpScout.node.js", + "dist/nodes/HelpScout/HelpScoutTrigger.node.js", + "dist/nodes/HtmlExtract/HtmlExtract.node.js", + "dist/nodes/HttpRequest.node.js", + "dist/nodes/Hubspot/Hubspot.node.js", + "dist/nodes/Hubspot/HubspotTrigger.node.js", + "dist/nodes/Hunter/Hunter.node.js", + "dist/nodes/If.node.js", + "dist/nodes/Intercom/Intercom.node.js", + "dist/nodes/Interval.node.js", + "dist/nodes/InvoiceNinja/InvoiceNinja.node.js", + "dist/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.js", + "dist/nodes/Jira/Jira.node.js", + "dist/nodes/JotForm/JotFormTrigger.node.js", + "dist/nodes/Keap/Keap.node.js", + "dist/nodes/Keap/KeapTrigger.node.js", + "dist/nodes/LinkFish/LinkFish.node.js", + "dist/nodes/Mailchimp/Mailchimp.node.js", + "dist/nodes/Mailchimp/MailchimpTrigger.node.js", + "dist/nodes/Mailgun/Mailgun.node.js", + "dist/nodes/Mailjet/Mailjet.node.js", + "dist/nodes/Mailjet/MailjetTrigger.node.js", + "dist/nodes/Mandrill/Mandrill.node.js", + "dist/nodes/Mattermost/Mattermost.node.js", + "dist/nodes/Mautic/Mautic.node.js", + "dist/nodes/Mautic/MauticTrigger.node.js", + "dist/nodes/Merge.node.js", + "dist/nodes/MessageBird/MessageBird.node.js", + "dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js", + "dist/nodes/Microsoft/OneDrive/MicrosoftOneDrive.node.js", + "dist/nodes/MoveBinaryData.node.js", + "dist/nodes/Mocean/Mocean.node.js", + "dist/nodes/MondayCom/MondayCom.node.js", + "dist/nodes/MongoDb/MongoDb.node.js", + "dist/nodes/MoveBinaryData.node.js", + "dist/nodes/Msg91/Msg91.node.js", + "dist/nodes/MySql/MySql.node.js", + "dist/nodes/NextCloud/NextCloud.node.js", + "dist/nodes/NoOp.node.js", + "dist/nodes/OpenWeatherMap.node.js", + "dist/nodes/PagerDuty/PagerDuty.node.js", + "dist/nodes/PayPal/PayPal.node.js", + "dist/nodes/PayPal/PayPalTrigger.node.js", + "dist/nodes/Pipedrive/Pipedrive.node.js", + "dist/nodes/Pipedrive/PipedriveTrigger.node.js", + "dist/nodes/Postgres/Postgres.node.js", + "dist/nodes/ReadBinaryFile.node.js", + "dist/nodes/ReadBinaryFiles.node.js", + "dist/nodes/ReadPdf.node.js", + "dist/nodes/Redis/Redis.node.js", + "dist/nodes/RenameKeys.node.js", + "dist/nodes/Rocketchat/Rocketchat.node.js", + "dist/nodes/RssFeedRead.node.js", + "dist/nodes/Rundeck/Rundeck.node.js", + "dist/nodes/Salesforce/Salesforce.node.js", + "dist/nodes/Set.node.js", + "dist/nodes/Shopify/Shopify.node.js", + "dist/nodes/Shopify/ShopifyTrigger.node.js", + "dist/nodes/Slack/Slack.node.js", + "dist/nodes/Sms77/Sms77.node.js", + "dist/nodes/SplitInBatches.node.js", + "dist/nodes/SpreadsheetFile.node.js", + "dist/nodes/SseTrigger.node.js", + "dist/nodes/Start.node.js", + "dist/nodes/Stripe/StripeTrigger.node.js", + "dist/nodes/Switch.node.js", + "dist/nodes/Salesmate/Salesmate.node.js", + "dist/nodes/Segment/Segment.node.js", + "dist/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.js", + "dist/nodes/Telegram/Telegram.node.js", + "dist/nodes/Telegram/TelegramTrigger.node.js", + "dist/nodes/Todoist/Todoist.node.js", + "dist/nodes/Toggl/TogglTrigger.node.js", + "dist/nodes/Trello/Trello.node.js", + "dist/nodes/Trello/TrelloTrigger.node.js", + "dist/nodes/Twilio/Twilio.node.js", + "dist/nodes/Twitter/Twitter.node.js", + "dist/nodes/Typeform/TypeformTrigger.node.js", + "dist/nodes/Uplead/Uplead.node.js", + "dist/nodes/Vero/Vero.node.js", + "dist/nodes/Webflow/WebflowTrigger.node.js", + "dist/nodes/Webhook.node.js", + "dist/nodes/Wordpress/Wordpress.node.js", + "dist/nodes/WooCommerce/WooCommerce.node.js", + "dist/nodes/WooCommerce/WooCommerceTrigger.node.js", + "dist/nodes/WriteBinaryFile.node.js", + "dist/nodes/Xml.node.js", + "dist/nodes/Zendesk/Zendesk.node.js", + "dist/nodes/Zendesk/ZendeskTrigger.node.js", + "dist/nodes/Zoho/ZohoCrm.node.js", + "dist/nodes/Zulip/Zulip.node.js" + ] + }, + "devDependencies": { + "@types/aws4": "^1.5.1", + "@types/basic-auth": "^1.1.2", + "@types/cheerio": "^0.22.15", + "@types/cron": "^1.6.1", + "@types/eventsource": "^1.1.2", + "@types/express": "^4.16.1", + "@types/formidable": "^1.0.31", + "@types/gm": "^1.18.2", + "@types/imap-simple": "^4.2.0", + "@types/jest": "^24.0.18", + "@types/lodash.set": "^4.3.6", + "@types/moment-timezone": "^0.5.12", + "@types/mongodb": "^3.5.4", + "@types/node": "^10.10.1", + "@types/nodemailer": "^6.4.0", + "@types/redis": "^2.8.11", + "@types/request-promise-native": "~1.0.15", + "@types/uuid": "^3.4.6", + "@types/xml2js": "^0.4.3", + "gulp": "^4.0.0", + "jest": "^24.9.0", + "n8n-workflow": "~0.32.0", + "ts-jest": "^24.0.2", + "tslint": "^5.17.0", + "typescript": "~3.7.4" + }, + "dependencies": { + "aws4": "^1.8.0", + "basic-auth": "^2.0.1", + "change-case": "^4.1.1", + "cheerio": "^1.0.0-rc.3", + "cron": "^1.7.2", + "eventsource": "^1.0.7", + "formidable": "^1.2.1", + "glob-promise": "^3.4.0", + "gm": "^1.23.1", + "googleapis": "~50.0.0", + "imap-simple": "^4.3.0", + "iso-639-1": "^2.1.3", + "jsonwebtoken": "^8.5.1", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.unset": "^4.5.2", + "moment": "2.24.0", + "moment-timezone": "^0.5.28", + "mongodb": "^3.5.5", + "mysql2": "^2.0.1", + "n8n-core": "~0.36.0", + "nodemailer": "^6.4.6", + "pdf-parse": "^1.1.1", + "pg-promise": "^9.0.3", + "redis": "^2.8.0", + "request": "^2.88.2", + "rhea": "^1.0.11", + "rss-parser": "^3.7.0", + "uuid": "^3.4.0", + "vm2": "^3.6.10", + "xlsx": "^0.14.3", + "xml2js": "^0.4.22" + }, + "jest": { + "transform": { + "^.+\\.tsx?$": "ts-jest" }, - "devDependencies": { - "@types/aws4": "^1.5.1", - "@types/basic-auth": "^1.1.2", - "@types/cheerio": "^0.22.15", - "@types/cron": "^1.6.1", - "@types/eventsource": "^1.1.2", - "@types/express": "^4.16.1", - "@types/formidable": "^1.0.31", - "@types/gm": "^1.18.2", - "@types/imap-simple": "^4.2.0", - "@types/jest": "^24.0.18", - "@types/lodash.set": "^4.3.6", - "@types/moment-timezone": "^0.5.12", - "@types/mongodb": "^3.5.4", - "@types/node": "^10.10.1", - "@types/nodemailer": "^6.4.0", - "@types/redis": "^2.8.11", - "@types/request-promise-native": "~1.0.15", - "@types/uuid": "^3.4.6", - "@types/xml2js": "^0.4.3", - "gulp": "^4.0.0", - "jest": "^24.9.0", - "n8n-workflow": "~0.32.0", - "ts-jest": "^24.0.2", - "tslint": "^5.17.0", - "typescript": "~3.7.4" - }, - "dependencies": { - "aws4": "^1.8.0", - "basic-auth": "^2.0.1", - "change-case": "^4.1.1", - "cheerio": "^1.0.0-rc.3", - "cron": "^1.7.2", - "eventsource": "^1.0.7", - "formidable": "^1.2.1", - "glob-promise": "^3.4.0", - "gm": "^1.23.1", - "googleapis": "~50.0.0", - "imap-simple": "^4.3.0", - "iso-639-1": "^2.1.3", - "jsonwebtoken": "^8.5.1", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.unset": "^4.5.2", - "moment": "2.24.0", - "moment-timezone": "^0.5.28", - "mongodb": "^3.5.5", - "mysql2": "^2.0.1", - "n8n-core": "~0.36.0", - "nodemailer": "^6.4.6", - "pdf-parse": "^1.1.1", - "pg-promise": "^9.0.3", - "redis": "^2.8.0", - "request": "^2.88.2", - "rhea": "^1.0.11", - "rss-parser": "^3.7.0", - "uuid": "^3.4.0", - "vm2": "^3.6.10", - "xlsx": "^0.14.3", - "xml2js": "^0.4.22" - }, - "jest": { - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testURL": "http://localhost/", - "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", - "testPathIgnorePatterns": [ - "/dist/", - "/node_modules/" - ], - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ] - } + "testURL": "http://localhost/", + "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", + "testPathIgnorePatterns": [ + "/dist/", + "/node_modules/" + ], + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ] + } } From edd4c7a82f568decbcf2891dd62c0b17015feefe Mon Sep 17 00:00:00 2001 From: ricardo Date: Thu, 11 Jun 2020 20:40:58 -0400 Subject: [PATCH 54/71] :zap: Small improvements to MessageBird node --- .../nodes/MessageBird/GenericFunctions.ts | 38 +++-- .../nodes/MessageBird/MessageBird.node.ts | 134 ++++++++++-------- 2 files changed, 88 insertions(+), 84 deletions(-) diff --git a/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts b/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts index ff1df3b265..ee790863d7 100644 --- a/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts +++ b/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts @@ -1,7 +1,15 @@ -import { IExecuteFunctions, IHookFunctions } from 'n8n-core'; -import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IHookFunctions, + } from 'n8n-core'; -import { IDataObject } from 'n8n-workflow'; +import { + OptionsWithUri, + } from 'request'; + +import { + IDataObject, + } from 'n8n-workflow'; /** * Make an API request to Message Bird @@ -17,23 +25,17 @@ export async function messageBirdApiRequest( method: string, resource: string, body: IDataObject, - query?: IDataObject + query: IDataObject = {}, ): Promise { const credentials = this.getCredentials('messageBirdApi'); if (credentials === undefined) { throw new Error('No credentials returned!'); } - if (query === undefined) { - query = {}; - } - let token; - token = token = `AccessKey ${credentials.accessKey}`; - const options: OptionsWithUri = { headers: { Accept: 'application/json', - Authorization: token + Authorization: `AccessKey ${credentials.accessKey}`, }, method, qs: query, @@ -51,18 +53,12 @@ export async function messageBirdApiRequest( if (error.response && error.response.body && error.response.body.errors) { // Try to return the error prettier - let errorMessage; - for (let i = 0; i < error.response.body.errors.length; i++) { - errorMessage = - errorMessage + - `Message Bird Error response [${error.statusCode}]: ${error.response.body.errors[i].description}`; - } - throw new Error(errorMessage); + const errorMessage = error.response.body.errors.map((e: IDataObject) => e.description); + + throw new Error(`MessageBird Error response [${error.statusCode}]: ${errorMessage.join('|')}`); } // If that data does not exist for some reason return the actual error - throw new Error( - `Message Bird error ${error.response.body.errors[0].description}` - ); + throw error; } } diff --git a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts index cb0960f1e0..8d1190c8d6 100644 --- a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts +++ b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts @@ -1,19 +1,24 @@ -import { IExecuteFunctions } from 'n8n-core'; +import { + IExecuteFunctions, + } from 'n8n-core'; + import { IDataObject, INodeTypeDescription, INodeExecutionData, - INodeType + INodeType, } from 'n8n-workflow'; -import { messageBirdApiRequest } from './GenericFunctions'; +import { + messageBirdApiRequest, + } from './GenericFunctions'; export class MessageBird implements INodeType { description: INodeTypeDescription = { displayName: 'MessageBird', name: 'messageBird', icon: 'file:messagebird.png', - group: ['transform'], + group: ['output'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Sending SMS', @@ -50,15 +55,17 @@ export class MessageBird implements INodeType { type: 'options', displayOptions: { show: { - resource: ['sms'] - } + resource: [ + 'sms', + ], + }, }, options: [ { name: 'Send', value: 'send', description: 'Send text messages (SMS)' - } + }, ], default: 'send', description: 'The operation to perform.' @@ -76,9 +83,13 @@ export class MessageBird implements INodeType { required: true, displayOptions: { show: { - operation: ['send'], - resource: ['sms'] - } + operation: [ + 'send', + ], + resource: [ + 'sms', + ], + }, }, description: 'The number from which to send the message' }, @@ -106,9 +117,13 @@ export class MessageBird implements INodeType { required: true, displayOptions: { show: { - operation: ['send'], - resource: ['sms'] - } + operation: [ + 'send', + ], + resource: [ + 'sms', + ], + }, }, description: 'The message to be send' }, @@ -119,91 +134,87 @@ export class MessageBird implements INodeType { placeholder: 'Add Fields', default: {}, options: [ - //date-time format { displayName: 'Created Date-time', name: 'createdDatetime', type: 'dateTime', - placeholder: '2011-08-30T09:30:16.768-04:00', default: '', - description: - 'The date and time of the creation of the message in RFC3339 format (Y-m-dTH:i:sP).' + description: 'The date and time of the creation of the message in RFC3339 format (Y-m-dTH:i:sP).', }, { displayName: 'Datacoding', name: 'datacoding', - type: 'string', + type: 'options', + options: [ + { + name: 'Auto', + value: 'auto', + }, + { + name: 'Plain', + value: 'plain', + }, + { + name: 'Unicode', + value: 'unicode', + }, + ], default: '', - description: - 'Using unicode will limit the maximum number of characters to 70 instead of 160' + description: 'Using unicode will limit the maximum number of characters to 70 instead of 160', }, { displayName: 'Gateway', name: 'gateway', type: 'number', default: '', - description: 'The SMS route that is used to send the message.' + description: 'The SMS route that is used to send the message.', }, { - displayName: 'Group Ids', + displayName: 'Group IDs', name: 'groupIds', placeholder: '1,2', type: 'string', default: '', - description: - 'group ids separated by commas, If provided recipients can be omitted' + description: 'Group IDs separated by commas, If provided recipients can be omitted', }, { - displayName: 'Mclass', + displayName: 'Message Type', name: 'mclass', type: 'options', - placeholder: 'permissible values from 0-3', + placeholder: 'Permissible values from 0-3', options: [ { - name: '0', - value: '0' + name: 'Normal', + value: 0 }, { - name: '1', - value: '1' + name: 'Flash', + value: 1, }, - { - name: '2', - value: '2' - }, - { - name: '3', - value: '3' - } ], - default: '', - description: - 'Indicated the message type. 1 is a normal message, 0 is a flash message.' + default: 1, + description: 'Indicated the message type. 1 is a normal message, 0 is a flash message.', }, { displayName: 'Reference', name: 'reference', type: 'string', default: '', - description: 'A client reference.' + description: 'A client reference.', }, { displayName: 'Report Url', name: 'reportUrl', type: 'string', default: '', - description: - 'The status report URL to be used on a per-message basis.
Reference is required for a status report webhook to be sent.' + description: 'The status report URL to be used on a per-message basis.
Reference is required for a status report webhook to be sent.', }, - //date-time format { displayName: 'Scheduled Date-time', name: 'scheduledDatetime', type: 'dateTime', default: '', - placeholder: '2011-08-30T09:30:16.768-04:00', - description: - 'The scheduled date and time of the message in RFC3339 format (Y-m-dTH:i:sP).' + description: 'The scheduled date and time of the message in RFC3339 format (Y-m-dTH:i:sP).', }, { displayName: 'Type', @@ -211,44 +222,41 @@ export class MessageBird implements INodeType { type: 'options', options: [ { - name: 'sms', + name: 'SMS', value: 'sms' }, { - name: 'binary', + name: 'Binary', value: 'binary' }, { - name: 'flash', + name: 'Flash', value: 'flash' } ], default: '', - description: - 'The type of message.
Values can be: sms, binary, or flash.' + description: 'The type of message.
Values can be: sms, binary, or flash.' }, - //hash { displayName: 'Type Details', name: 'typeDetails', type: 'string', default: '', - description: - 'A hash with extra information.
Is only used when a binary message is sent.' + description: 'A hash with extra information.
Is only used when a binary message is sent.', }, { displayName: 'Validity', name: 'validity', type: 'number', - default: '', + default: 1, typeOptions: { - minValue: 1 + minValue: 1, }, - description: 'The amount of seconds that the message is valid.' - } - ] - } - ] + description: 'The amount of seconds that the message is valid.', + }, + ], + }, + ], }; async execute(this: IExecuteFunctions): Promise { From 09862d602ee74aa0c3e047a565292a05c926bd5f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 12 Jun 2020 09:39:56 +0200 Subject: [PATCH 55/71] :zap: Minor improvements to MessageBird-Node --- .../nodes/MessageBird/GenericFunctions.ts | 16 ++++---- .../nodes/MessageBird/MessageBird.node.ts | 38 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts b/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts index ee790863d7..5a7eda2210 100644 --- a/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts +++ b/packages/nodes-base/nodes/MessageBird/GenericFunctions.ts @@ -1,15 +1,15 @@ +import { + OptionsWithUri, +} from 'request'; + import { IExecuteFunctions, IHookFunctions, - } from 'n8n-core'; - -import { - OptionsWithUri, - } from 'request'; +} from 'n8n-core'; import { IDataObject, - } from 'n8n-workflow'; +} from 'n8n-workflow'; /** * Make an API request to Message Bird @@ -26,7 +26,7 @@ export async function messageBirdApiRequest( resource: string, body: IDataObject, query: IDataObject = {}, -): Promise { +): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('messageBirdApi'); if (credentials === undefined) { throw new Error('No credentials returned!'); @@ -41,7 +41,7 @@ export async function messageBirdApiRequest( qs: query, body, uri: `https://rest.messagebird.com${resource}`, - json: true + json: true, }; try { diff --git a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts index 8d1190c8d6..9c74b6a3d9 100644 --- a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts +++ b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts @@ -64,11 +64,11 @@ export class MessageBird implements INodeType { { name: 'Send', value: 'send', - description: 'Send text messages (SMS)' + description: 'Send text messages (SMS)', }, ], default: 'send', - description: 'The operation to perform.' + description: 'The operation to perform.', }, // ---------------------------------- @@ -91,7 +91,7 @@ export class MessageBird implements INodeType { ], }, }, - description: 'The number from which to send the message' + description: 'The number from which to send the message.', }, { displayName: 'To', @@ -103,10 +103,10 @@ export class MessageBird implements INodeType { displayOptions: { show: { operation: ['send'], - resource: ['sms'] - } + resource: ['sms'], + }, }, - description: 'all recipients separated by commas' + description: 'All recipients separated by commas.', }, { @@ -125,7 +125,7 @@ export class MessageBird implements INodeType { ], }, }, - description: 'The message to be send' + description: 'The message to be send.', }, { displayName: 'Additional Fields', @@ -160,7 +160,7 @@ export class MessageBird implements INodeType { }, ], default: '', - description: 'Using unicode will limit the maximum number of characters to 70 instead of 160', + description: 'Using unicode will limit the maximum number of characters to 70 instead of 160.', }, { displayName: 'Gateway', @@ -175,7 +175,7 @@ export class MessageBird implements INodeType { placeholder: '1,2', type: 'string', default: '', - description: 'Group IDs separated by commas, If provided recipients can be omitted', + description: 'Group IDs separated by commas, If provided recipients can be omitted.', }, { displayName: 'Message Type', @@ -183,14 +183,14 @@ export class MessageBird implements INodeType { type: 'options', placeholder: 'Permissible values from 0-3', options: [ - { - name: 'Normal', - value: 0 - }, { name: 'Flash', value: 1, }, + { + name: 'Normal', + value: 0, + }, ], default: 1, description: 'Indicated the message type. 1 is a normal message, 0 is a flash message.', @@ -221,10 +221,6 @@ export class MessageBird implements INodeType { name: 'type', type: 'options', options: [ - { - name: 'SMS', - value: 'sms' - }, { name: 'Binary', value: 'binary' @@ -232,10 +228,14 @@ export class MessageBird implements INodeType { { name: 'Flash', value: 'flash' - } + }, + { + name: 'SMS', + value: 'sms' + }, ], default: '', - description: 'The type of message.
Values can be: sms, binary, or flash.' + description: 'The type of message.
Values can be: sms, binary, or flash.', }, { displayName: 'Type Details', From 6a3f075612b9beee9b8321910c62bd0f6b93f365 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 12 Jun 2020 10:30:22 +0200 Subject: [PATCH 56/71] :bug: Fix bug with parameter being a string --- packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index 90e4e8b1fd..52fe2fcc84 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -670,7 +670,7 @@ export class GoogleSheets implements INodeType { sheetId: range.sheetId, dimension: deletePropertyToDimensions[propertyName] as string, startIndex: range.startIndex, - endIndex: range.startIndex + range.amount, + endIndex: parseInt(range.startIndex.toString(), 10) + parseInt(range.amount.toString(), 10), } } }); From 002437862d560c412aeaa382fd223caf6a1958d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 12 Jun 2020 10:28:40 -0300 Subject: [PATCH 57/71] :zap: Fix PNG file copy for CLI build --watch command (#661) --- packages/node-dev/src/Build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-dev/src/Build.ts b/packages/node-dev/src/Build.ts index ddb74add0a..fd695efb4b 100644 --- a/packages/node-dev/src/Build.ts +++ b/packages/node-dev/src/Build.ts @@ -105,10 +105,10 @@ export async function buildFiles (options?: IBuildOptions): Promise { } return new Promise((resolve, reject) => { + copyfiles([join(process.cwd(), './*.png'), outputDirectory], { up: true }, () => resolve(outputDirectory)); buildProcess.on('exit', code => { // Remove the tmp tsconfig file tsconfigData.cleanup(); - copyfiles([join(process.cwd(), './*.png'), outputDirectory], { up: true }, () => resolve(outputDirectory)); }); }); } From 3705e2bfbf9fd7acaf1689b75d01815d40eea9b4 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 12 Jun 2020 15:29:55 +0200 Subject: [PATCH 58/71] :bookmark: Release n8n-node-dev@0.8.0 --- packages/node-dev/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 9d859de394..1c252141b3 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "0.7.0", + "version": "0.8.0", "description": "CLI to simplify n8n credentials/node development", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From d1c5eb26fb550e682a289e2924291d3acb42a29b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 12 Jun 2020 15:32:39 +0200 Subject: [PATCH 59/71] :bookmark: Release n8n-node-dev@0.9.0 --- packages/node-dev/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 1c252141b3..66394fec29 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "0.8.0", + "version": "0.9.0", "description": "CLI to simplify n8n credentials/node development", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From ed160764c9a8e9424609b2bdb0df5c138228f17b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 12 Jun 2020 15:51:38 +0200 Subject: [PATCH 60/71] :zap: Remove debug messages --- packages/cli/src/ExternalHooks.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/cli/src/ExternalHooks.ts b/packages/cli/src/ExternalHooks.ts index b2b84cd7c7..355415158a 100644 --- a/packages/cli/src/ExternalHooks.ts +++ b/packages/cli/src/ExternalHooks.ts @@ -16,22 +16,16 @@ class ExternalHooksClass implements IExternalHooksClass { async init(): Promise { - console.log('ExternalHooks.init'); - if (this.initDidRun === true) { return; } const externalHookFiles = config.get('externalHookFiles').split(':'); - console.log('externalHookFiles'); - console.log(externalHookFiles); - // Load all the provided hook-files for (let hookFilePath of externalHookFiles) { hookFilePath = hookFilePath.trim(); if (hookFilePath !== '') { - console.log(' --- load: ' + hookFilePath); try { const hookFile = require(hookFilePath); @@ -57,8 +51,6 @@ class ExternalHooksClass implements IExternalHooksClass { } async run(hookName: string, hookParameters?: any[]): Promise { // tslint:disable-line:no-any - console.log('RUN NOW: ' + hookName); - const externalHookFunctions: IExternalHooksFunctions = { dbCollections: Db.collections, }; From 2bddf55d4ffaafeb6cb5b0ba92d014a374cf2a32 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 12 Jun 2020 21:12:21 +0200 Subject: [PATCH 61/71] :bug: Fix issue that it always used data of first item --- packages/nodes-base/nodes/OpenWeatherMap.node.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/nodes-base/nodes/OpenWeatherMap.node.ts b/packages/nodes-base/nodes/OpenWeatherMap.node.ts index e12e384094..38e883e53f 100644 --- a/packages/nodes-base/nodes/OpenWeatherMap.node.ts +++ b/packages/nodes-base/nodes/OpenWeatherMap.node.ts @@ -213,20 +213,20 @@ export class OpenWeatherMap implements INodeType { // Set base data qs = { APPID: credentials.accessToken, - units: this.getNodeParameter('format', 0) as string + units: this.getNodeParameter('format', i) as string }; // Get the location - locationSelection = this.getNodeParameter('locationSelection', 0) as string; + locationSelection = this.getNodeParameter('locationSelection', i) as string; if (locationSelection === 'cityName') { - qs.q = this.getNodeParameter('cityName', 0) as string; + qs.q = this.getNodeParameter('cityName', i) as string; } else if (locationSelection === 'cityId') { - qs.id = this.getNodeParameter('cityId', 0) as number; + qs.id = this.getNodeParameter('cityId', i) as number; } else if (locationSelection === 'coordinates') { - qs.lat = this.getNodeParameter('latitude', 0) as string; - qs.lon = this.getNodeParameter('longitude', 0) as string; + qs.lat = this.getNodeParameter('latitude', i) as string; + qs.lon = this.getNodeParameter('longitude', i) as string; } else if (locationSelection === 'zipCode') { - qs.zip = this.getNodeParameter('zipCode', 0) as string; + qs.zip = this.getNodeParameter('zipCode', i) as string; } else { throw new Error(`The locationSelection "${locationSelection}" is not known!`); } From a5055940b3c2360abf23fd008a1333282e84c865 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 12:34:20 +0200 Subject: [PATCH 62/71] :bug: Fix issue with multiuser-setup --- .../migrations/1588157391238-InitialMigration.ts | 8 ++++---- .../migrations/1587669153312-InitialMigration.ts | 16 +++++++++------- .../migrations/1588102412422-InitialMigration.ts | 8 ++++---- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/databases/mysqldb/migrations/1588157391238-InitialMigration.ts b/packages/cli/src/databases/mysqldb/migrations/1588157391238-InitialMigration.ts index fc11ef32fb..1d1d4d8cc5 100644 --- a/packages/cli/src/databases/mysqldb/migrations/1588157391238-InitialMigration.ts +++ b/packages/cli/src/databases/mysqldb/migrations/1588157391238-InitialMigration.ts @@ -8,8 +8,8 @@ export class InitialMigration1588157391238 implements MigrationInterface { async up(queryRunner: QueryRunner): Promise { const tablePrefix = config.get('database.tablePrefix'); - await queryRunner.query('CREATE TABLE IF NOT EXISTS `' + tablePrefix + 'credentials_entity` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(128) NOT NULL, `data` text NOT NULL, `type` varchar(32) NOT NULL, `nodesAccess` json NOT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, INDEX `IDX_07fde106c0b471d8cc80a64fc8` (`type`), PRIMARY KEY (`id`)) ENGINE=InnoDB', undefined); - await queryRunner.query('CREATE TABLE IF NOT EXISTS `' + tablePrefix + 'execution_entity` (`id` int NOT NULL AUTO_INCREMENT, `data` text NOT NULL, `finished` tinyint NOT NULL, `mode` varchar(255) NOT NULL, `retryOf` varchar(255) NULL, `retrySuccessId` varchar(255) NULL, `startedAt` datetime NOT NULL, `stoppedAt` datetime NOT NULL, `workflowData` json NOT NULL, `workflowId` varchar(255) NULL, INDEX `IDX_c4d999a5e90784e8caccf5589d` (`workflowId`), PRIMARY KEY (`id`)) ENGINE=InnoDB', undefined); + await queryRunner.query('CREATE TABLE IF NOT EXISTS `' + tablePrefix + 'credentials_entity` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(128) NOT NULL, `data` text NOT NULL, `type` varchar(32) NOT NULL, `nodesAccess` json NOT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, INDEX `IDX_' + tablePrefix + '07fde106c0b471d8cc80a64fc8` (`type`), PRIMARY KEY (`id`)) ENGINE=InnoDB', undefined); + await queryRunner.query('CREATE TABLE IF NOT EXISTS `' + tablePrefix + 'execution_entity` (`id` int NOT NULL AUTO_INCREMENT, `data` text NOT NULL, `finished` tinyint NOT NULL, `mode` varchar(255) NOT NULL, `retryOf` varchar(255) NULL, `retrySuccessId` varchar(255) NULL, `startedAt` datetime NOT NULL, `stoppedAt` datetime NOT NULL, `workflowData` json NOT NULL, `workflowId` varchar(255) NULL, INDEX `IDX_' + tablePrefix + 'c4d999a5e90784e8caccf5589d` (`workflowId`), PRIMARY KEY (`id`)) ENGINE=InnoDB', undefined); await queryRunner.query('CREATE TABLE IF NOT EXISTS`' + tablePrefix + 'workflow_entity` (`id` int NOT NULL AUTO_INCREMENT, `name` varchar(128) NOT NULL, `active` tinyint NOT NULL, `nodes` json NOT NULL, `connections` json NOT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, `settings` json NULL, `staticData` json NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB', undefined); } @@ -17,9 +17,9 @@ export class InitialMigration1588157391238 implements MigrationInterface { const tablePrefix = config.get('database.tablePrefix'); await queryRunner.query('DROP TABLE `' + tablePrefix + 'workflow_entity`', undefined); - await queryRunner.query('DROP INDEX `IDX_c4d999a5e90784e8caccf5589d` ON `' + tablePrefix + 'execution_entity`', undefined); + await queryRunner.query('DROP INDEX `IDX_' + tablePrefix + 'c4d999a5e90784e8caccf5589d` ON `' + tablePrefix + 'execution_entity`', undefined); await queryRunner.query('DROP TABLE `' + tablePrefix + 'execution_entity`', undefined); - await queryRunner.query('DROP INDEX `IDX_07fde106c0b471d8cc80a64fc8` ON `' + tablePrefix + 'credentials_entity`', undefined); + await queryRunner.query('DROP INDEX `IDX_' + tablePrefix + '07fde106c0b471d8cc80a64fc8` ON `' + tablePrefix + 'credentials_entity`', undefined); await queryRunner.query('DROP TABLE `' + tablePrefix + 'credentials_entity`', undefined); } diff --git a/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts b/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts index 555015c10d..29a80e434f 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts @@ -7,29 +7,31 @@ export class InitialMigration1587669153312 implements MigrationInterface { async up(queryRunner: QueryRunner): Promise { let tablePrefix = config.get('database.tablePrefix'); + const tablePrefixIndex = tablePrefix; const schema = config.get('database.postgresdb.schema'); if (schema) { tablePrefix = schema + '.' + tablePrefix; } - await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}credentials_entity ("id" SERIAL NOT NULL, "name" character varying(128) NOT NULL, "data" text NOT NULL, "type" character varying(32) NOT NULL, "nodesAccess" json NOT NULL, "createdAt" TIMESTAMP NOT NULL, "updatedAt" TIMESTAMP NOT NULL, CONSTRAINT PK_814c3d3c36e8a27fa8edb761b0e PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_07fde106c0b471d8cc80a64fc8 ON ${tablePrefix}credentials_entity (type) `, undefined); - await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}execution_entity ("id" SERIAL NOT NULL, "data" text NOT NULL, "finished" boolean NOT NULL, "mode" character varying NOT NULL, "retryOf" character varying, "retrySuccessId" character varying, "startedAt" TIMESTAMP NOT NULL, "stoppedAt" TIMESTAMP NOT NULL, "workflowData" json NOT NULL, "workflowId" character varying, CONSTRAINT PK_e3e63bbf986767844bbe1166d4e PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_c4d999a5e90784e8caccf5589d ON ${tablePrefix}execution_entity ("workflowId") `, undefined); - await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}workflow_entity ("id" SERIAL NOT NULL, "name" character varying(128) NOT NULL, "active" boolean NOT NULL, "nodes" json NOT NULL, "connections" json NOT NULL, "createdAt" TIMESTAMP NOT NULL, "updatedAt" TIMESTAMP NOT NULL, "settings" json, "staticData" json, CONSTRAINT PK_eded7d72664448da7745d551207 PRIMARY KEY ("id"))`, undefined); + await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}credentials_entity ("id" SERIAL NOT NULL, "name" character varying(128) NOT NULL, "data" text NOT NULL, "type" character varying(32) NOT NULL, "nodesAccess" json NOT NULL, "createdAt" TIMESTAMP NOT NULL, "updatedAt" TIMESTAMP NOT NULL, CONSTRAINT PK_${tablePrefixIndex}814c3d3c36e8a27fa8edb761b0e PRIMARY KEY ("id"))`, undefined); + await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixIndex}07fde106c0b471d8cc80a64fc8 ON ${tablePrefix}credentials_entity (type) `, undefined); + await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}execution_entity ("id" SERIAL NOT NULL, "data" text NOT NULL, "finished" boolean NOT NULL, "mode" character varying NOT NULL, "retryOf" character varying, "retrySuccessId" character varying, "startedAt" TIMESTAMP NOT NULL, "stoppedAt" TIMESTAMP NOT NULL, "workflowData" json NOT NULL, "workflowId" character varying, CONSTRAINT PK_${tablePrefixIndex}e3e63bbf986767844bbe1166d4e PRIMARY KEY ("id"))`, undefined); + await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixIndex}c4d999a5e90784e8caccf5589d ON ${tablePrefix}execution_entity ("workflowId") `, undefined); + await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}workflow_entity ("id" SERIAL NOT NULL, "name" character varying(128) NOT NULL, "active" boolean NOT NULL, "nodes" json NOT NULL, "connections" json NOT NULL, "createdAt" TIMESTAMP NOT NULL, "updatedAt" TIMESTAMP NOT NULL, "settings" json, "staticData" json, CONSTRAINT PK_${tablePrefixIndex}eded7d72664448da7745d551207 PRIMARY KEY ("id"))`, undefined); } async down(queryRunner: QueryRunner): Promise { let tablePrefix = config.get('database.tablePrefix'); + const tablePrefixIndex = tablePrefix; const schema = config.get('database.postgresdb.schema'); if (schema) { tablePrefix = schema + '.' + tablePrefix; } await queryRunner.query(`DROP TABLE ${tablePrefix}workflow_entity`, undefined); - await queryRunner.query(`DROP INDEX IDX_c4d999a5e90784e8caccf5589d`, undefined); + await queryRunner.query(`DROP INDEX IDX_${tablePrefixIndex}c4d999a5e90784e8caccf5589d`, undefined); await queryRunner.query(`DROP TABLE ${tablePrefix}execution_entity`, undefined); - await queryRunner.query(`DROP INDEX IDX_07fde106c0b471d8cc80a64fc8`, undefined); + await queryRunner.query(`DROP INDEX IDX_${tablePrefixIndex}07fde106c0b471d8cc80a64fc8`, undefined); await queryRunner.query(`DROP TABLE ${tablePrefix}credentials_entity`, undefined); } diff --git a/packages/cli/src/databases/sqlite/migrations/1588102412422-InitialMigration.ts b/packages/cli/src/databases/sqlite/migrations/1588102412422-InitialMigration.ts index 31b271d633..c2bb55040e 100644 --- a/packages/cli/src/databases/sqlite/migrations/1588102412422-InitialMigration.ts +++ b/packages/cli/src/databases/sqlite/migrations/1588102412422-InitialMigration.ts @@ -9,9 +9,9 @@ export class InitialMigration1588102412422 implements MigrationInterface { const tablePrefix = config.get('database.tablePrefix'); await queryRunner.query(`CREATE TABLE IF NOT EXISTS "${tablePrefix}credentials_entity" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(128) NOT NULL, "data" text NOT NULL, "type" varchar(32) NOT NULL, "nodesAccess" text NOT NULL, "createdAt" datetime NOT NULL, "updatedAt" datetime NOT NULL)`, undefined); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_07fde106c0b471d8cc80a64fc8" ON "${tablePrefix}credentials_entity" ("type") `, undefined); + await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_${tablePrefix}07fde106c0b471d8cc80a64fc8" ON "${tablePrefix}credentials_entity" ("type") `, undefined); await queryRunner.query(`CREATE TABLE IF NOT EXISTS "${tablePrefix}execution_entity" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "data" text NOT NULL, "finished" boolean NOT NULL, "mode" varchar NOT NULL, "retryOf" varchar, "retrySuccessId" varchar, "startedAt" datetime NOT NULL, "stoppedAt" datetime NOT NULL, "workflowData" text NOT NULL, "workflowId" varchar)`, undefined); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_c4d999a5e90784e8caccf5589d" ON "${tablePrefix}execution_entity" ("workflowId") `, undefined); + await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_${tablePrefix}c4d999a5e90784e8caccf5589d" ON "${tablePrefix}execution_entity" ("workflowId") `, undefined); await queryRunner.query(`CREATE TABLE IF NOT EXISTS "${tablePrefix}workflow_entity" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(128) NOT NULL, "active" boolean NOT NULL, "nodes" text NOT NULL, "connections" text NOT NULL, "createdAt" datetime NOT NULL, "updatedAt" datetime NOT NULL, "settings" text, "staticData" text)`, undefined); } @@ -19,9 +19,9 @@ export class InitialMigration1588102412422 implements MigrationInterface { const tablePrefix = config.get('database.tablePrefix'); await queryRunner.query(`DROP TABLE "${tablePrefix}workflow_entity"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_c4d999a5e90784e8caccf5589d"`, undefined); + await queryRunner.query(`DROP INDEX "IDX_${tablePrefix}c4d999a5e90784e8caccf5589d"`, undefined); await queryRunner.query(`DROP TABLE "${tablePrefix}execution_entity"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_07fde106c0b471d8cc80a64fc8"`, undefined); + await queryRunner.query(`DROP INDEX "IDX_${tablePrefix}07fde106c0b471d8cc80a64fc8"`, undefined); await queryRunner.query(`DROP TABLE "${tablePrefix}credentials_entity"`, undefined); } From 094614508d691bfa8e2e7008376a00578ade3838 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 13:47:49 +0200 Subject: [PATCH 63/71] :zap: Some minor improvements --- .../nodes/Google/Drive/GenericFunctions.ts | 36 +++++++++---------- .../nodes/Google/Drive/GoogleDrive.node.ts | 4 +-- .../nodes-base/nodes/Twitter/Twitter.node.ts | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts index 9946cd802a..7d15e492ad 100644 --- a/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts @@ -1,6 +1,6 @@ import { OptionsWithUri, - } from 'request'; +} from 'request'; import { IExecuteFunctions, @@ -64,7 +64,7 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF errorMessages = errorMessages.join('|'); - } else if (error.response.body.error.message){ + } else if (error.response.body.error.message) { errorMessages = error.response.body.error.message; } @@ -74,7 +74,7 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF } } -export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any +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[] = []; @@ -93,7 +93,7 @@ export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOp return returnData; } -function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IDataObject) : Promise { +function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IDataObject): Promise { //https://developers.google.com/identity/protocols/oauth2/service-account#httprest const scopes = [ @@ -106,25 +106,25 @@ function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoa 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, + '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', - }, + algorithm: 'RS256', + header: { + 'kid': credentials.privateKey as string, + 'typ': 'JWT', + 'alg': 'RS256', + }, } - ); + ); - const options: OptionsWithUri = { + const options: OptionsWithUri = { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, diff --git a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts index 8ddfd7c1fa..f6291b1045 100644 --- a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts @@ -797,7 +797,7 @@ export class GoogleDrive implements INodeType { { name: 'domain', value: 'domain', - description:"All files shared to the user's domain that are searchable", + description: 'All files shared to the user\'s domain that are searchable', }, { name: 'drive', @@ -945,7 +945,7 @@ export class GoogleDrive implements INodeType { queryCorpora = options.corpora as string; } - let driveId : string | undefined; + let driveId: string | undefined; driveId = options.driveId as string; if (driveId === '') { driveId = undefined; diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index 94718973c5..a62f9fd95b 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -138,7 +138,7 @@ export class Twitter implements INodeType { const isImage = binaryData[binaryPropertyName].mimeType.includes('image'); if (isImage && isAnimatedWebp) { - throw new Error('Animated .webp images are not supported use .git instead'); + throw new Error('Animated .webp images are not supported use .gif instead'); } if (isImage) { From 8d8d9f256f006575cf90395c75ed9f2e50aa3a62 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 16:43:37 +0200 Subject: [PATCH 64/71] :zap: Add OAuth2 support to Mautic-Trigger Node --- .../nodes/Mautic/MauticTrigger.node.ts | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts b/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts index a3ba20f28a..4f844e1188 100644 --- a/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts +++ b/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts @@ -38,7 +38,25 @@ export class MauticTrigger implements INodeType { { name: 'mauticApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'credentials', + ], + }, + }, + }, + { + name: 'mauticOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, ], webhooks: [ { @@ -49,6 +67,22 @@ export class MauticTrigger implements INodeType { }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Credentials', + value: 'credentials', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'credentials', + }, { displayName: 'Events', name: 'events', From 81a29ff16d40368f577940aa1edd2e9e2f530aa0 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 16:44:49 +0200 Subject: [PATCH 65/71] :zap: Fix issue with some parameters returning strings --- .../nodes/Google/Sheet/GoogleSheets.node.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index 52fe2fcc84..042eb39105 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -622,7 +622,7 @@ export class GoogleSheets implements INodeType { // ---------------------------------- // append // ---------------------------------- - const keyRow = this.getNodeParameter('keyRow', 0) as number; + const keyRow = parseInt(this.getNodeParameter('keyRow', 0) as string, 10); const items = this.getInputData(); @@ -693,8 +693,8 @@ export class GoogleSheets implements INodeType { return []; } - const dataStartRow = this.getNodeParameter('dataStartRow', 0) as number; - const keyRow = this.getNodeParameter('keyRow', 0) as number; + const dataStartRow = parseInt(this.getNodeParameter('dataStartRow', 0) as string, 10); + const keyRow = parseInt(this.getNodeParameter('keyRow', 0) as string, 10); const items = this.getInputData(); @@ -735,8 +735,8 @@ export class GoogleSheets implements INodeType { } ]; } else { - const dataStartRow = this.getNodeParameter('dataStartRow', 0) as number; - const keyRow = this.getNodeParameter('keyRow', 0) as number; + const dataStartRow = parseInt(this.getNodeParameter('dataStartRow', 0) as string, 10); + const keyRow = parseInt(this.getNodeParameter('keyRow', 0) as string, 10); returnData = sheet.structureArrayDataByColumn(sheetData, keyRow, dataStartRow); } @@ -769,8 +769,8 @@ export class GoogleSheets implements INodeType { const data = await sheet.batchUpdate(updateData, valueInputMode); } else { const keyName = this.getNodeParameter('key', 0) as string; - const keyRow = this.getNodeParameter('keyRow', 0) as number; - const dataStartRow = this.getNodeParameter('dataStartRow', 0) as number; + const keyRow = parseInt(this.getNodeParameter('keyRow', 0) as string, 10); + const dataStartRow = parseInt(this.getNodeParameter('dataStartRow', 0) as string, 10); const setData: IDataObject[] = []; items.forEach((item) => { From 0af237e5b18e2b1ca81ecef3f3e64d47ba6bb220 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 17:46:13 +0200 Subject: [PATCH 66/71] :zap: Some minor improvements to Monday.com-Node --- .../nodes/MondayCom/BoardItemDescription.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts index 4f8cfccecc..9537c32521 100644 --- a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts +++ b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts @@ -82,10 +82,10 @@ export const boardItemFields = [ ], }, }, - description: `Item's ID` + description: 'The unique identifier of the item to add update to.', }, { - displayName: 'Body', + displayName: 'Update Text', name: 'value', type: 'string', required: true, @@ -100,7 +100,7 @@ export const boardItemFields = [ ], }, }, - description: 'The update text', + description: 'The update text to add.', }, /* -------------------------------------------------------------------------- */ /* boardItem:changeColumnValue */ @@ -142,7 +142,7 @@ export const boardItemFields = [ ], }, }, - description: `Item's ID` + description: 'The unique identifier of the item to to change column of.', }, { displayName: 'Column ID', @@ -184,7 +184,7 @@ export const boardItemFields = [ ], }, }, - description: 'The column value in JSON format.', + description: 'The column value in JSON format. Documentation can be found here.', }, /* -------------------------------------------------------------------------- */ /* boardItem:changeMultipleColumnValues */ @@ -244,7 +244,7 @@ export const boardItemFields = [ ], }, }, - description: 'The column fields and values in JSON format.', + description: 'The column fields and values in JSON format. Documentation can be found here.', typeOptions: { alwaysOpenEditWindow: true, }, From 2af04e775dfdb62086946007bc025e564f0eeef2 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 19:22:49 +0200 Subject: [PATCH 67/71] :zap: Add delete option to MongoDb-Node --- .../nodes-base/nodes/MongoDb/MongoDb.node.ts | 13 +++++++- .../nodes/MongoDb/mongo.node.options.ts | 30 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts index dbd7ab2929..d91a287b88 100644 --- a/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts +++ b/packages/nodes-base/nodes/MongoDb/MongoDb.node.ts @@ -32,7 +32,18 @@ export class MongoDb implements INodeType { const items = this.getInputData(); const operation = this.getNodeParameter('operation', 0) as string; - if (operation === 'find') { + if (operation === 'delete') { + // ---------------------------------- + // delete + // ---------------------------------- + + const { deletedCount } = await mdb + .collection(this.getNodeParameter('collection', 0) as string) + .deleteMany(JSON.parse(this.getNodeParameter('query', 0) as string)); + + returnItems = this.helpers.returnJsonArray([{ deletedCount }]); + + } else if (operation === 'find') { // ---------------------------------- // find // ---------------------------------- diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts index 190ff3de88..953a45a620 100644 --- a/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts +++ b/packages/nodes-base/nodes/MongoDb/mongo.node.options.ts @@ -28,6 +28,11 @@ export const nodeDescription: INodeTypeDescription = { name: 'operation', type: 'options', options: [ + { + name: 'Delete', + value: 'delete', + description: 'Delete documents.' + }, { name: 'Find', value: 'find', @@ -57,13 +62,36 @@ export const nodeDescription: INodeTypeDescription = { description: 'MongoDB Collection' }, + // ---------------------------------- + // delete + // ---------------------------------- + { + displayName: 'Delete Query (JSON format)', + name: 'query', + type: 'json', + typeOptions: { + rows: 5 + }, + displayOptions: { + show: { + operation: [ + 'delete' + ], + }, + }, + default: '{}', + placeholder: `{ "birth": { "$gt": "1950-01-01" } }`, + required: true, + description: 'MongoDB Delete query.' + }, + // ---------------------------------- // find // ---------------------------------- { displayName: 'Query (JSON format)', name: 'query', - type: 'string', + type: 'json', typeOptions: { rows: 5 }, From 1cfdccc311066aa2efecb650ae5ec4687c7ed62b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 20:31:57 +0200 Subject: [PATCH 68/71] :bookmark: Release n8n-nodes-base@0.65.0 --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 182922492c..8a3c76c261 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.64.1", + "version": "0.65.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 716716bd1c11111c4952f61c57c5f58c40e5d74f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 20:34:22 +0200 Subject: [PATCH 69/71] :arrow_up: Set n8n-nodes-base@0.65.0 on n8n --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index f3a747bfe2..f221cd59c7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -102,7 +102,7 @@ "mysql2": "^2.0.1", "n8n-core": "~0.36.0", "n8n-editor-ui": "~0.47.0", - "n8n-nodes-base": "~0.64.1", + "n8n-nodes-base": "~0.65.0", "n8n-workflow": "~0.33.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", From 7f74408487664cc9f2aaf11494bb9b4f8f8aaa6b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 13 Jun 2020 20:35:00 +0200 Subject: [PATCH 70/71] :bookmark: Release n8n@0.70.0 --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index f221cd59c7..1628a80b82 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.69.1", + "version": "0.70.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From bc4d407c4427e41259e2b180ec2053424fb97431 Mon Sep 17 00:00:00 2001 From: ricardo Date: Sat, 13 Jun 2020 19:48:24 -0400 Subject: [PATCH 71/71] :sparkles: Improvements to Hubspot-Node --- .../HubspotOAuth2Api.credentials.ts | 26 +- .../nodes/Hubspot/GenericFunctions.ts | 37 +- .../nodes-base/nodes/Hubspot/Hubspot.node.ts | 8 +- .../nodes/Hubspot/HubspotTrigger.node.ts | 61 +--- packages/nodes-base/package.json | 344 +----------------- 5 files changed, 62 insertions(+), 414 deletions(-) diff --git a/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts b/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts index 775211cbc9..ce18b899df 100644 --- a/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts @@ -3,6 +3,12 @@ import { NodePropertyTypes, } from 'n8n-workflow'; +const scopes = [ + 'contacts', + 'forms', + 'tickets', +]; + export class HubspotOAuth2Api implements ICredentialType { name = 'hubspotOAuth2Api'; extends = [ @@ -27,8 +33,8 @@ export class HubspotOAuth2Api implements ICredentialType { { displayName: 'Scope', name: 'scope', - type: 'string' as NodePropertyTypes, - default: '', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' '), }, { displayName: 'Auth URI Query Parameters', @@ -39,20 +45,8 @@ export class HubspotOAuth2Api implements ICredentialType { { displayName: 'Authentication', name: 'authentication', - type: 'options' as NodePropertyTypes, - options: [ - { - name: 'Body', - value: 'body', - description: 'Send credentials in body', - }, - { - name: 'Header', - value: 'header', - description: 'Send credentials as Basic Auth header', - }, - ], - default: 'header', + type: 'hidden' as NodePropertyTypes, + default: 'body', description: 'Resource to consume.', }, ]; diff --git a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts index eb414b82aa..d8d68abfb6 100644 --- a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts @@ -14,7 +14,12 @@ import { } from 'n8n-workflow'; export async function hubspotApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise { // tslint:disable-line:no-any - const authenticationMethod = this.getNodeParameter('authentication', 0); + + let authenticationMethod = this.getNodeParameter('authentication', 0); + + if (this.getNode().type.includes('Trigger')) { + authenticationMethod = 'developerApi'; + } const options: OptionsWithUri = { method, @@ -26,26 +31,40 @@ export async function hubspotApiRequest(this: IHookFunctions | IExecuteFunctions }; try { - if (authenticationMethod === 'accessToken') { + if (authenticationMethod === 'apiKey') { const credentials = this.getCredentials('hubspotApi'); options.qs.hapikey = credentials!.apiKey as string; + return await this.helpers.request!(options); + } else if (authenticationMethod === 'developerApi') { + const credentials = this.getCredentials('hubspotDeveloperApi'); + + options.qs.hapikey = credentials!.apiKey as string; + return await this.helpers.request!(options); } else { // @ts-ignore return await this.helpers.requestOAuth2!.call(this, 'hubspotOAuth2Api', options, 'Bearer'); } } catch (error) { - if (error.response && error.response.body && error.response.body.errors) { - // Try to return the error prettier - let errorMessages = error.response.body.errors; + let errorMessages; - if (errorMessages[0].message) { - // @ts-ignore - errorMessages = errorMessages.map(errorItem => errorItem.message); + if (error.response && error.response.body) { + + if (error.response.body.message) { + + errorMessages = [error.response.body.message]; + + } else if (error.response.body.errors) { + // Try to return the error prettier + errorMessages = error.response.body.errors; + + if (errorMessages[0].message) { + // @ts-ignore + errorMessages = errorMessages.map(errorItem => errorItem.message); + } } - throw new Error(`Hubspot error response [${error.statusCode}]: ${errorMessages.join('|')}`); } diff --git a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts index bed4606042..adf01d1ec3 100644 --- a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts +++ b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts @@ -76,7 +76,7 @@ export class Hubspot implements INodeType { displayOptions: { show: { authentication: [ - 'accessToken', + 'apiKey', ], }, }, @@ -100,15 +100,15 @@ export class Hubspot implements INodeType { type: 'options', options: [ { - name: 'Access Token', - value: 'accessToken', + name: 'API Key', + value: 'apiKey', }, { name: 'OAuth2', value: 'oAuth2', }, ], - default: 'accessToken', + default: 'apiKey', description: 'The method of authentication.', }, { diff --git a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts index 38fd678d58..47b2318b36 100644 --- a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts +++ b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts @@ -37,24 +37,6 @@ export class HubspotTrigger implements INodeType { { name: 'hubspotDeveloperApi', required: true, - displayOptions: { - show: { - authentication: [ - 'developerApi', - ], - }, - }, - }, - { - name: 'hubspotOAuth2Api', - required: true, - displayOptions: { - show: { - authentication: [ - 'oAuth2', - ], - }, - }, }, ], webhooks: [ @@ -72,23 +54,6 @@ export class HubspotTrigger implements INodeType { }, ], properties: [ - { - displayName: 'Authentication', - name: 'authentication', - type: 'options', - options: [ - { - name: 'Developer API', - value: 'developerApi', - }, - { - name: 'OAuth2', - value: 'oAuth2', - }, - ], - default: 'developerApi', - description: 'The method of authentication.', - }, { displayName: 'App ID', name: 'appId', @@ -282,15 +247,7 @@ export class HubspotTrigger implements INodeType { async webhook(this: IWebhookFunctions): Promise { - const authenticationMethod = this.getNodeParameter('authentication') as string; - - let credentials : IDataObject; - - if (authenticationMethod === 'hubspotDeveloperApi') { - credentials = this.getCredentials('hubspotDeveloperApi') as IDataObject; - } else { - credentials = this.getCredentials('hubspotOAuth2Api') as IDataObject; - } + const credentials = this.getCredentials('hubspotDeveloperApi') as IDataObject; if (credentials === undefined) { throw new Error('No credentials found!'); @@ -303,12 +260,18 @@ export class HubspotTrigger implements INodeType { if (headerData['x-hubspot-signature'] === undefined) { return {}; } - const hash = `${credentials!.clientSecret}${JSON.stringify(bodyData)}`; - const signature = createHash('sha256').update(hash).digest('hex'); - //@ts-ignore - if (signature !== headerData['x-hubspot-signature']) { - return {}; + + // check signare if client secret is defined + + if (credentials.clientSecret !== '') { + const hash = `${credentials!.clientSecret}${JSON.stringify(bodyData)}`; + const signature = createHash('sha256').update(hash).digest('hex'); + //@ts-ignore + if (signature !== headerData['x-hubspot-signature']) { + return {}; + } } + for (let i = 0; i < bodyData.length; i++) { const subscriptionType = bodyData[i].subscriptionType as string; if (subscriptionType.includes('contact')) { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 7b90d3e2a9..5b33ebcc98 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -69,6 +69,7 @@ "dist/credentials/HttpHeaderAuth.credentials.js", "dist/credentials/HubspotApi.credentials.js", "dist/credentials/HubspotDeveloperApi.credentials.js", + "dist/credentials/HubspotOAuth2Api.credentials.js", "dist/credentials/HunterApi.credentials.js", "dist/credentials/Imap.credentials.js", "dist/credentials/IntercomApi.credentials.js", @@ -356,340 +357,11 @@ "/dist/", "/node_modules/" ], - "n8n": { - "credentials": [ - "dist/credentials/ActiveCampaignApi.credentials.js", - "dist/credentials/AgileCrmApi.credentials.js", - "dist/credentials/AcuitySchedulingApi.credentials.js", - "dist/credentials/AirtableApi.credentials.js", - "dist/credentials/Amqp.credentials.js", - "dist/credentials/AsanaApi.credentials.js", - "dist/credentials/Aws.credentials.js", - "dist/credentials/AffinityApi.credentials.js", - "dist/credentials/BannerbearApi.credentials.js", - "dist/credentials/BitbucketApi.credentials.js", - "dist/credentials/BitlyApi.credentials.js", - "dist/credentials/ChargebeeApi.credentials.js", - "dist/credentials/ClearbitApi.credentials.js", - "dist/credentials/ClickUpApi.credentials.js", - "dist/credentials/ClockifyApi.credentials.js", - "dist/credentials/CockpitApi.credentials.js", - "dist/credentials/CodaApi.credentials.js", - "dist/credentials/CopperApi.credentials.js", - "dist/credentials/CalendlyApi.credentials.js", - "dist/credentials/DisqusApi.credentials.js", - "dist/credentials/DriftApi.credentials.js", - "dist/credentials/DropboxApi.credentials.js", - "dist/credentials/EventbriteApi.credentials.js", - "dist/credentials/FacebookGraphApi.credentials.js", - "dist/credentials/FreshdeskApi.credentials.js", - "dist/credentials/FileMaker.credentials.js", - "dist/credentials/FlowApi.credentials.js", - "dist/credentials/GithubApi.credentials.js", - "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", - "dist/credentials/HttpBasicAuth.credentials.js", - "dist/credentials/HttpDigestAuth.credentials.js", - "dist/credentials/HttpHeaderAuth.credentials.js", - "dist/credentials/HubspotApi.credentials.js", - "dist/credentials/HubspotDeveloperApi.credentials.js", - "dist/credentials/HubspotOAuth2Api.credentials.js", - "dist/credentials/HunterApi.credentials.js", - "dist/credentials/Imap.credentials.js", - "dist/credentials/IntercomApi.credentials.js", - "dist/credentials/InvoiceNinjaApi.credentials.js", - "dist/credentials/JiraSoftwareCloudApi.credentials.js", - "dist/credentials/JiraSoftwareServerApi.credentials.js", - "dist/credentials/JotFormApi.credentials.js", - "dist/credentials/KeapOAuth2Api.credentials.js", - "dist/credentials/LinkFishApi.credentials.js", - "dist/credentials/MailchimpApi.credentials.js", - "dist/credentials/MailgunApi.credentials.js", - "dist/credentials/MailjetEmailApi.credentials.js", - "dist/credentials/MailjetSmsApi.credentials.js", - "dist/credentials/MandrillApi.credentials.js", - "dist/credentials/MattermostApi.credentials.js", - "dist/credentials/MauticApi.credentials.js", - "dist/credentials/MicrosoftExcelOAuth2Api.credentials.js", - "dist/credentials/MicrosoftOAuth2Api.credentials.js", - "dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js", - "dist/credentials/MoceanApi.credentials.js", - "dist/credentials/MondayComApi.credentials.js", - "dist/credentials/MongoDb.credentials.js", - "dist/credentials/Msg91Api.credentials.js", - "dist/credentials/MySql.credentials.js", - "dist/credentials/NextCloudApi.credentials.js", - "dist/credentials/OAuth1Api.credentials.js", - "dist/credentials/OAuth2Api.credentials.js", - "dist/credentials/OpenWeatherMapApi.credentials.js", - "dist/credentials/PagerDutyApi.credentials.js", - "dist/credentials/PayPalApi.credentials.js", - "dist/credentials/PipedriveApi.credentials.js", - "dist/credentials/Postgres.credentials.js", - "dist/credentials/Redis.credentials.js", - "dist/credentials/RocketchatApi.credentials.js", - "dist/credentials/RundeckApi.credentials.js", - "dist/credentials/ShopifyApi.credentials.js", - "dist/credentials/SalesforceOAuth2Api.credentials.js", - "dist/credentials/SlackApi.credentials.js", - "dist/credentials/SlackOAuth2Api.credentials.js", - "dist/credentials/Sms77Api.credentials.js", - "dist/credentials/Smtp.credentials.js", - "dist/credentials/StripeApi.credentials.js", - "dist/credentials/SalesmateApi.credentials.js", - "dist/credentials/SegmentApi.credentials.js", - "dist/credentials/SurveyMonkeyApi.credentials.js", - "dist/credentials/TelegramApi.credentials.js", - "dist/credentials/TodoistApi.credentials.js", - "dist/credentials/TrelloApi.credentials.js", - "dist/credentials/TwilioApi.credentials.js", - "dist/credentials/TwitterOAuth1Api.credentials.js", - "dist/credentials/TypeformApi.credentials.js", - "dist/credentials/TogglApi.credentials.js", - "dist/credentials/UpleadApi.credentials.js", - "dist/credentials/VeroApi.credentials.js", - "dist/credentials/WebflowApi.credentials.js", - "dist/credentials/WooCommerceApi.credentials.js", - "dist/credentials/WordpressApi.credentials.js", - "dist/credentials/ZendeskApi.credentials.js", - "dist/credentials/ZohoOAuth2Api.credentials.js", - "dist/credentials/ZulipApi.credentials.js" - ], - "nodes": [ - "dist/nodes/ActiveCampaign/ActiveCampaign.node.js", - "dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js", - "dist/nodes/AgileCrm/AgileCrm.node.js", - "dist/nodes/Airtable/Airtable.node.js", - "dist/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.js", - "dist/nodes/Amqp/Amqp.node.js", - "dist/nodes/Amqp/AmqpTrigger.node.js", - "dist/nodes/Asana/Asana.node.js", - "dist/nodes/Asana/AsanaTrigger.node.js", - "dist/nodes/Affinity/Affinity.node.js", - "dist/nodes/Affinity/AffinityTrigger.node.js", - "dist/nodes/Aws/AwsLambda.node.js", - "dist/nodes/Aws/S3/AwsS3.node.js", - "dist/nodes/Aws/AwsSes.node.js", - "dist/nodes/Aws/AwsSns.node.js", - "dist/nodes/Aws/AwsSnsTrigger.node.js", - "dist/nodes/Bannerbear/Bannerbear.node.js", - "dist/nodes/Bitbucket/BitbucketTrigger.node.js", - "dist/nodes/Bitly/Bitly.node.js", - "dist/nodes/Calendly/CalendlyTrigger.node.js", - "dist/nodes/Chargebee/Chargebee.node.js", - "dist/nodes/Chargebee/ChargebeeTrigger.node.js", - "dist/nodes/Clearbit/Clearbit.node.js", - "dist/nodes/ClickUp/ClickUp.node.js", - "dist/nodes/ClickUp/ClickUpTrigger.node.js", - "dist/nodes/Clockify/ClockifyTrigger.node.js", - "dist/nodes/Cockpit/Cockpit.node.js", - "dist/nodes/Coda/Coda.node.js", - "dist/nodes/Copper/CopperTrigger.node.js", - "dist/nodes/Cron.node.js", - "dist/nodes/Crypto.node.js", - "dist/nodes/DateTime.node.js", - "dist/nodes/Discord/Discord.node.js", - "dist/nodes/Disqus/Disqus.node.js", - "dist/nodes/Drift/Drift.node.js", - "dist/nodes/Dropbox/Dropbox.node.js", - "dist/nodes/EditImage.node.js", - "dist/nodes/EmailReadImap.node.js", - "dist/nodes/EmailSend.node.js", - "dist/nodes/ErrorTrigger.node.js", - "dist/nodes/Eventbrite/EventbriteTrigger.node.js", - "dist/nodes/ExecuteCommand.node.js", - "dist/nodes/ExecuteWorkflow.node.js", - "dist/nodes/Facebook/FacebookGraphApi.node.js", - "dist/nodes/FileMaker/FileMaker.node.js", - "dist/nodes/Freshdesk/Freshdesk.node.js", - "dist/nodes/Flow/Flow.node.js", - "dist/nodes/Flow/FlowTrigger.node.js", - "dist/nodes/Function.node.js", - "dist/nodes/FunctionItem.node.js", - "dist/nodes/Github/Github.node.js", - "dist/nodes/Github/GithubTrigger.node.js", - "dist/nodes/Gitlab/Gitlab.node.js", - "dist/nodes/Gitlab/GitlabTrigger.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", - "dist/nodes/HelpScout/HelpScout.node.js", - "dist/nodes/HelpScout/HelpScoutTrigger.node.js", - "dist/nodes/HtmlExtract/HtmlExtract.node.js", - "dist/nodes/HttpRequest.node.js", - "dist/nodes/Hubspot/Hubspot.node.js", - "dist/nodes/Hubspot/HubspotTrigger.node.js", - "dist/nodes/Hunter/Hunter.node.js", - "dist/nodes/If.node.js", - "dist/nodes/Intercom/Intercom.node.js", - "dist/nodes/Interval.node.js", - "dist/nodes/InvoiceNinja/InvoiceNinja.node.js", - "dist/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.js", - "dist/nodes/Jira/Jira.node.js", - "dist/nodes/JotForm/JotFormTrigger.node.js", - "dist/nodes/Keap/Keap.node.js", - "dist/nodes/Keap/KeapTrigger.node.js", - "dist/nodes/LinkFish/LinkFish.node.js", - "dist/nodes/Mailchimp/Mailchimp.node.js", - "dist/nodes/Mailchimp/MailchimpTrigger.node.js", - "dist/nodes/Mailgun/Mailgun.node.js", - "dist/nodes/Mailjet/Mailjet.node.js", - "dist/nodes/Mailjet/MailjetTrigger.node.js", - "dist/nodes/Mandrill/Mandrill.node.js", - "dist/nodes/Mattermost/Mattermost.node.js", - "dist/nodes/Mautic/Mautic.node.js", - "dist/nodes/Mautic/MauticTrigger.node.js", - "dist/nodes/Merge.node.js", - "dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js", - "dist/nodes/Microsoft/OneDrive/MicrosoftOneDrive.node.js", - "dist/nodes/MoveBinaryData.node.js", - "dist/nodes/Mocean/Mocean.node.js", - "dist/nodes/MondayCom/MondayCom.node.js", - "dist/nodes/MongoDb/MongoDb.node.js", - "dist/nodes/MoveBinaryData.node.js", - "dist/nodes/Msg91/Msg91.node.js", - "dist/nodes/MySql/MySql.node.js", - "dist/nodes/NextCloud/NextCloud.node.js", - "dist/nodes/NoOp.node.js", - "dist/nodes/OpenWeatherMap.node.js", - "dist/nodes/PagerDuty/PagerDuty.node.js", - "dist/nodes/PayPal/PayPal.node.js", - "dist/nodes/PayPal/PayPalTrigger.node.js", - "dist/nodes/Pipedrive/Pipedrive.node.js", - "dist/nodes/Pipedrive/PipedriveTrigger.node.js", - "dist/nodes/Postgres/Postgres.node.js", - "dist/nodes/ReadBinaryFile.node.js", - "dist/nodes/ReadBinaryFiles.node.js", - "dist/nodes/ReadPdf.node.js", - "dist/nodes/Redis/Redis.node.js", - "dist/nodes/RenameKeys.node.js", - "dist/nodes/Rocketchat/Rocketchat.node.js", - "dist/nodes/RssFeedRead.node.js", - "dist/nodes/Rundeck/Rundeck.node.js", - "dist/nodes/Salesforce/Salesforce.node.js", - "dist/nodes/Set.node.js", - "dist/nodes/Shopify/Shopify.node.js", - "dist/nodes/Shopify/ShopifyTrigger.node.js", - "dist/nodes/Slack/Slack.node.js", - "dist/nodes/Sms77/Sms77.node.js", - "dist/nodes/SplitInBatches.node.js", - "dist/nodes/SpreadsheetFile.node.js", - "dist/nodes/SseTrigger.node.js", - "dist/nodes/Start.node.js", - "dist/nodes/Stripe/StripeTrigger.node.js", - "dist/nodes/Switch.node.js", - "dist/nodes/Salesmate/Salesmate.node.js", - "dist/nodes/Segment/Segment.node.js", - "dist/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.js", - "dist/nodes/Telegram/Telegram.node.js", - "dist/nodes/Telegram/TelegramTrigger.node.js", - "dist/nodes/Todoist/Todoist.node.js", - "dist/nodes/Toggl/TogglTrigger.node.js", - "dist/nodes/Trello/Trello.node.js", - "dist/nodes/Trello/TrelloTrigger.node.js", - "dist/nodes/Twilio/Twilio.node.js", - "dist/nodes/Twitter/Twitter.node.js", - "dist/nodes/Typeform/TypeformTrigger.node.js", - "dist/nodes/Uplead/Uplead.node.js", - "dist/nodes/Vero/Vero.node.js", - "dist/nodes/Webflow/WebflowTrigger.node.js", - "dist/nodes/Webhook.node.js", - "dist/nodes/Wordpress/Wordpress.node.js", - "dist/nodes/WooCommerce/WooCommerce.node.js", - "dist/nodes/WooCommerce/WooCommerceTrigger.node.js", - "dist/nodes/WriteBinaryFile.node.js", - "dist/nodes/Xml.node.js", - "dist/nodes/Zendesk/Zendesk.node.js", - "dist/nodes/Zendesk/ZendeskTrigger.node.js", - "dist/nodes/Zoho/ZohoCrm.node.js", - "dist/nodes/Zulip/Zulip.node.js" - ] - }, - "devDependencies": { - "@types/aws4": "^1.5.1", - "@types/basic-auth": "^1.1.2", - "@types/cheerio": "^0.22.15", - "@types/cron": "^1.6.1", - "@types/eventsource": "^1.1.2", - "@types/express": "^4.16.1", - "@types/formidable": "^1.0.31", - "@types/gm": "^1.18.2", - "@types/imap-simple": "^4.2.0", - "@types/jest": "^24.0.18", - "@types/lodash.set": "^4.3.6", - "@types/moment-timezone": "^0.5.12", - "@types/mongodb": "^3.5.4", - "@types/node": "^10.10.1", - "@types/nodemailer": "^6.4.0", - "@types/redis": "^2.8.11", - "@types/request-promise-native": "~1.0.15", - "@types/uuid": "^3.4.6", - "@types/xml2js": "^0.4.3", - "gulp": "^4.0.0", - "jest": "^24.9.0", - "n8n-workflow": "~0.31.0", - "ts-jest": "^24.0.2", - "tslint": "^5.17.0", - "typescript": "~3.7.4" - }, - "dependencies": { - "aws4": "^1.8.0", - "basic-auth": "^2.0.1", - "change-case": "^4.1.1", - "cheerio": "^1.0.0-rc.3", - "cron": "^1.7.2", - "eventsource": "^1.0.7", - "formidable": "^1.2.1", - "glob-promise": "^3.4.0", - "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", - "moment": "2.24.0", - "moment-timezone": "^0.5.28", - "mongodb": "^3.5.5", - "mysql2": "^2.0.1", - "n8n-core": "~0.34.0", - "nodemailer": "^6.4.6", - "pdf-parse": "^1.1.1", - "pg-promise": "^9.0.3", - "redis": "^2.8.0", - "request": "^2.88.2", - "rhea": "^1.0.11", - "rss-parser": "^3.7.0", - "uuid": "^3.4.0", - "vm2": "^3.6.10", - "xlsx": "^0.14.3", - "xml2js": "^0.4.22" - }, - "jest": { - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testURL": "http://localhost/", - "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", - "testPathIgnorePatterns": [ - "/dist/", - "/node_modules/" - ], - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ] - } + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ] + } }