mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
Done
This commit is contained in:
parent
f9db4bb501
commit
91c40367e1
|
@ -11,11 +11,11 @@ import {
|
|||
WorkflowHelpers,
|
||||
WorkflowRunner,
|
||||
WorkflowExecuteAdditionalData,
|
||||
IWebhookDb,
|
||||
} from './';
|
||||
|
||||
import {
|
||||
ActiveWorkflows,
|
||||
ActiveWebhooks,
|
||||
NodeExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
|
@ -26,7 +26,7 @@ import {
|
|||
INode,
|
||||
INodeExecutionData,
|
||||
IRunExecutionData,
|
||||
IWebhookData,
|
||||
NodeHelpers,
|
||||
IWorkflowExecuteAdditionalData as IWorkflowExecuteAdditionalDataWorkflow,
|
||||
WebhookHttpMethod,
|
||||
Workflow,
|
||||
|
@ -38,19 +38,21 @@ import * as express from 'express';
|
|||
|
||||
export class ActiveWorkflowRunner {
|
||||
private activeWorkflows: ActiveWorkflows | null = null;
|
||||
private activeWebhooks: ActiveWebhooks | null = null;
|
||||
|
||||
private activationErrors: {
|
||||
[key: string]: IActivationError;
|
||||
} = {};
|
||||
|
||||
|
||||
async init() {
|
||||
|
||||
// Get the active workflows from database
|
||||
|
||||
// NOTE
|
||||
// Here I guess we can have a flag on the workflow table like hasTrigger
|
||||
// so intead of pulling all the active wehhooks just pull the actives that have a trigger
|
||||
const workflowsData: IWorkflowDb[] = await Db.collections.Workflow!.find({ active: true }) as IWorkflowDb[];
|
||||
|
||||
this.activeWebhooks = new ActiveWebhooks();
|
||||
|
||||
// Add them as active workflows
|
||||
this.activeWorkflows = new ActiveWorkflows();
|
||||
|
||||
if (workflowsData.length !== 0) {
|
||||
|
@ -58,20 +60,27 @@ export class ActiveWorkflowRunner {
|
|||
console.log(' Start Active Workflows:');
|
||||
console.log(' ================================');
|
||||
|
||||
const nodeTypes = NodeTypes();
|
||||
|
||||
for (const workflowData of workflowsData) {
|
||||
console.log(` - ${workflowData.name}`);
|
||||
try {
|
||||
await this.add(workflowData.id.toString(), workflowData);
|
||||
console.log(` => Started`);
|
||||
} catch (error) {
|
||||
console.log(` => ERROR: Workflow could not be activated:`);
|
||||
console.log(` ${error.message}`);
|
||||
|
||||
const workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings});
|
||||
|
||||
if (workflow.getTriggerNodes().length !== 0
|
||||
|| workflow.getPollNodes().length !== 0) {
|
||||
console.log(` - ${workflowData.name}`);
|
||||
try {
|
||||
await this.add(workflowData.id.toString(), workflowData);
|
||||
console.log(` => Started`);
|
||||
} catch (error) {
|
||||
console.log(` => ERROR: Workflow could not be activated:`);
|
||||
console.log(` ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all the currently active workflows
|
||||
*
|
||||
|
@ -94,7 +103,6 @@ export class ActiveWorkflowRunner {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a webhook for the given method and path exists and executes the workflow.
|
||||
*
|
||||
|
@ -110,30 +118,41 @@ export class ActiveWorkflowRunner {
|
|||
throw new ResponseHelper.ResponseError('The "activeWorkflows" instance did not get initialized yet.', 404, 404);
|
||||
}
|
||||
|
||||
const webhookData: IWebhookData | undefined = this.activeWebhooks!.get(httpMethod, path);
|
||||
const webhook = await Db.collections.Webhook?.findOne({ webhookPath: path, method: httpMethod }) as IWebhookDb;
|
||||
|
||||
if (webhookData === undefined) {
|
||||
// check if something exist
|
||||
if (webhook === undefined) {
|
||||
// The requested webhook is not registered
|
||||
throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404);
|
||||
}
|
||||
|
||||
const workflowData = await Db.collections.Workflow!.findOne(webhookData.workflowId);
|
||||
const workflowData = await Db.collections.Workflow!.findOne(webhook.workflowId);
|
||||
if (workflowData === undefined) {
|
||||
throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhookData.workflowId}"`, 404, 404);
|
||||
throw new ResponseHelper.ResponseError(`Could not find workflow with id "${webhook.workflowId}"`, 404, 404);
|
||||
}
|
||||
|
||||
const nodeTypes = NodeTypes();
|
||||
const workflow = new Workflow({ id: webhookData.workflowId, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings});
|
||||
const workflow = new Workflow({ id: webhook.workflowId.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings});
|
||||
|
||||
const credentials = await WorkflowCredentials([workflow.getNode(webhook.node as string) as INode]);
|
||||
|
||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(credentials);
|
||||
|
||||
const webhookData = NodeHelpers.getNodeWebhooks(workflow, workflow.getNode(webhook.node as string) as INode, additionalData).filter((webhook) => {
|
||||
return (webhook.httpMethod === httpMethod && webhook.path === path);
|
||||
})[0];
|
||||
|
||||
// Get the node which has the webhook defined to know where to start from and to
|
||||
// get additional data
|
||||
const workflowStartNode = workflow.getNode(webhookData.node);
|
||||
|
||||
if (workflowStartNode === null) {
|
||||
throw new ResponseHelper.ResponseError('Could not find node to process webhook.', 404, 404);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const executionMode = 'webhook';
|
||||
//@ts-ignore
|
||||
WebhookHelpers.executeWebhook(workflow, webhookData, workflowData, workflowStartNode, executionMode, undefined, req, res, (error: Error | null, data: object) => {
|
||||
if (error !== null) {
|
||||
return reject(error);
|
||||
|
@ -143,19 +162,14 @@ export class ActiveWorkflowRunner {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ids of the currently active workflows
|
||||
*
|
||||
* @returns {string[]}
|
||||
* @memberof ActiveWorkflowRunner
|
||||
*/
|
||||
getActiveWorkflows(): string[] {
|
||||
if (this.activeWorkflows === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.activeWorkflows.allActiveWorkflows();
|
||||
getActiveWorkflows(): Promise<IWorkflowDb[]> {
|
||||
return Db.collections.Workflow?.find({ select: ['id'] }) as Promise<IWorkflowDb[]>;
|
||||
}
|
||||
|
||||
|
||||
|
@ -166,15 +180,11 @@ export class ActiveWorkflowRunner {
|
|||
* @returns {boolean}
|
||||
* @memberof ActiveWorkflowRunner
|
||||
*/
|
||||
isActive(id: string): boolean {
|
||||
if (this.activeWorkflows !== null) {
|
||||
return this.activeWorkflows.isActive(id);
|
||||
}
|
||||
|
||||
return false;
|
||||
async isActive(id: string): Promise<boolean> {
|
||||
const workflow = await Db.collections.Workflow?.findOne({ id }) as IWorkflowDb;
|
||||
return workflow?.active as boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return error if there was a problem activating the workflow
|
||||
*
|
||||
|
@ -190,7 +200,6 @@ export class ActiveWorkflowRunner {
|
|||
return this.activationErrors[id];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds all the webhooks of the workflow
|
||||
*
|
||||
|
@ -202,12 +211,47 @@ export class ActiveWorkflowRunner {
|
|||
*/
|
||||
async addWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflowExecuteAdditionalDataWorkflow, mode: WorkflowExecuteMode): Promise<void> {
|
||||
const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData);
|
||||
let path = '';
|
||||
|
||||
for (const webhookData of webhooks) {
|
||||
await this.activeWebhooks!.add(workflow, webhookData, mode);
|
||||
// Save static data!
|
||||
await WorkflowHelpers.saveStaticData(workflow);
|
||||
|
||||
const node = workflow.getNode(webhookData.node) as INode;
|
||||
node.name = webhookData.node;
|
||||
|
||||
path = node.parameters.path as string;
|
||||
|
||||
if (node.parameters.path === undefined) {
|
||||
path = workflow.getSimpleParameterValue(node, webhookData.webhookDescription['path'], 'GET') as string;
|
||||
}
|
||||
|
||||
const webhook = {
|
||||
workflowId: webhookData.workflowId,
|
||||
webhookPath: NodeHelpers.getNodeWebhookPath(workflow.id as string, node, path),
|
||||
node: node.name,
|
||||
method: webhookData.httpMethod,
|
||||
} as IWebhookDb;
|
||||
|
||||
try {
|
||||
await Db.collections.Webhook?.insert(webhook);
|
||||
|
||||
const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, false);
|
||||
if (webhookExists === false) {
|
||||
// If webhook does not exist yet create it
|
||||
await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, false);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// The workflow was saved with two webhooks with the
|
||||
// same path/method so delete all webhooks saved
|
||||
|
||||
await Db.collections.Webhook?.delete({ workflowId: workflow.id });
|
||||
|
||||
// then show error to the user
|
||||
throw new Error(error.message || error.detail);
|
||||
}
|
||||
}
|
||||
// Save static data!
|
||||
await WorkflowHelpers.saveStaticData(workflow);
|
||||
}
|
||||
|
||||
|
||||
|
@ -227,10 +271,22 @@ export class ActiveWorkflowRunner {
|
|||
const nodeTypes = NodeTypes();
|
||||
const workflow = new Workflow({ id: workflowId, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings });
|
||||
|
||||
await this.activeWebhooks!.removeWorkflow(workflow);
|
||||
const mode = 'internal';
|
||||
|
||||
// Save the static workflow data if needed
|
||||
await WorkflowHelpers.saveStaticData(workflow);
|
||||
const credentials = await WorkflowCredentials(workflowData.nodes);
|
||||
const additionalData = await WorkflowExecuteAdditionalData.getBase(credentials);
|
||||
|
||||
const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData);
|
||||
|
||||
for (const webhookData of webhooks) {
|
||||
await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, false);
|
||||
}
|
||||
|
||||
const webhook = {
|
||||
workflowId: workflowData.id,
|
||||
} as IWebhookDb;
|
||||
|
||||
await Db.collections.Webhook?.delete(webhook);
|
||||
}
|
||||
|
||||
|
||||
|
@ -322,7 +378,6 @@ export class ActiveWorkflowRunner {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes a workflow active
|
||||
*
|
||||
|
@ -361,7 +416,11 @@ export class ActiveWorkflowRunner {
|
|||
|
||||
// Add the workflows which have webhooks defined
|
||||
await this.addWorkflowWebhooks(workflowInstance, additionalData, mode);
|
||||
await this.activeWorkflows.add(workflowId, workflowInstance, additionalData, getTriggerFunctions, getPollFunctions);
|
||||
|
||||
if (workflowInstance.getTriggerNodes().length !== 0
|
||||
|| workflowInstance.getPollNodes().length !== 0) {
|
||||
await this.activeWorkflows.add(workflowId, workflowInstance, additionalData, getTriggerFunctions, getPollFunctions);
|
||||
}
|
||||
|
||||
if (this.activationErrors[workflowId] !== undefined) {
|
||||
// If there were activation errors delete them
|
||||
|
@ -386,7 +445,6 @@ export class ActiveWorkflowRunner {
|
|||
await WorkflowHelpers.saveStaticData(workflowInstance!);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes a workflow inactive
|
||||
*
|
||||
|
@ -395,6 +453,7 @@ export class ActiveWorkflowRunner {
|
|||
* @memberof ActiveWorkflowRunner
|
||||
*/
|
||||
async remove(workflowId: string): Promise<void> {
|
||||
|
||||
if (this.activeWorkflows !== null) {
|
||||
// Remove all the webhooks of the workflow
|
||||
await this.removeWorkflowWebhooks(workflowId);
|
||||
|
@ -404,8 +463,13 @@ export class ActiveWorkflowRunner {
|
|||
delete this.activationErrors[workflowId];
|
||||
}
|
||||
|
||||
// Remove the workflow from the "list" of active workflows
|
||||
return this.activeWorkflows.remove(workflowId);
|
||||
// if it's active in memory then it's a trigger
|
||||
// so remove from list of actives workflows
|
||||
if (this.activeWorkflows.isActive(workflowId)) {
|
||||
this.activeWorkflows.remove(workflowId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`The "activeWorkflows" instance did not get initialized yet.`);
|
||||
|
|
|
@ -27,10 +27,12 @@ export let collections: IDatabaseCollections = {
|
|||
Credentials: null,
|
||||
Execution: null,
|
||||
Workflow: null,
|
||||
Webhook: null,
|
||||
};
|
||||
|
||||
import {
|
||||
InitialMigration1587669153312
|
||||
InitialMigration1587669153312,
|
||||
WebhookModel1589476000887,
|
||||
} from './databases/postgresdb/migrations';
|
||||
|
||||
import {
|
||||
|
@ -81,7 +83,7 @@ export async function init(): Promise<IDatabaseCollections> {
|
|||
port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number,
|
||||
username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string,
|
||||
schema: config.get('database.postgresdb.schema'),
|
||||
migrations: [InitialMigration1587669153312],
|
||||
migrations: [InitialMigration1587669153312, WebhookModel1589476000887],
|
||||
migrationsRun: true,
|
||||
migrationsTableName: `${entityPrefix}migrations`,
|
||||
};
|
||||
|
@ -135,6 +137,7 @@ export async function init(): Promise<IDatabaseCollections> {
|
|||
collections.Credentials = getRepository(entities.CredentialsEntity);
|
||||
collections.Execution = getRepository(entities.ExecutionEntity);
|
||||
collections.Workflow = getRepository(entities.WorkflowEntity);
|
||||
collections.Webhook = getRepository(entities.WebhookEntity);
|
||||
|
||||
return collections;
|
||||
}
|
||||
|
|
|
@ -40,8 +40,16 @@ export interface IDatabaseCollections {
|
|||
Credentials: Repository<ICredentialsDb> | null;
|
||||
Execution: Repository<IExecutionFlattedDb> | null;
|
||||
Workflow: Repository<IWorkflowDb> | null;
|
||||
Webhook: Repository<IWebhookDb> | null;
|
||||
}
|
||||
|
||||
export interface IWebhookDb {
|
||||
id?: number | ObjectID;
|
||||
workflowId: number | string | ObjectID;
|
||||
webhookPath: string;
|
||||
method: string;
|
||||
node: string;
|
||||
}
|
||||
|
||||
export interface IWorkflowBase extends IWorkflowBaseWorkflow {
|
||||
id?: number | string | ObjectID;
|
||||
|
|
|
@ -427,7 +427,9 @@ class App {
|
|||
const newWorkflowData = req.body;
|
||||
const id = req.params.id;
|
||||
|
||||
if (this.activeWorkflowRunner.isActive(id)) {
|
||||
const isActive = await this.activeWorkflowRunner.isActive(id);
|
||||
|
||||
if (isActive) {
|
||||
// When workflow gets saved always remove it as the triggers could have been
|
||||
// changed and so the changes would not take effect
|
||||
await this.activeWorkflowRunner.remove(id);
|
||||
|
@ -492,7 +494,9 @@ class App {
|
|||
this.app.delete('/rest/workflows/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<boolean> => {
|
||||
const id = req.params.id;
|
||||
|
||||
if (this.activeWorkflowRunner.isActive(id)) {
|
||||
const isActive = await this.activeWorkflowRunner.isActive(id);
|
||||
|
||||
if (isActive) {
|
||||
// Before deleting a workflow deactivate it
|
||||
await this.activeWorkflowRunner.remove(id);
|
||||
}
|
||||
|
@ -503,6 +507,7 @@ class App {
|
|||
}));
|
||||
|
||||
|
||||
|
||||
this.app.post('/rest/workflows/run', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<IExecutionPushResponse> => {
|
||||
const workflowData = req.body.workflowData;
|
||||
const runData: IRunData | undefined = req.body.runData;
|
||||
|
@ -632,7 +637,8 @@ class App {
|
|||
|
||||
// Returns the active workflow ids
|
||||
this.app.get('/rest/active', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<string[]> => {
|
||||
return this.activeWorkflowRunner.getActiveWorkflows();
|
||||
const activeWorkflows = await this.activeWorkflowRunner.getActiveWorkflows();
|
||||
return activeWorkflows.map(workflow => workflow.id.toString()) as string[];
|
||||
}));
|
||||
|
||||
|
||||
|
|
|
@ -69,6 +69,33 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo
|
|||
return returnData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the webhooks which should be created for the give workflow
|
||||
*
|
||||
* @export
|
||||
* @param {string} workflowId
|
||||
* @param {Workflow} workflow
|
||||
* @returns {IWebhookData[]}
|
||||
*/
|
||||
export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
||||
// Check all the nodes in the workflow if they have webhooks
|
||||
|
||||
const returnData: IWebhookData[] = [];
|
||||
|
||||
let parentNodes: string[] | undefined;
|
||||
|
||||
for (const node of Object.values(workflow.nodes)) {
|
||||
if (parentNodes !== undefined && !parentNodes.includes(node.name)) {
|
||||
// If parentNodes are given check only them if they have webhooks
|
||||
// and no other ones
|
||||
continue;
|
||||
}
|
||||
returnData.push.apply(returnData, NodeHelpers.getNodeWebhooksBasic(workflow, node));
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes a webhook
|
||||
|
|
31
packages/cli/src/databases/mongodb/WebhookEntity.ts
Normal file
31
packages/cli/src/databases/mongodb/WebhookEntity.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import {
|
||||
Column,
|
||||
Entity,
|
||||
Unique,
|
||||
ObjectIdColumn,
|
||||
ObjectID,
|
||||
} from 'typeorm';
|
||||
|
||||
import {
|
||||
IWebhookDb,
|
||||
} from '../../Interfaces';
|
||||
|
||||
@Entity()
|
||||
@Unique(['webhookPath', 'method'])
|
||||
export class WebhookEntity implements IWebhookDb {
|
||||
|
||||
@ObjectIdColumn()
|
||||
id: ObjectID;
|
||||
|
||||
@Column()
|
||||
workflowId: number;
|
||||
|
||||
@Column()
|
||||
webhookPath: string;
|
||||
|
||||
@Column()
|
||||
method: string;
|
||||
|
||||
@Column()
|
||||
node: string;
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
export * from './CredentialsEntity';
|
||||
export * from './ExecutionEntity';
|
||||
export * from './WorkflowEntity';
|
||||
export * from './WebhookEntity';
|
||||
|
||||
|
|
30
packages/cli/src/databases/mysqldb/WebhookEntity.ts
Normal file
30
packages/cli/src/databases/mysqldb/WebhookEntity.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
Column,
|
||||
Entity,
|
||||
Unique,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import {
|
||||
IWebhookDb,
|
||||
} from '../../Interfaces';
|
||||
|
||||
@Entity()
|
||||
@Unique(['webhookPath', 'method'])
|
||||
export class WebhookEntity implements IWebhookDb {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
workflowId: number;
|
||||
|
||||
@Column()
|
||||
webhookPath: string;
|
||||
|
||||
@Column()
|
||||
method: string;
|
||||
|
||||
@Column()
|
||||
node: string;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
export * from './CredentialsEntity';
|
||||
export * from './ExecutionEntity';
|
||||
export * from './WorkflowEntity';
|
||||
export * from './WebhookEntity';
|
||||
|
|
30
packages/cli/src/databases/postgresdb/WebhookEntity.ts
Normal file
30
packages/cli/src/databases/postgresdb/WebhookEntity.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
Column,
|
||||
Entity,
|
||||
Unique,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import {
|
||||
IWebhookDb,
|
||||
} from '../../';
|
||||
|
||||
@Entity()
|
||||
@Unique(['webhookPath', 'method'])
|
||||
export class WebhookEntity implements IWebhookDb {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
workflowId: number;
|
||||
|
||||
@Column()
|
||||
webhookPath: string;
|
||||
|
||||
@Column()
|
||||
method: string;
|
||||
|
||||
@Column()
|
||||
node: string;
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
export * from './CredentialsEntity';
|
||||
export * from './ExecutionEntity';
|
||||
export * from './WorkflowEntity';
|
||||
export * from './WebhookEntity';
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import {MigrationInterface, QueryRunner} from 'typeorm';
|
||||
|
||||
import { IWorkflowDb, NodeTypes, WebhookHelpers } from '../../..';
|
||||
import { Workflow } from 'n8n-workflow/dist/src/Workflow';
|
||||
import {
|
||||
IWebhookDb,
|
||||
} from '../../../Interfaces';
|
||||
|
||||
import * as config from '../../../../config';
|
||||
|
||||
export class WebhookModel1589476000887 implements MigrationInterface {
|
||||
name = 'WebhookModel1589476000887';
|
||||
|
||||
async up(queryRunner: QueryRunner): Promise<void> {
|
||||
let tablePrefix = config.get('database.tablePrefix');
|
||||
const schema = config.get('database.postgresdb.schema');
|
||||
if (schema) {
|
||||
tablePrefix = schema + '.' + tablePrefix;
|
||||
}
|
||||
|
||||
await queryRunner.query(`CREATE TABLE ${tablePrefix}webhook_entity ("id" SERIAL NOT NULL, "workflowId" integer NOT NULL, "webhookPath" character varying NOT NULL, "method" character varying NOT NULL, "node" character varying NOT NULL, CONSTRAINT "UQ_b21ace2e13596ccd87dc9bf4ea6" UNIQUE ("webhookPath", "method"), CONSTRAINT "PK_202217c8b912cf70b93b1e87256" PRIMARY KEY ("id"))`, undefined);
|
||||
|
||||
const workflows = await queryRunner.query(`SELECT * FROM ${tablePrefix}workflow_entity WHERE active=true`) as IWorkflowDb[];
|
||||
const data: IWebhookDb[] = [];
|
||||
const nodeTypes = NodeTypes();
|
||||
for (const workflow of workflows) {
|
||||
const workflowInstance = new Workflow({ id: workflow.id as string, name: workflow.name, nodes: workflow.nodes, connections: workflow.connections, active: workflow.active, nodeTypes, staticData: workflow.staticData, settings: workflow.settings });
|
||||
// I'm writing something more simple than this. I tried to use the built in method
|
||||
// getWorkflowWebhooks but it needs additionaldata and to get it I need the credentials
|
||||
// and for some reason when I use
|
||||
// const credentials = await WorkflowCredentials(node);
|
||||
// to get the credentials I got an error I think is cuz the database is yet not ready.
|
||||
const webhooks = WebhookHelpers.getWorkflowWebhooksBasic(workflowInstance);
|
||||
for (const webhook of webhooks) {
|
||||
data.push({
|
||||
workflowId: workflowInstance.id as string,
|
||||
webhookPath: webhook.path,
|
||||
method: webhook.httpMethod,
|
||||
node: webhook.node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (data.length !== 0) {
|
||||
await queryRunner.manager.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${tablePrefix}webhook_entity`)
|
||||
.values(data)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
async down(queryRunner: QueryRunner): Promise<void> {
|
||||
let tablePrefix = config.get('database.tablePrefix');
|
||||
const schema = config.get('database.postgresdb.schema');
|
||||
if (schema) {
|
||||
tablePrefix = schema + '.' + tablePrefix;
|
||||
}
|
||||
await queryRunner.query(`DROP TABLE ${tablePrefix}webhook_entity`, undefined);
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
export * from './1587669153312-InitialMigration';
|
||||
export * from './1589476000887-WebhookModel';
|
||||
|
||||
|
|
30
packages/cli/src/databases/sqlite/WebhookEntity.ts
Normal file
30
packages/cli/src/databases/sqlite/WebhookEntity.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
Column,
|
||||
Entity,
|
||||
Unique,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import {
|
||||
IWebhookDb,
|
||||
} from '../../Interfaces';
|
||||
|
||||
@Entity()
|
||||
@Unique(['webhookPath', 'method'])
|
||||
export class WebhookEntity implements IWebhookDb {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
workflowId: number;
|
||||
|
||||
@Column()
|
||||
webhookPath: string;
|
||||
|
||||
@Column()
|
||||
method: string;
|
||||
|
||||
@Column()
|
||||
node: string;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
export * from './CredentialsEntity';
|
||||
export * from './ExecutionEntity';
|
||||
export * from './WorkflowEntity';
|
||||
|
||||
export * from './WebhookEntity';
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"test:e2e": "vue-cli-service test:e2e",
|
||||
"test:unit": "vue-cli-service test:unit"
|
||||
},
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"uuid": "^8.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@beyonk/google-fonts-webpack-plugin": "^1.2.3",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.19",
|
||||
|
|
|
@ -126,6 +126,8 @@ import RunData from '@/components/RunData.vue';
|
|||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
|
@ -946,6 +948,10 @@ export default mixins(
|
|||
// Check if node-name is unique else find one that is
|
||||
newNodeData.name = this.getUniqueNodeName(newNodeData.name);
|
||||
|
||||
if (nodeTypeData.webhooks && nodeTypeData.webhooks.length) {
|
||||
newNodeData.webhookPath = uuidv4();
|
||||
}
|
||||
|
||||
await this.addNodes([newNodeData]);
|
||||
|
||||
// Automatically deselect all nodes and select the current one and also active
|
||||
|
|
|
@ -297,6 +297,7 @@ export interface INode {
|
|||
continueOnFail?: boolean;
|
||||
parameters: INodeParameters;
|
||||
credentials?: INodeCredentials;
|
||||
webhookPath?: string;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -791,6 +791,59 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData:
|
|||
return returnData;
|
||||
}
|
||||
|
||||
export function getNodeWebhooksBasic(workflow: Workflow, node: INode): IWebhookData[] {
|
||||
if (node.disabled === true) {
|
||||
// Node is disabled so webhooks will also not be enabled
|
||||
return [];
|
||||
}
|
||||
|
||||
const nodeType = workflow.nodeTypes.getByName(node.type) as INodeType;
|
||||
|
||||
if (nodeType.description.webhooks === undefined) {
|
||||
// Node does not have any webhooks so return
|
||||
return [];
|
||||
}
|
||||
|
||||
const workflowId = workflow.id || '__UNSAVED__';
|
||||
|
||||
const returnData: IWebhookData[] = [];
|
||||
for (const webhookDescription of nodeType.description.webhooks) {
|
||||
let nodeWebhookPath = workflow.getSimpleParameterValue(node, webhookDescription['path'], 'GET');
|
||||
if (nodeWebhookPath === undefined) {
|
||||
// TODO: Use a proper logger
|
||||
console.error(`No webhook path could be found for node "${node.name}" in workflow "${workflowId}".`);
|
||||
continue;
|
||||
}
|
||||
|
||||
nodeWebhookPath = nodeWebhookPath.toString();
|
||||
|
||||
if (nodeWebhookPath.charAt(0) === '/') {
|
||||
nodeWebhookPath = nodeWebhookPath.slice(1);
|
||||
}
|
||||
|
||||
const path = getNodeWebhookPath(workflowId, node, nodeWebhookPath);
|
||||
|
||||
const httpMethod = workflow.getSimpleParameterValue(node, webhookDescription['httpMethod'], 'GET');
|
||||
|
||||
if (httpMethod === undefined) {
|
||||
// TODO: Use a proper logger
|
||||
console.error(`The webhook "${path}" for node "${node.name}" in workflow "${workflowId}" could not be added because the httpMethod is not defined.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
returnData.push({
|
||||
httpMethod: httpMethod.toString() as WebhookHttpMethod,
|
||||
node: node.name,
|
||||
path,
|
||||
webhookDescription,
|
||||
workflowId,
|
||||
});
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the webhook path
|
||||
|
@ -802,7 +855,16 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData:
|
|||
* @returns {string}
|
||||
*/
|
||||
export function getNodeWebhookPath(workflowId: string, node: INode, path: string): string {
|
||||
return `${workflowId}/${encodeURIComponent(node.name.toLowerCase())}/${path}`;
|
||||
let webhookPath = '';
|
||||
if (node.webhookPath === undefined) {
|
||||
webhookPath = `${workflowId}/${encodeURIComponent(node.name.toLowerCase())}/${path}`;
|
||||
} else {
|
||||
if (node.type === 'n8n-nodes-base.webhook') {
|
||||
return path;
|
||||
}
|
||||
webhookPath = `${node.webhookPath}/${path}`;
|
||||
}
|
||||
return webhookPath;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue