2019-06-23 03:35:23 -07:00
|
|
|
import Vorpal = require('vorpal');
|
|
|
|
import { Args } from 'vorpal';
|
|
|
|
import { promises as fs } from 'fs';
|
|
|
|
import {
|
2019-08-08 11:38:25 -07:00
|
|
|
ActiveExecutions,
|
2019-06-23 03:35:23 -07:00
|
|
|
Db,
|
2019-08-08 11:38:25 -07:00
|
|
|
GenericHelpers,
|
2019-06-23 03:35:23 -07:00
|
|
|
IWorkflowBase,
|
2019-08-08 11:38:25 -07:00
|
|
|
IWorkflowExecutionDataProcess,
|
2019-06-23 03:35:23 -07:00
|
|
|
LoadNodesAndCredentials,
|
|
|
|
NodeTypes,
|
2019-08-08 11:38:25 -07:00
|
|
|
WorkflowCredentials,
|
2019-06-23 03:35:23 -07:00
|
|
|
WorkflowHelpers,
|
2019-08-08 11:38:25 -07:00
|
|
|
WorkflowRunner,
|
2019-06-23 03:35:23 -07:00
|
|
|
} from "../src";
|
|
|
|
import {
|
|
|
|
UserSettings,
|
|
|
|
} from "n8n-core";
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = (vorpal: Vorpal) => {
|
|
|
|
return vorpal
|
2019-06-23 23:28:24 -07:00
|
|
|
.command('execute')
|
2019-06-23 03:35:23 -07:00
|
|
|
// @ts-ignore
|
|
|
|
.description('Executes a given workflow')
|
|
|
|
.option('--file <workflow-file>',
|
|
|
|
'The path to a workflow file to execute')
|
|
|
|
.option('--id <workflow-id>',
|
|
|
|
'The id of the workflow to execute')
|
|
|
|
.option('\n')
|
|
|
|
// TODO: Add validation
|
|
|
|
// .validate((args: Args) => {
|
|
|
|
// })
|
|
|
|
.action(async (args: Args) => {
|
|
|
|
// Start directly with the init of the database to improve startup time
|
|
|
|
const startDbInitPromise = Db.init();
|
|
|
|
|
|
|
|
// Load all node and credential types
|
|
|
|
const loadNodesAndCredentials = LoadNodesAndCredentials();
|
|
|
|
const loadNodesAndCredentialsPromise = loadNodesAndCredentials.init();
|
|
|
|
|
|
|
|
if (!args.options.id && !args.options.file) {
|
|
|
|
GenericHelpers.logOutput(`Either option "--id" or "--file" have to be set!`);
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (args.options.id && args.options.file) {
|
|
|
|
GenericHelpers.logOutput(`Either "id" or "file" can be set never both!`);
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
let workflowId: string | undefined;
|
|
|
|
let workflowData: IWorkflowBase | undefined = undefined;
|
|
|
|
if (args.options.file) {
|
|
|
|
// Path to workflow is given
|
|
|
|
try {
|
|
|
|
workflowData = JSON.parse(await fs.readFile(args.options.file, 'utf8'));
|
|
|
|
} catch (error) {
|
|
|
|
if (error.code === 'ENOENT') {
|
|
|
|
GenericHelpers.logOutput(`The file "${args.options.file}" could not be found.`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do a basic check if the data in the file looks right
|
|
|
|
// TODO: Later check with the help of TypeScript data if it is valid or not
|
|
|
|
if (workflowData === undefined || workflowData.nodes === undefined || workflowData.connections === undefined) {
|
|
|
|
GenericHelpers.logOutput(`The file "${args.options.file}" does not contain valid workflow data.`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
workflowId = workflowData.id!.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait till the database is ready
|
|
|
|
await startDbInitPromise;
|
|
|
|
|
|
|
|
if (args.options.id) {
|
|
|
|
// Id of workflow is given
|
|
|
|
workflowId = args.options.id;
|
|
|
|
workflowData = await Db.collections!.Workflow!.findOne(workflowId);
|
|
|
|
if (workflowData === undefined) {
|
|
|
|
GenericHelpers.logOutput(`The workflow with the id "${workflowId}" does not exist.`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the settings exist
|
|
|
|
await UserSettings.prepareUserSettings();
|
|
|
|
|
|
|
|
// Wait till the n8n-packages have been read
|
|
|
|
await loadNodesAndCredentialsPromise;
|
|
|
|
|
|
|
|
// Add the found types to an instance other parts of the application can use
|
|
|
|
const nodeTypes = NodeTypes();
|
|
|
|
await nodeTypes.init(loadNodesAndCredentials.nodeTypes);
|
|
|
|
|
|
|
|
if (!WorkflowHelpers.isWorkflowIdValid(workflowId)) {
|
|
|
|
workflowId = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the workflow contains the required "Start" node
|
|
|
|
// "requiredNodeTypes" are also defined in editor-ui/views/NodeView.vue
|
|
|
|
const requiredNodeTypes = ['n8n-nodes-base.start'];
|
|
|
|
let startNodeFound = false;
|
2019-08-08 11:38:25 -07:00
|
|
|
for (const node of workflowData!.nodes) {
|
2019-06-23 03:35:23 -07:00
|
|
|
if (requiredNodeTypes.includes(node.type)) {
|
|
|
|
startNodeFound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (startNodeFound === false) {
|
|
|
|
// If the workflow does not contain a start-node we can not know what
|
|
|
|
// should be executed and with which data to start.
|
|
|
|
GenericHelpers.logOutput(`The workflow does not contain a "Start" node. So it can not be executed.`);
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2019-08-08 11:38:25 -07:00
|
|
|
const credentials = await WorkflowCredentials(workflowData!.nodes);
|
|
|
|
|
|
|
|
const runData: IWorkflowExecutionDataProcess = {
|
|
|
|
credentials,
|
|
|
|
executionMode: 'cli',
|
|
|
|
workflowData: workflowData!,
|
|
|
|
};
|
|
|
|
|
|
|
|
const workflowRunner = new WorkflowRunner();
|
|
|
|
const executionId = await workflowRunner.run(runData);
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
const activeExecutions = ActiveExecutions.getInstance();
|
2019-06-23 23:12:49 -07:00
|
|
|
const data = await activeExecutions.getPostExecutePromise(executionId);
|
2019-06-23 03:35:23 -07:00
|
|
|
|
|
|
|
console.log('Execution was successfull:');
|
|
|
|
console.log('====================================');
|
|
|
|
console.log(JSON.stringify(data, null, 2));
|
|
|
|
} catch (e) {
|
|
|
|
console.error('GOT ERROR');
|
|
|
|
console.log('====================================');
|
|
|
|
console.error(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|