import { IDataObject , IExecuteData , INode , INodeCredentialsDetails , IRun , IRunExecutionData , ITaskData , IWorkflowCredentials , LoggerProxy as Logger , Workflow , } from 'n8n-workflow' ; import { validate } from 'class-validator' ; import { CredentialTypes , Db , ICredentialsTypeData , ITransferNodeTypes , IWorkflowErrorData , IWorkflowExecutionDataProcess , NodeTypes , ResponseHelper , WorkflowCredentials , WorkflowRunner , } from '.' ; import * as config from '../config' ; import { WorkflowEntity } from './databases/entities/WorkflowEntity' ; const ERROR_TRIGGER_TYPE = config . get ( 'nodes.errorTriggerType' ) as string ; export param { IRun } inputData returns { ( ITaskData | undefined ) } export function getDataLastExecutedNodeData ( inputData ) { const { runData } = inputData . data . resultData ; const { lastNodeExecuted } = inputData . data . resultData ; if ( lastNodeExecuted === undefined ) { return undefined ; } if ( runData [ lastNodeExecuted ] === undefined ) { return undefined ; } return runData [ lastNodeExecuted ] [ runData [ lastNodeExecuted ] . length - 1 ] ; } param { ( string | null | undefined ) } id returns { boolean } memberof export function isWorkflowIdValid ( id ) { if ( typeof id === 'string' ) { id = parseInt ( id , 10 ) ; } if ( isNaN ( id as number ) ) { return false ; } return true ; } export param { string } workflowId param { IWorkflowErrorData } workflowErrorData returns { Promise < void > } export async function executeErrorWorkflow ( workflowId , workflowErrorData ) { try { const workflowData = await Db . collections . Workflow ! . findOne ( { id : Number ( workflowId ) } ) ; if ( workflowData === undefined ) { Logger . error ( `Calling Error Workflow for "${ workflowErrorData . workflow . id }". Could not find error workflow "${ workflowId }"` , { workflowId } ) ; return ; } const executionMode = 'error' ; const nodeTypes = NodeTypes ( ) ; const workflowInstance = new Workflow ( { id : workflowId , name : workflowData . name , nodeTypes , nodes : workflowData . nodes , connections : workflowData . connections , active : workflowData . active , staticData : workflowData . staticData , settings : workflowData . settings , } ) ; let node ; let workflowStartNode ; for ( const nodeName of Object . keys ( workflowInstance . nodes ) ) { node = workflowInstance . nodes [ nodeName ] ; if ( node . type === ERROR_TRIGGER_TYPE ) { workflowStartNode = node ; } } if ( workflowStartNode === undefined ) { Logger . error ( `Calling Error Workflow for "${ workflowErrorData . workflow . id }". Could not find "${ ERROR_TRIGGER_TYPE }" in workflow "${ workflowId }"` ) ; return ; } const nodeExecutionStack = [ ] ; nodeExecutionStack . push ( { node : workflowStartNode , data : { main : [ [ { json : workflowErrorData , } , ] , ] , } , } ) ; const runExecutionData = { startData : { } , resultData : { runData : { } , } , executionData : { contextData : { } , nodeExecutionStack , waitingExecution : { } , } , } ; const runData = { executionMode , executionData : runExecutionData , workflowData , } ; const workflowRunner = new WorkflowRunner ( ) ; await workflowRunner . run ( runData ) ; } catch ( error ) { Logger . error ( `Calling Error Workflow for "${ workflowErrorData . workflow . id }": "${ error . message }"` , { workflowId : workflowErrorData . workflow . id } ) ; } } export returns { ITransferNodeTypes } export function getAllNodeTypeData ( ) { const nodeTypes = NodeTypes ( ) ; const returnData = { } ; for ( const nodeTypeName of Object . keys ( nodeTypes . nodeTypes ) ) { if ( nodeTypes . nodeTypes [ nodeTypeName ] === undefined ) { throw new Error ( `The NodeType "${ nodeTypeName }" could not be found!` ) ; } returnData [ nodeTypeName ] = { className : nodeTypes . nodeTypes [ nodeTypeName ] . type . constructor . name , sourcePath : nodeTypes . nodeTypes [ nodeTypeName ] . sourcePath , } ; } return returnData ; } export param { INode [ ] } nodes returns { ITransferNodeTypes } export function getNodeTypeData ( nodes ) { const nodeTypes = NodeTypes ( ) ; const neededNodeTypes = getNeededNodeTypes ( nodes ) ; const returnData = { } ; for ( const nodeTypeName of neededNodeTypes ) { if ( nodeTypes . nodeTypes [ nodeTypeName . type ] === undefined ) { throw new Error ( `The NodeType "${ nodeTypeName . type }" could not be found!` ) ; } returnData [ nodeTypeName . type ] = { className : nodeTypes . nodeTypes [ nodeTypeName . type ] . type . constructor . name , sourcePath : nodeTypes . nodeTypes [ nodeTypeName . type ] . sourcePath , } ; } return returnData ; } export param { string } type returns { ICredentialsTypeData } export function getCredentialsDataWithParents ( type ) { const credentialTypes = CredentialTypes ( ) ; const credentialType = credentialTypes . getByName ( type ) ; const credentialTypeData = { } ; credentialTypeData [ type ] = credentialType ; if ( credentialType === undefined || credentialType . extends === undefined ) { return credentialTypeData ; } for ( const typeName of credentialType . extends ) { if ( credentialTypeData [ typeName ] !== undefined ) { continue ; } credentialTypeData [ typeName ] = credentialTypes . getByName ( typeName ) ; Object . assign ( credentialTypeData , getCredentialsDataWithParents ( typeName ) ) ; } return credentialTypeData ; } export param { IWorkflowCredentials } credentials returns { ICredentialsTypeData } export function getCredentialsDataByNodes ( nodes ) { const credentialTypeData = { } ; for ( const node of nodes ) { const credentialsUsedByThisNode = node . credentials ; if ( credentialsUsedByThisNode ) { for ( const credentialType of Object . keys ( credentialsUsedByThisNode ) ) { if ( credentialTypeData [ credentialType ] !== undefined ) { continue ; } Object . assign ( credentialTypeData , getCredentialsDataWithParents ( credentialType ) ) ; } } } return credentialTypeData ; } export param { INode [ ] } nodes returns { string [ ] } export function getNeededNodeTypes ( nodes ) { const neededNodeTypes = [ ] ; for ( const node of nodes ) { if ( neededNodeTypes . find ( ( neededNodes ) => node . type === neededNodes . type ) === undefined ) { neededNodeTypes . push ( { type : node . type , version : node . typeVersion } ) ; } } return neededNodeTypes ; } export param { Workflow } workflow returns { Promise < void > } export async function saveStaticData ( workflow ) { if ( workflow . staticData . __dataChanged === true ) { if ( isWorkflowIdValid ( workflow . id ) ) { try { await saveStaticDataById ( workflow . id ! , workflow . staticData ) ; workflow . staticData . __dataChanged = false ; } catch ( e ) { Logger . error ( `There was a problem saving the workflow with id "${ workflow . id }" to save changed staticData: "${ e . message }"` , { workflowId : workflow . id } ) ; } } } } export param { ( string | number ) } workflowId param { IDataObject } newStaticData returns { Promise < void > } export async function saveStaticDataById ( workflowId , newStaticData ) { await Db . collections . Workflow ! . update ( workflowId , { staticData : newStaticData , } ) ; } export param { ( string | number ) } workflowId returns export async function getStaticDataById ( workflowId ) { const workflowData = await Db . collections . Workflow ! . findOne ( workflowId , { select : [ 'staticData' ] , } ) ; if ( workflowData === undefined ) { return { } ; } return workflowData . staticData || { } ; } export async function replaceInvalidCredentials ( workflow ) { const { nodes } = workflow ; if ( ! nodes ) return workflow ; const credentialsByName = { } ; const credentialsById = { } ; for ( const node of nodes ) { if ( ! node . credentials || node . disabled ) { continue ; } const allNodeCredentials = Object . entries ( node . credentials ) ; for ( const [ nodeCredentialType , nodeCredentials ] of allNodeCredentials ) { if ( typeof nodeCredentials === 'string' || nodeCredentials . id === null ) { const name = typeof nodeCredentials === 'string' ? nodeCredentials : nodeCredentials . name ; if ( ! credentialsByName [ nodeCredentialType ] ) { credentialsByName [ nodeCredentialType ] = { } ; } if ( credentialsByName [ nodeCredentialType ] [ name ] === undefined ) { const credentials = await Db . collections . Credentials ?. find ( { name , type : nodeCredentialType , } ) ; if ( credentials ?. length === 1 ) { credentialsByName [ nodeCredentialType ] [ name ] = { id : credentials [ 0 ] . id . toString ( ) , name : credentials [ 0 ] . name , } ; node . credentials [ nodeCredentialType ] = credentialsByName [ nodeCredentialType ] [ name ] ; continue ; } credentialsByName [ nodeCredentialType ] [ name ] = { id : null , name , } ; } else { node . credentials [ nodeCredentialType ] = credentialsByName [ nodeCredentialType ] [ name ] ; } continue ; } if ( ! credentialsById [ nodeCredentialType ] ) { credentialsById [ nodeCredentialType ] = { } ; } if ( credentialsById [ nodeCredentialType ] [ nodeCredentials . id ] === undefined ) { const credentials = await Db . collections . Credentials ?. findOne ( { id : nodeCredentials . id , type : nodeCredentialType , } ) ; if ( credentials ) { credentialsById [ nodeCredentialType ] [ nodeCredentials . id ] = { id : credentials . id . toString ( ) , name : credentials . name , } ; node . credentials [ nodeCredentialType ] = credentialsById [ nodeCredentialType ] [ nodeCredentials . id ] ; continue ; } const credsByName = await Db . collections . Credentials ?. find ( { name : nodeCredentials . name , type : nodeCredentialType , } ) ; if ( credsByName ?. length === 1 ) { credentialsById [ nodeCredentialType ] [ credsByName [ 0 ] . id ] = { id : credsByName [ 0 ] . id . toString ( ) , name : credsByName [ 0 ] . name , } ; node . credentials [ nodeCredentialType ] = credentialsById [ nodeCredentialType ] [ credsByName [ 0 ] . id ] ; continue ; } credentialsById [ nodeCredentialType ] [ nodeCredentials . id ] = nodeCredentials ; continue ; } node . credentials [ nodeCredentialType ] = credentialsById [ nodeCredentialType ] [ nodeCredentials . id ] ; } } return workflow ; } export async function validateWorkflow ( newWorkflow ) { const errors = await validate ( newWorkflow ) ; if ( errors . length ) { const validationErrorMessage = Object . values ( errors [ 0 ] . constraints ! ) [ 0 ] ; throw new ResponseHelper . ResponseError ( validationErrorMessage , undefined , 400 ) ; } } export function throwDuplicateEntryError ( error ) { const errorMessage = error . message . toLowerCase ( ) ; if ( errorMessage . includes ( 'unique' ) || errorMessage . includes ( 'duplicate' ) ) { throw new ResponseHelper . ResponseError ( 'There is already a workflow with this name' , undefined , 400 ) ; } throw new ResponseHelper . ResponseError ( errorMessage , undefined , 400 ) ; } export type NameRequest = Express . Request & { query : { name ? : string ; } ; } ;