From 01a2dd13d5bb0d46ccac0764ab0077ceb63ab72a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 22 Jan 2020 00:17:29 -0800 Subject: [PATCH 1/2] :zap: Small improvements for Salesmate-Node --- .../nodes/Salesmate/ActivityDescription.ts | 22 ++--- .../nodes/Salesmate/CompanyDescription.ts | 22 ++--- .../nodes/Salesmate/DealDescription.ts | 22 ++--- .../nodes/Salesmate/GenericFunctions.ts | 18 ++++ .../nodes/Salesmate/Salesmate.node.ts | 87 ++++++++++++++----- 5 files changed, 119 insertions(+), 52 deletions(-) diff --git a/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts b/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts index fe737729a7..656bb8f15f 100644 --- a/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts +++ b/packages/nodes-base/nodes/Salesmate/ActivityDescription.ts @@ -19,9 +19,9 @@ export const activityOperations = [ description: 'Create a activity', }, { - name: 'Update', - value: 'update', - description: 'Update a activity', + name: 'Delete', + value: 'delete', + description: 'Delete a activity', }, { name: 'Get', @@ -34,9 +34,9 @@ export const activityOperations = [ description: 'Get all companies', }, { - name: 'Delete', - value: 'delete', - description: 'Delete a activity', + name: 'Update', + value: 'update', + description: 'Update a activity', }, ], default: 'create', @@ -427,12 +427,14 @@ export const activityFields = [ name: 'fields', type: 'string', default: '', + description: 'Comma separated list of fields to return.', }, { displayName: 'Sort By', name: 'sortBy', type: 'string', default: '', + description: 'The field to sort by.', }, { displayName: 'Sort Order', @@ -440,11 +442,11 @@ export const activityFields = [ type: 'options', options: [ { - name: 'Asc', + name: 'ASC', value: 'asc', }, { - name: 'Desc', + name: 'DESC', value: 'desc', }, ], @@ -508,11 +510,11 @@ export const activityFields = [ type: 'options', options: [ { - name: 'And', + name: 'AND', value: 'AND', }, { - name: 'Or', + name: 'OR', value: 'OR', }, ], diff --git a/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts b/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts index a2dbc2e762..dbf20eb1bd 100644 --- a/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts +++ b/packages/nodes-base/nodes/Salesmate/CompanyDescription.ts @@ -19,9 +19,9 @@ export const companyOperations = [ description: 'Create a company', }, { - name: 'Update', - value: 'update', - description: 'Update a company', + name: 'Delete', + value: 'delete', + description: 'Delete a company', }, { name: 'Get', @@ -34,9 +34,9 @@ export const companyOperations = [ description: 'Get all companies', }, { - name: 'Delete', - value: 'delete', - description: 'Delete a company', + name: 'Update', + value: 'update', + description: 'Update a company', }, ], default: 'create', @@ -520,12 +520,14 @@ export const companyFields = [ name: 'fields', type: 'string', default: '', + description: 'Comma separated list of fields to return.', }, { displayName: 'Sort By', name: 'sortBy', type: 'string', default: '', + description: 'The field to sort by.', }, { displayName: 'Sort Order', @@ -533,11 +535,11 @@ export const companyFields = [ type: 'options', options: [ { - name: 'Asc', + name: 'ASC', value: 'asc', }, { - name: 'Desc', + name: 'DESC', value: 'desc', }, ], @@ -601,11 +603,11 @@ export const companyFields = [ type: 'options', options: [ { - name: 'And', + name: 'AND', value: 'AND', }, { - name: 'Or', + name: 'OR', value: 'OR', }, ], diff --git a/packages/nodes-base/nodes/Salesmate/DealDescription.ts b/packages/nodes-base/nodes/Salesmate/DealDescription.ts index 343c02b55a..b4291c872d 100644 --- a/packages/nodes-base/nodes/Salesmate/DealDescription.ts +++ b/packages/nodes-base/nodes/Salesmate/DealDescription.ts @@ -19,9 +19,9 @@ export const dealOperations = [ description: 'Create a deal', }, { - name: 'Update', - value: 'update', - description: 'Update a deal', + name: 'Delete', + value: 'delete', + description: 'Delete a deal', }, { name: 'Get', @@ -34,9 +34,9 @@ export const dealOperations = [ description: 'Get all companies', }, { - name: 'Delete', - value: 'delete', - description: 'Delete a deal', + name: 'Update', + value: 'update', + description: 'Update a deal', }, ], default: 'create', @@ -691,12 +691,14 @@ export const dealFields = [ name: 'fields', type: 'string', default: '', + description: 'Comma separated list of fields to return.', }, { displayName: 'Sort By', name: 'sortBy', type: 'string', default: '', + description: 'The field to sort by.', }, { displayName: 'Sort Order', @@ -704,11 +706,11 @@ export const dealFields = [ type: 'options', options: [ { - name: 'Asc', + name: 'ASC', value: 'asc', }, { - name: 'Desc', + name: 'DESC', value: 'desc', }, ], @@ -772,11 +774,11 @@ export const dealFields = [ type: 'options', options: [ { - name: 'And', + name: 'AND', value: 'AND', }, { - name: 'Or', + name: 'OR', value: 'OR', }, ], diff --git a/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts b/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts index a84dbc95e2..1471ae4a6c 100644 --- a/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts @@ -65,3 +65,21 @@ export function validateJSON(json: string | undefined): any { // tslint:disable- } return result; } + + +/** + * Converts data from the Salesmate format into a simple object + * + * @export + * @param {IDataObject[]} data + * @returns {IDataObject} + */ +export function simplifySalesmateData(data: IDataObject[]): IDataObject { + const returnData: IDataObject = {}; + + for (const item of data) { + returnData[item.fieldName as string] = item.value; + } + + return returnData; +} diff --git a/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts b/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts index a57b8b7410..e8f5a49528 100644 --- a/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts +++ b/packages/nodes-base/nodes/Salesmate/Salesmate.node.ts @@ -12,6 +12,7 @@ import { import { salesmateApiRequest, salesmateApiRequestAllItems, + simplifySalesmateData, validateJSON, } from './GenericFunctions'; import { @@ -289,12 +290,9 @@ export class Salesmate implements INodeType { const rawData = this.getNodeParameter('rawData', i) as boolean; responseData = await salesmateApiRequest.call(this, 'GET', `/v1/companies/${companyId}`); responseData = responseData.Data; + if (!rawData) { - responseData = responseData.map((company: IDataObject) => { - const aux: IDataObject = {}; - aux[company.fieldName as string] = company.value; - return aux; - }); + responseData = simplifySalesmateData(responseData); } } if (operation === 'getAll') { @@ -314,9 +312,26 @@ export class Salesmate implements INodeType { qs.sortOrder = options.sortOrder as string; } if (options.fields) { + if ((options.fields as string).trim() === '') { + throw new Error('You have to add at least one field'); + } body.fields = (options.fields as string).split(',') as string[]; } else { - throw new Error('You have to add at least one field'); + body.fields = [ + 'name', + 'description', + 'billingAddressLine1', + 'billingAddressLine2', + 'billingCity', + 'billingZipCode', + 'billingState', + 'billingCountry', + 'website', + 'owner', + 'tags', + 'photo', + 'createdAt', + ]; } if (!jsonActive) { const filters: IDataObject[] = []; @@ -332,7 +347,7 @@ export class Salesmate implements INodeType { }; filter.condition = condition.condition; filter.data = condition.value; - filters.push(filter) + filters.push(filter); } } } @@ -440,12 +455,9 @@ export class Salesmate implements INodeType { const rawData = this.getNodeParameter('rawData', i) as boolean; responseData = await salesmateApiRequest.call(this, 'GET', `/v1/activities/${activityId}`); responseData = responseData.Data; + if (!rawData) { - responseData = responseData.map((activity: IDataObject) => { - const aux: IDataObject = {}; - aux[activity.fieldName as string] = activity.value; - return aux; - }); + responseData = simplifySalesmateData(responseData); } } if (operation === 'getAll') { @@ -465,9 +477,27 @@ export class Salesmate implements INodeType { qs.sortOrder = options.sortOrder as string; } if (options.fields) { + if ((options.fields as string).trim() === '') { + throw new Error('You have to add at least one field'); + } body.fields = (options.fields as string).split(',') as string[]; } else { - throw new Error('You have to add at least one field'); + body.fields = [ + 'title', + 'dueDate', + 'description', + 'duration', + 'owner', + 'Deal.title', + 'PrimaryContact.name', + 'PrimaryContact.email', + 'PrimaryCompany.name', + 'PrimaryCompany.email', + 'tags', + 'type', + 'createdAt', + 'isCompleted', + ]; } if (!jsonActive) { const filters: IDataObject[] = []; @@ -483,7 +513,7 @@ export class Salesmate implements INodeType { }; filter.condition = condition.condition; filter.data = condition.value; - filters.push(filter) + filters.push(filter); } } } @@ -617,12 +647,9 @@ export class Salesmate implements INodeType { const rawData = this.getNodeParameter('rawData', i) as boolean; responseData = await salesmateApiRequest.call(this, 'GET', `/v1/deals/${dealId}`); responseData = responseData.Data; + if (!rawData) { - responseData = responseData.map((deal: IDataObject) => { - const aux: IDataObject = {}; - aux[deal.fieldName as string] = deal.value; - return aux; - }); + responseData = simplifySalesmateData(responseData); } } if (operation === 'getAll') { @@ -641,10 +668,26 @@ export class Salesmate implements INodeType { if (options.sortOrder) { qs.sortOrder = options.sortOrder as string; } - if (options.fields) { + if (options.fields !== undefined) { + if ((options.fields as string).trim() === '') { + throw new Error('You have to add at least one field'); + } body.fields = (options.fields as string).split(',') as string[]; } else { - throw new Error('You have to add at least one field'); + body.fields = [ + 'title', + 'PrimaryContact.name', + 'PrimaryContact.email', + 'PrimaryCompany.name', + 'PrimaryCompany.email', + 'dealValue', + 'priority', + 'stage', + 'status', + 'owner', + 'tags', + 'createdAt', + ]; } if (!jsonActive) { const filters: IDataObject[] = []; @@ -660,7 +703,7 @@ export class Salesmate implements INodeType { }; filter.condition = condition.condition; filter.data = condition.value; - filters.push(filter) + filters.push(filter); } } } From aa1899f81d70489740aa1f05706894dcab4625a0 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 22 Jan 2020 15:06:43 -0800 Subject: [PATCH 2/2] :zap: Prepare webhooks for multitenancy --- packages/cli/src/ActiveWorkflowRunner.ts | 48 +++++++------ packages/cli/src/Server.ts | 13 +++- packages/cli/src/TestWebhooks.ts | 53 ++++++++++++--- packages/cli/src/WebhookHelpers.ts | 18 ++--- packages/core/src/ActiveWebhooks.ts | 87 ++++++++---------------- packages/core/src/ActiveWorkflows.ts | 5 +- packages/core/src/Interfaces.ts | 2 - packages/workflow/src/Interfaces.ts | 2 +- packages/workflow/src/NodeHelpers.ts | 2 +- packages/workflow/src/Workflow.ts | 35 ---------- 10 files changed, 121 insertions(+), 144 deletions(-) diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index 9604fd9de6..25255988dc 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -117,22 +117,24 @@ export class ActiveWorkflowRunner { throw new ResponseHelper.ResponseError('The requested webhook is not registred.', 404, 404); } + const workflowData = await Db.collections.Workflow!.findOne(webhookData.workflowId); + if (workflowData === undefined) { + throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhookData.workflowId}"`, 404, 404); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(webhookData.workflowId, workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + // Get the node which has the webhook defined to know where to start from and to // get additional data - const workflowStartNode = webhookData.workflow.getNode(webhookData.node); + const workflowStartNode = workflow.getNode(webhookData.node); if (workflowStartNode === null) { throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404); } - const executionMode = 'webhook'; - - const workflowData = await Db.collections.Workflow!.findOne(webhookData.workflow.id!); - - if (workflowData === undefined) { - throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhookData.workflow.id}"`, 404, 404); - } return new Promise((resolve, reject) => { - WebhookHelpers.executeWebhook(webhookData, workflowData, workflowStartNode, executionMode, undefined, req, res, (error: Error | null, data: object) => { + const executionMode = 'webhook'; + WebhookHelpers.executeWebhook(workflow, webhookData, workflowData, workflowStartNode, executionMode, undefined, req, res, (error: Error | null, data: object) => { if (error !== null) { return reject(error); } @@ -202,7 +204,9 @@ export class ActiveWorkflowRunner { const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData); for (const webhookData of webhooks) { - await this.activeWebhooks!.add(webhookData, mode); + await this.activeWebhooks!.add(workflow, webhookData, mode); + // Save static data! + await WorkflowHelpers.saveStaticData(workflow); } } @@ -214,8 +218,19 @@ export class ActiveWorkflowRunner { * @returns * @memberof ActiveWorkflowRunner */ - removeWorkflowWebhooks(workflowId: string): Promise { - return this.activeWebhooks!.removeByWorkflowId(workflowId); + async removeWorkflowWebhooks(workflowId: string): Promise { + const workflowData = await Db.collections.Workflow!.findOne(workflowId); + if (workflowData === undefined) { + throw new Error(`Could not find workflow with id "${workflowId}"`); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(workflowId, workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + + await this.activeWebhooks!.removeWorkflow(workflow); + + // Save the static workflow data if needed + await WorkflowHelpers.saveStaticData(workflow); } @@ -348,7 +363,7 @@ export class ActiveWorkflowRunner { await this.activeWorkflows.add(workflowId, workflowInstance, additionalData, getTriggerFunctions, getPollFunctions); if (this.activationErrors[workflowId] !== undefined) { - // If there were any activation errors delete them + // If there were activation errors delete them delete this.activationErrors[workflowId]; } } catch (error) { @@ -380,16 +395,9 @@ export class ActiveWorkflowRunner { */ async remove(workflowId: string): Promise { if (this.activeWorkflows !== null) { - const workflowData = this.activeWorkflows.get(workflowId); - // Remove all the webhooks of the workflow await this.removeWorkflowWebhooks(workflowId); - if (workflowData) { - // Save the static workflow data if needed - await WorkflowHelpers.saveStaticData(workflowData.workflow); - } - if (this.activationErrors[workflowId] !== undefined) { // If there were any activation errors delete them delete this.activationErrors[workflowId]; diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 8fff1ef217..de561725ed 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -132,7 +132,7 @@ class App { const authIgnoreRegex = new RegExp(`^\/(rest|healthz|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`); // Check for basic auth credentials if activated - const basicAuthActive = config.get('security.basicAuth.active') as boolean; + const basicAuthActive = config.get('security.basicAuth.active') as boolean; if (basicAuthActive === true) { const basicAuthUser = await GenericHelpers.getConfigValue('security.basicAuth.user') as string; if (basicAuthUser === '') { @@ -1072,7 +1072,16 @@ class App { // Removes a test webhook this.app.delete('/rest/test-webhook/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { const workflowId = req.params.id; - return this.testWebhooks.cancelTestWebhook(workflowId); + + const workflowData = await Db.collections.Workflow!.findOne(workflowId); + if (workflowData === undefined) { + throw new ResponseHelper.ResponseError(`Could not find workflow with id "${workflowId}" so webhook could not be deleted!`); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(workflowId.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + + return this.testWebhooks.cancelTestWebhook(workflowId, workflow); })); diff --git a/packages/cli/src/TestWebhooks.ts b/packages/cli/src/TestWebhooks.ts index 540aa9e0f8..8631f259ab 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -1,11 +1,18 @@ import * as express from 'express'; +import { + In as findIn, + FindManyOptions, +} from 'typeorm'; import { + Db, IResponseCallbackData, + IWorkflowDb, + NodeTypes, Push, ResponseHelper, WebhookHelpers, - IWorkflowDb, + WorkflowHelpers, } from './'; import { @@ -60,9 +67,17 @@ export class TestWebhooks { throw new ResponseHelper.ResponseError('The requested webhook is not registred.', 404, 404); } + const workflowData = await Db.collections.Workflow!.findOne(webhookData.workflowId); + if (workflowData === undefined) { + throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhookData.workflowId}"`, 404, 404); + } + + const nodeTypes = NodeTypes(); + const workflow = new Workflow(webhookData.workflowId, workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + // Get the node which has the webhook defined to know where to start from and to // get additional data - const workflowStartNode = webhookData.workflow.getNode(webhookData.node); + const workflowStartNode = workflow.getNode(webhookData.node); if (workflowStartNode === null) { throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404); } @@ -72,8 +87,7 @@ export class TestWebhooks { return new Promise(async (resolve, reject) => { try { const executionMode = 'manual'; - - const executionId = await WebhookHelpers.executeWebhook(webhookData, this.testWebhookData[webhookKey].workflowData, workflowStartNode, executionMode, this.testWebhookData[webhookKey].sessionId, request, response, (error: Error | null, data: IResponseCallbackData) => { + const executionId = await WebhookHelpers.executeWebhook(workflow, webhookData, this.testWebhookData[webhookKey].workflowData, workflowStartNode, executionMode, this.testWebhookData[webhookKey].sessionId, request, response, (error: Error | null, data: IResponseCallbackData) => { if (error !== null) { return reject(error); } @@ -90,7 +104,7 @@ export class TestWebhooks { // Inform editor-ui that webhook got received if (this.testWebhookData[webhookKey].sessionId !== undefined) { const pushInstance = Push.getInstance(); - pushInstance.send('testWebhookReceived', { workflowId: webhookData.workflow.id, executionId }, this.testWebhookData[webhookKey].sessionId!); + pushInstance.send('testWebhookReceived', { workflowId: webhookData.workflowId, executionId }, this.testWebhookData[webhookKey].sessionId!); } } catch (error) { @@ -100,7 +114,7 @@ export class TestWebhooks { // Remove the webhook clearTimeout(this.testWebhookData[webhookKey].timeout); delete this.testWebhookData[webhookKey]; - this.activeWebhooks!.removeByWorkflowId(webhookData.workflow.id!.toString()); + this.activeWebhooks!.removeWorkflow(workflow); }); } @@ -125,7 +139,7 @@ export class TestWebhooks { // Remove test-webhooks automatically if they do not get called (after 120 seconds) const timeout = setTimeout(() => { - this.cancelTestWebhook(workflowData.id.toString()); + this.cancelTestWebhook(workflowData.id.toString(), workflow); }, 120000); let key: string; @@ -136,9 +150,12 @@ export class TestWebhooks { timeout, workflowData, }; - await this.activeWebhooks!.add(webhookData, mode); + await this.activeWebhooks!.add(workflow, webhookData, mode); } + // Save static data! + await WorkflowHelpers.saveStaticData(workflow); + return true; } @@ -150,7 +167,7 @@ export class TestWebhooks { * @returns {boolean} * @memberof TestWebhooks */ - cancelTestWebhook(workflowId: string): boolean { + cancelTestWebhook(workflowId: string, workflow: Workflow): boolean { let foundWebhook = false; for (const webhookKey of Object.keys(this.testWebhookData)) { const webhookData = this.testWebhookData[webhookKey]; @@ -175,7 +192,7 @@ export class TestWebhooks { // Remove the webhook delete this.testWebhookData[webhookKey]; - this.activeWebhooks!.removeByWorkflowId(workflowId); + this.activeWebhooks!.removeWorkflow(workflow); } return foundWebhook; @@ -189,8 +206,22 @@ export class TestWebhooks { if (this.activeWebhooks === null) { return; } + const nodeTypes = NodeTypes(); - return this.activeWebhooks.removeAll(); + const findQuery = { + where: { + id: findIn(this.activeWebhooks.getWorkflowIds()) + }, + } as FindManyOptions; + + const workflowsDb = await Db.collections.Workflow!.find(findQuery); + const workflows: Workflow[] = []; + for (const workflowData of workflowsDb) { + const workflow = new Workflow(workflowData.id.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings); + workflows.push(workflow); + } + + return this.activeWebhooks.removeAll(workflows); } } diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index b13cc3b0ea..da95f7416a 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -84,9 +84,9 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo * @param {((error: Error | null, data: IResponseCallbackData) => void)} responseCallback * @returns {(Promise)} */ - export async function executeWebhook(webhookData: IWebhookData, workflowData: IWorkflowDb, workflowStartNode: INode, executionMode: WorkflowExecuteMode, sessionId: string | undefined, req: express.Request, res: express.Response, responseCallback: (error: Error | null, data: IResponseCallbackData) => void): Promise { + export async function executeWebhook(workflow: Workflow, webhookData: IWebhookData, workflowData: IWorkflowDb, workflowStartNode: INode, executionMode: WorkflowExecuteMode, sessionId: string | undefined, req: express.Request, res: express.Response, responseCallback: (error: Error | null, data: IResponseCallbackData) => void): Promise { // Get the nodeType to know which responseMode is set - const nodeType = webhookData.workflow.nodeTypes.getByName(workflowStartNode.type); + const nodeType = workflow.nodeTypes.getByName(workflowStartNode.type); if (nodeType === undefined) { const errorMessage = `The type of the webhook node "${workflowStartNode.name}" is not known.`; responseCallback(new Error(errorMessage), {}); @@ -94,8 +94,8 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo } // Get the responseMode - const responseMode = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived'); - const responseCode = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number; + const responseMode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived'); + const responseCode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number; if (!['onReceived', 'lastNode'].includes(responseMode as string)) { // If the mode is not known we error. Is probably best like that instead of using @@ -122,7 +122,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo let webhookResultData: IWebhookResponseData; try { - webhookResultData = await webhookData.workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode); + webhookResultData = await workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode); } catch (e) { // Send error response to webhook caller const errorMessage = 'Workflow Webhook Error: Workflow could not be started!'; @@ -287,7 +287,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo return data; } - const responseData = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson'); + const responseData = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson'); if (didSendResponse === false) { let data: IDataObject | IDataObject[]; @@ -296,13 +296,13 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo // Return the JSON data of the first entry data = returnData.data!.main[0]![0].json; - const responsePropertyName = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined); + const responsePropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined); if (responsePropertyName !== undefined) { data = get(data, responsePropertyName as string) as IDataObject; } - const responseContentType = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined); + const responseContentType = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined); if (responseContentType !== undefined) { // Send the webhook response manually to be able to set the content-type @@ -329,7 +329,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo didSendResponse = true; } - const responseBinaryPropertyName = webhookData.workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data'); + const responseBinaryPropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data'); if (responseBinaryPropertyName === undefined && didSendResponse === false) { responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {}); diff --git a/packages/core/src/ActiveWebhooks.ts b/packages/core/src/ActiveWebhooks.ts index 6468044d47..17cf753830 100644 --- a/packages/core/src/ActiveWebhooks.ts +++ b/packages/core/src/ActiveWebhooks.ts @@ -1,6 +1,7 @@ import { IWebhookData, WebhookHttpMethod, + Workflow, WorkflowExecuteMode, } from 'n8n-workflow'; @@ -29,29 +30,26 @@ export class ActiveWebhooks { * @returns {Promise} * @memberof ActiveWebhooks */ - async add(webhookData: IWebhookData, mode: WorkflowExecuteMode): Promise { - if (webhookData.workflow.id === undefined) { + async add(workflow: Workflow, webhookData: IWebhookData, mode: WorkflowExecuteMode): Promise { + if (workflow.id === undefined) { throw new Error('Webhooks can only be added for saved workflows as an id is needed!'); } - if (this.workflowWebhooks[webhookData.workflow.id] === undefined) { - this.workflowWebhooks[webhookData.workflow.id] = []; + if (this.workflowWebhooks[webhookData.workflowId] === undefined) { + this.workflowWebhooks[webhookData.workflowId] = []; } // Make the webhook available directly because sometimes to create it successfully // it gets called this.webhookUrls[this.getWebhookKey(webhookData.httpMethod, webhookData.path)] = webhookData; - const webhookExists = await webhookData.workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); + const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); if (webhookExists === false) { // If webhook does not exist yet create it - await webhookData.workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); + await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); } - // Run the "activate" hooks on the nodes - await webhookData.workflow.runNodeHooks('activate', webhookData, NodeExecuteFunctions, mode); - - this.workflowWebhooks[webhookData.workflow.id].push(webhookData); + this.workflowWebhooks[webhookData.workflowId].push(webhookData); } @@ -73,6 +71,17 @@ export class ActiveWebhooks { } + /** + * Returns the ids of all the workflows which have active webhooks + * + * @returns {string[]} + * @memberof ActiveWebhooks + */ + getWorkflowIds(): string[] { + return Object.keys(this.workflowWebhooks); + } + + /** * Returns key to uniquely identify a webhook * @@ -89,11 +98,13 @@ export class ActiveWebhooks { /** * Removes all webhooks of a workflow * - * @param {string} workflowId + * @param {Workflow} workflow * @returns {boolean} * @memberof ActiveWebhooks */ - async removeByWorkflowId(workflowId: string): Promise { + async removeWorkflow(workflow: Workflow): Promise { + const workflowId = workflow.id!.toString(); + if (this.workflowWebhooks[workflowId] === undefined) { // If it did not exist then there is nothing to remove return false; @@ -105,10 +116,7 @@ export class ActiveWebhooks { // Go through all the registered webhooks of the workflow and remove them for (const webhookData of webhooks) { - await webhookData.workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); - - // Run the "deactivate" hooks on the nodes - await webhookData.workflow.runNodeHooks('deactivate', webhookData, NodeExecuteFunctions, mode); + await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); delete this.webhookUrls[this.getWebhookKey(webhookData.httpMethod, webhookData.path)]; } @@ -121,55 +129,16 @@ export class ActiveWebhooks { /** - * Removes all the currently active webhooks + * Removes all the webhooks of the given workflow */ - async removeAll(): Promise { - const workflowIds = Object.keys(this.workflowWebhooks); - + async removeAll(workflows: Workflow[]): Promise { const removePromises = []; - for (const workflowId of workflowIds) { - removePromises.push(this.removeByWorkflowId(workflowId)); + for (const workflow of workflows) { + removePromises.push(this.removeWorkflow(workflow)); } await Promise.all(removePromises); return; } - - // /** - // * Removes a single webhook by its key. - // * Currently not used, runNodeHooks for "deactivate" is missing - // * - // * @param {string} webhookKey - // * @returns {boolean} - // * @memberof ActiveWebhooks - // */ - // removeByWebhookKey(webhookKey: string): boolean { - // if (this.webhookUrls[webhookKey] === undefined) { - // // If it did not exist then there is nothing to remove - // return false; - // } - - // const webhookData = this.webhookUrls[webhookKey]; - - // // Remove from workflow-webhooks - // const workflowWebhooks = this.workflowWebhooks[webhookData.workflowId]; - // for (let index = 0; index < workflowWebhooks.length; index++) { - // if (workflowWebhooks[index].path === webhookData.path) { - // workflowWebhooks.splice(index, 1); - // break; - // } - // } - - // if (workflowWebhooks.length === 0) { - // // When there are no webhooks left for any workflow remove it totally - // delete this.workflowWebhooks[webhookData.workflowId]; - // } - - // // Remove from webhook urls - // delete this.webhookUrls[webhookKey]; - - // return true; - // } - } diff --git a/packages/core/src/ActiveWorkflows.ts b/packages/core/src/ActiveWorkflows.ts index f450a28064..5576aeed3e 100644 --- a/packages/core/src/ActiveWorkflows.ts +++ b/packages/core/src/ActiveWorkflows.ts @@ -69,9 +69,7 @@ export class ActiveWorkflows { async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getTriggerFunctions: IGetExecuteTriggerFunctions, getPollFunctions: IGetExecutePollFunctions): Promise { console.log('ADD ID (active): ' + id); - this.workflowData[id] = { - workflow - }; + this.workflowData[id] = {}; const triggerNodes = workflow.getTriggerNodes(); let triggerResponse: ITriggerResponse | undefined; @@ -170,7 +168,6 @@ export class ActiveWorkflows { const pollResponse = await workflow.runPoll(node, pollFunctions); if (pollResponse !== null) { - // TODO: Run workflow pollFunctions.__emit(pollResponse); } }; diff --git a/packages/core/src/Interfaces.ts b/packages/core/src/Interfaces.ts index a45d393dc3..cfbc19bb8e 100644 --- a/packages/core/src/Interfaces.ts +++ b/packages/core/src/Interfaces.ts @@ -14,7 +14,6 @@ import { ITriggerResponse, IWebhookFunctions as IWebhookFunctionsBase, IWorkflowSettings as IWorkflowSettingsWorkflow, - Workflow, } from 'n8n-workflow'; @@ -125,5 +124,4 @@ export interface INodeInputDataConnections { export interface IWorkflowData { pollResponses?: IPollResponse[]; triggerResponses?: ITriggerResponse[]; - workflow: Workflow; } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 8fd424ba19..2070215911 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -495,7 +495,7 @@ export interface IWebhookData { node: string; path: string; webhookDescription: IWebhookDescription; - workflow: Workflow; + workflowId: string; workflowExecuteAdditionalData: IWorkflowExecuteAdditionalData; } diff --git a/packages/workflow/src/NodeHelpers.ts b/packages/workflow/src/NodeHelpers.ts index e3c6a79b8a..76d8f8b55f 100644 --- a/packages/workflow/src/NodeHelpers.ts +++ b/packages/workflow/src/NodeHelpers.ts @@ -771,7 +771,7 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData: node: node.name, path, webhookDescription, - workflow, + workflowId: workflow.id, workflowExecuteAdditionalData: additionalData, }); } diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 1ee7991391..9bb8391aed 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -907,41 +907,6 @@ export class Workflow { } - /** - * Executes the hooks of the node - * - * @param {string} hookName The name of the hook to execute - * @param {IWebhookData} webhookData - * @param {INodeExecuteFunctions} nodeExecuteFunctions - * @param {WorkflowExecuteMode} mode - * @returns {Promise} - * @memberof Workflow - */ - async runNodeHooks(hookName: string, webhookData: IWebhookData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode): Promise { - const node = this.getNode(webhookData.node) as INode; - const nodeType = this.nodeTypes.getByName(node.type) as INodeType; - - if (nodeType.description.hooks === undefined) { - return; - } - - - if (nodeType.description.hooks[hookName] === undefined) { - return; - } - - - if (nodeType.hooks === undefined && nodeType.description.hooks[hookName]!.length !== 0) { - // There should be hook functions but they do not exist - throw new Error('There are hooks defined to run but are not implemented.'); - } - - for (const hookDescription of nodeType.description.hooks[hookName]!) { - const thisArgs = nodeExecuteFunctions.getExecuteHookFunctions(this, node, webhookData.workflowExecuteAdditionalData, mode); - await nodeType.hooks![hookDescription.method].call(thisArgs); - } - } - /** * Executes the Webhooks method of the node