Add additional external hooks and provide additional data

This commit is contained in:
Jan Oberhauser 2020-05-05 01:23:54 +02:00
parent a380a9a394
commit 0387671cae
5 changed files with 79 additions and 37 deletions

View file

@ -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'

View file

@ -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<void> {
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<void> {
async run(hookName: string, hookParameters?: any[]): Promise<void> { // 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);
}
}

View file

@ -188,9 +188,13 @@ export interface IExecutingWorkflowData {
workflowExecution?: PCancelable<IRun>;
}
export interface IExternalHookFunctions {
DbCollections: IDatabaseCollections;
}
export interface IExternalHooks {
init(): Promise<void>;
run(hookName: string): Promise<void>;
run(hookName: string, hookParameters?: any[]): Promise<void>; // tslint:disable-line:no-any
}
export interface IN8nConfig {

View file

@ -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<IWorkflowResponse> => {
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<IWorkflowResponse> => {
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<boolean> => {
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<boolean> => {
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!');
}

View file

@ -1,10 +1,46 @@
import {
IExternalHookFunctions,
IWorkflowBase,
} from '../';
// TODO: Move that to interfaces
interface IExternalHooks {
credentials?: {
create?: Array<{ (this: IExternalHookFunctions): Promise<void>; }>
delete?: Array<{ (this: IExternalHookFunctions, credentialId: string): Promise<void>; }>
update?: Array<{ (this: IExternalHookFunctions, credentialId: string): Promise<void>; }>
};
workflow?: {
create?: Array<{ (this: IExternalHookFunctions, workflowData: IWorkflowBase): Promise<void>; }>
delete?: Array<{ (this: IExternalHookFunctions, workflowId: string): Promise<void>; }>
update?: Array<{ (this: IExternalHookFunctions, workflowData: IWorkflowBase): Promise<void>; }>
};
}
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;