import { promises as fs } from 'fs' ; import { Command , flags } from '@oclif/command' ; import { BinaryDataManager , IBinaryDataConfig , UserSettings , PLACEHOLDER_EMPTY_WORKFLOW_ID , } from 'n8n-core' ; import { INode , LoggerProxy } from 'n8n-workflow' ; import { ActiveExecutions , CredentialsOverwrites , CredentialTypes , Db , ExternalHooks , GenericHelpers , InternalHooksManager , IWorkflowBase , IWorkflowExecutionDataProcess , LoadNodesAndCredentials , NodeTypes , WorkflowHelpers , WorkflowRunner , } from '../src' ; import { getLogger } from '../src/Logger' ; import config = require ( '../config' ) ; export class Execute extends Command { static description = '\nExecutes a given workflow' ; static examples = [ `$ n8n execute --id=5` , `$ n8n execute --file=workflow.json` ] ; static flags = { help : flags . help ( { char : 'h' } ) , file : flags . string ( { description : 'path to a workflow file to execute' , } ) , id : flags . string ( { description : 'id of the workflow to execute' , } ) , rawOutput : flags . boolean ( { description : 'Outputs only JSON data, with no other text' , } ) , } ; async run ( ) { const logger = getLogger ( ) ; LoggerProxy . init ( logger ) ; const binaryDataConfig = config . get ( 'binaryDataManager' ) as IBinaryDataConfig ; await BinaryDataManager . init ( binaryDataConfig , true ) ; const { flags } = this . parse ( Execute ) ; const startDbInitPromise = Db . init ( ) ; const loadNodesAndCredentials = LoadNodesAndCredentials ( ) ; const loadNodesAndCredentialsPromise = loadNodesAndCredentials . init ( ) ; if ( ! flags . id && ! flags . file ) { console . info ( `Either option "--id" or "--file" have to be set!` ) ; return ; } if ( flags . id && flags . file ) { console . info ( `Either "id" or "file" can be set never both!` ) ; return ; } let workflowId ; let workflowData ; if ( flags . file ) { try { workflowData = JSON . parse ( await fs . readFile ( flags . file , 'utf8' ) ) ; } catch ( error ) { if ( error . code === 'ENOENT' ) { console . info ( `The file "${ flags . file }" could not be found.` ) ; return ; } throw error ; } if ( workflowData === undefined || workflowData . nodes === undefined || workflowData . connections === undefined ) { console . info ( `The file "${ flags . file }" does not contain valid workflow data.` ) ; return ; } workflowId = workflowData . id ? workflowData . id . toString ( ) : PLACEHOLDER_EMPTY_WORKFLOW_ID ; } await startDbInitPromise ; if ( flags . id ) { workflowId = flags . id ; workflowData = await Db . collections . Workflow ! . findOne ( workflowId ) ; if ( workflowData === undefined ) { console . info ( `The workflow with the id "${ workflowId }" does not exist.` ) ; process . exit ( 1 ) ; } } await UserSettings . prepareUserSettings ( ) ; await loadNodesAndCredentialsPromise ; const credentialsOverwrites = CredentialsOverwrites ( ) ; await credentialsOverwrites . init ( ) ; const externalHooks = ExternalHooks ( ) ; await externalHooks . init ( ) ; const nodeTypes = NodeTypes ( ) ; await nodeTypes . init ( loadNodesAndCredentials . nodeTypes ) ; const credentialTypes = CredentialTypes ( ) ; await credentialTypes . init ( loadNodesAndCredentials . credentialTypes ) ; const instanceId = await UserSettings . getInstanceId ( ) ; const { cli } = await GenericHelpers . getVersions ( ) ; InternalHooksManager . init ( instanceId , cli , nodeTypes ) ; if ( ! WorkflowHelpers . isWorkflowIdValid ( workflowId ) ) { workflowId = undefined ; } const requiredNodeTypes = [ 'n8n-nodes-base.start' ] ; let startNode ; for ( const node of workflowData ! . nodes ) { if ( requiredNodeTypes . includes ( node . type ) ) { startNode = node ; break ; } } if ( startNode === undefined ) { console . info ( `The workflow does not contain a "Start" node. So it can not be executed.` ) ; return Promise . resolve ( ) ; } try { const runData = { executionMode : 'cli' , startNodes : [ startNode . name ] , workflowData : workflowData ! , } ; const workflowRunner = new WorkflowRunner ( ) ; const executionId = await workflowRunner . run ( runData ) ; const activeExecutions = ActiveExecutions . getInstance ( ) ; const data = await activeExecutions . getPostExecutePromise ( executionId ) ; if ( data === undefined ) { throw new Error ( 'Workflow did not return any data!' ) ; } if ( data . data . resultData . error ) { console . info ( 'Execution was NOT successful. See log message for details.' ) ; logger . info ( 'Execution error:' ) ; logger . info ( '====================================' ) ; logger . info ( JSON . stringify ( data , null , 2 ) ) ; const { error } = data . data . resultData ; throw { ... error , stack : error . stack , } ; } if ( flags . rawOutput === undefined ) { this . log ( 'Execution was successful:' ) ; this . log ( '====================================' ) ; } this . log ( JSON . stringify ( data , null , 2 ) ) ; } catch ( e ) { console . error ( 'Error executing workflow. See log messages for details.' ) ; logger . error ( '\nExecution error:' ) ; logger . info ( '====================================' ) ; logger . error ( e . message ) ; logger . error ( e . stack ) ; this . exit ( 1 ) ; } this . exit ( ) ; } }