⚡ Introduce FE external hooks (#1332)
* ⚡ Introduce FE external hooks * ⚡ update hooks * ⚡ add data from frontend settings to hooks * re-organize and update * cleanup * 👌 * ⚡ cleanup workflowSave mixin, add events * avoid alert on new workflow save as * ⚡ update workflow active events * rename externalhooks method * ⚡ Rename frontend hooks Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
39
packages/editor-ui/src/components/mixins/externalHooks.ts
Normal file
39
packages/editor-ui/src/components/mixins/externalHooks.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { IExternalHooks } from '@/Interface';
|
||||
import { IDataObject } from 'n8n-workflow';
|
||||
import Vue from 'vue';
|
||||
import { Store } from 'vuex';
|
||||
|
||||
export async function runExternalHook(
|
||||
eventName: string,
|
||||
store: Store<IDataObject>,
|
||||
metadata?: IDataObject,
|
||||
) {
|
||||
// @ts-ignore
|
||||
if (!window.n8nExternalHooks) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [resource, operator] = eventName.split('.');
|
||||
|
||||
// @ts-ignore
|
||||
if (window.n8nExternalHooks[resource] && window.n8nExternalHooks[resource][operator]) {
|
||||
// @ts-ignore
|
||||
const hookMethods = window.n8nExternalHooks[resource][operator];
|
||||
|
||||
for (const hookmethod of hookMethods) {
|
||||
await hookmethod(store, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const externalHooks = Vue.extend({
|
||||
methods: {
|
||||
$externalHooks(): IExternalHooks {
|
||||
return {
|
||||
run: async (eventName: string, metadata?: IDataObject): Promise<void> => {
|
||||
await runExternalHook.call(this, eventName, this.$store, metadata);
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
XYPositon,
|
||||
} from '../../Interface';
|
||||
|
||||
import { externalHooks } from '@/components/mixins/externalHooks';
|
||||
import { restApi } from '@/components/mixins/restApi';
|
||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||
import { showMessage } from '@/components/mixins/showMessage';
|
||||
@@ -36,6 +37,7 @@ import { isEqual } from 'lodash';
|
||||
import mixins from 'vue-typed-mixins';
|
||||
|
||||
export const workflowHelpers = mixins(
|
||||
externalHooks,
|
||||
nodeHelpers,
|
||||
restApi,
|
||||
showMessage,
|
||||
@@ -422,6 +424,7 @@ export const workflowHelpers = mixins(
|
||||
this.$store.commit('setWorkflowId', workflowData.id);
|
||||
this.$store.commit('setWorkflowName', {newName: workflowData.name, setStateDirty: false});
|
||||
this.$store.commit('setWorkflowSettings', workflowData.settings || {});
|
||||
this.$store.commit('setStateDirty', false);
|
||||
} else {
|
||||
// Workflow exists already so update it
|
||||
await this.restApi().updateWorkflow(currentWorkflow, workflowData);
|
||||
@@ -441,6 +444,7 @@ export const workflowHelpers = mixins(
|
||||
message: `The workflow "${workflowData.name}" got saved!`,
|
||||
type: 'success',
|
||||
});
|
||||
this.$externalHooks().run('workflow.afterUpdate', { workflowData });
|
||||
} catch (e) {
|
||||
this.$store.commit('removeActiveAction', 'workflowSaving');
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
NodeHelpers,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { externalHooks } from '@/components/mixins/externalHooks';
|
||||
import { restApi } from '@/components/mixins/restApi';
|
||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||
|
||||
@@ -17,6 +18,7 @@ import mixins from 'vue-typed-mixins';
|
||||
import { titleChange } from './titleChange';
|
||||
|
||||
export const workflowRun = mixins(
|
||||
externalHooks,
|
||||
restApi,
|
||||
workflowHelpers,
|
||||
titleChange,
|
||||
@@ -82,6 +84,7 @@ export const workflowRun = mixins(
|
||||
duration: 0,
|
||||
});
|
||||
this.$titleSet(workflow.name as string, 'ERROR');
|
||||
this.$externalHooks().run('workflow.runError', { errorMessages });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import {
|
||||
IWorkflowData,
|
||||
} from '../../Interface';
|
||||
|
||||
import { restApi } from '@/components/mixins/restApi';
|
||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
||||
import { showMessage } from '@/components/mixins/showMessage';
|
||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
|
||||
export const workflowSave = mixins(
|
||||
nodeHelpers,
|
||||
restApi,
|
||||
showMessage,
|
||||
workflowHelpers,
|
||||
)
|
||||
.extend({
|
||||
methods: {
|
||||
// Saves the currently loaded workflow to the database.
|
||||
async saveCurrentWorkflow (withNewName = false) {
|
||||
const currentWorkflow = this.$route.params.name;
|
||||
let workflowName: string | null | undefined = '';
|
||||
if (currentWorkflow === undefined || withNewName === true) {
|
||||
// Currently no workflow name is set to get it from user
|
||||
workflowName = await this.$prompt(
|
||||
'Enter workflow name',
|
||||
'Name',
|
||||
{
|
||||
confirmButtonText: 'Save',
|
||||
cancelButtonText: 'Cancel',
|
||||
},
|
||||
)
|
||||
.then((data) => {
|
||||
// @ts-ignore
|
||||
return data.value;
|
||||
})
|
||||
.catch(() => {
|
||||
// User did cancel
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (workflowName === undefined) {
|
||||
// User did cancel
|
||||
return;
|
||||
} else if (['', null].includes(workflowName)) {
|
||||
// User did not enter a name
|
||||
this.$showMessage({
|
||||
title: 'Name missing',
|
||||
message: `No name for the workflow got entered and could so not be saved!`,
|
||||
type: 'error',
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.$store.commit('addActiveAction', 'workflowSaving');
|
||||
|
||||
let workflowData: IWorkflowData = await this.getWorkflowDataToSave();
|
||||
|
||||
if (currentWorkflow === undefined || withNewName === true) {
|
||||
// Workflow is new or is supposed to get saved under a new name
|
||||
// so create a new entry in database
|
||||
workflowData.name = workflowName!.trim() as string;
|
||||
|
||||
if (withNewName === true) {
|
||||
// If an existing workflow gets resaved with a new name
|
||||
// make sure that the new ones is not active
|
||||
workflowData.active = false;
|
||||
}
|
||||
|
||||
workflowData = await this.restApi().createNewWorkflow(workflowData);
|
||||
|
||||
this.$store.commit('setActive', workflowData.active || false);
|
||||
this.$store.commit('setWorkflowId', workflowData.id);
|
||||
this.$store.commit('setWorkflowName', {newName: workflowData.name, setStateDirty: false});
|
||||
this.$store.commit('setWorkflowSettings', workflowData.settings || {});
|
||||
} else {
|
||||
// Workflow exists already so update it
|
||||
await this.restApi().updateWorkflow(currentWorkflow, workflowData);
|
||||
}
|
||||
// Set dirty = false before pushing route so unsaved changes message doesnt trigger.
|
||||
this.$store.commit('setStateDirty', false);
|
||||
|
||||
if (this.$route.params.name !== workflowData.id) {
|
||||
this.$router.push({
|
||||
name: 'NodeViewExisting',
|
||||
params: { name: workflowData.id as string, action: 'workflowSave' },
|
||||
});
|
||||
}
|
||||
|
||||
this.$store.commit('removeActiveAction', 'workflowSaving');
|
||||
this.$showMessage({
|
||||
title: 'Workflow saved',
|
||||
message: `The workflow "${workflowData.name}" got saved!`,
|
||||
type: 'success',
|
||||
});
|
||||
} catch (e) {
|
||||
this.$store.commit('removeActiveAction', 'workflowSaving');
|
||||
|
||||
this.$showMessage({
|
||||
title: 'Problem saving workflow',
|
||||
message: `There was a problem saving the workflow: "${e.message}"`,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user