2019-06-23 03:35:23 -07:00
import {
2020-05-16 10:05:40 -07:00
CredentialTypes ,
2019-06-23 03:35:23 -07:00
Db ,
2020-05-16 10:05:40 -07:00
ICredentialsTypeData ,
2019-12-19 14:07:55 -08:00
ITransferNodeTypes ,
2019-06-23 03:35:23 -07:00
IWorkflowErrorData ,
2020-10-22 06:46:03 -07:00
IWorkflowExecutionDataProcess ,
2019-06-23 03:35:23 -07:00
NodeTypes ,
2019-08-08 11:38:25 -07:00
WorkflowCredentials ,
WorkflowRunner ,
2019-06-23 03:35:23 -07:00
} from './' ;
import {
2019-08-08 11:38:25 -07:00
IDataObject ,
2019-06-23 03:35:23 -07:00
IExecuteData ,
INode ,
2019-12-19 14:07:55 -08:00
IRun ,
2019-06-23 03:35:23 -07:00
IRunExecutionData ,
2019-12-19 14:07:55 -08:00
ITaskData ,
2020-05-16 10:05:40 -07:00
IWorkflowCredentials ,
2019-06-23 03:35:23 -07:00
Workflow ,
} from 'n8n-workflow' ;
2019-07-21 10:47:41 -07:00
import * as config from '../config' ;
2019-06-23 03:35:23 -07:00
const ERROR_TRIGGER_TYPE = config . get ( 'nodes.errorTriggerType' ) as string ;
2019-12-19 14:07:55 -08:00
/ * *
* Returns the data of the last executed node
*
* @export
* @param { IRun } inputData
* @returns { ( ITaskData | undefined ) }
* /
export function getDataLastExecutedNodeData ( inputData : IRun ) : ITaskData | undefined {
const runData = inputData . data . resultData . runData ;
const lastNodeExecuted = inputData . data . resultData . lastNodeExecuted ;
if ( lastNodeExecuted === undefined ) {
return undefined ;
}
if ( runData [ lastNodeExecuted ] === undefined ) {
return undefined ;
}
return runData [ lastNodeExecuted ] [ runData [ lastNodeExecuted ] . length - 1 ] ;
}
2019-06-23 03:35:23 -07:00
/ * *
* Returns if the given id is a valid workflow id
*
* @param { ( string | null | undefined ) } id The id to check
* @returns { boolean }
* @memberof App
* /
export function isWorkflowIdValid ( id : string | null | undefined | number ) : boolean {
if ( typeof id === 'string' ) {
id = parseInt ( id , 10 ) ;
}
if ( isNaN ( id as number ) ) {
return false ;
}
return true ;
}
/ * *
* Executes the error workflow
*
* @export
* @param { string } workflowId The id of the error workflow
* @param { IWorkflowErrorData } workflowErrorData The error data
* @returns { Promise < void > }
* /
export async function executeErrorWorkflow ( workflowId : string , workflowErrorData : IWorkflowErrorData ) : Promise < void > {
// Wrap everything in try/catch to make sure that no errors bubble up and all get caught here
try {
const workflowData = await Db . collections . Workflow ! . findOne ( { id : workflowId } ) ;
if ( workflowData === undefined ) {
// The error workflow could not be found
console . error ( ` ERROR: Calling Error Workflow for " ${ workflowErrorData . workflow . id } ". Could not find error workflow " ${ workflowId } " ` ) ;
return ;
}
const executionMode = 'error' ;
const nodeTypes = NodeTypes ( ) ;
2020-02-15 17:07:01 -08:00
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 } ) ;
2019-06-23 03:35:23 -07:00
let node : INode ;
let workflowStartNode : INode | undefined ;
for ( const nodeName of Object . keys ( workflowInstance . nodes ) ) {
node = workflowInstance . nodes [ nodeName ] ;
if ( node . type === ERROR_TRIGGER_TYPE ) {
workflowStartNode = node ;
}
}
if ( workflowStartNode === undefined ) {
console . error ( ` ERROR: Calling Error Workflow for " ${ workflowErrorData . workflow . id } ". Could not find " ${ ERROR_TRIGGER_TYPE } " in workflow " ${ workflowId } " ` ) ;
return ;
}
// Can execute without webhook so go on
// Initialize the data of the webhook node
const nodeExecutionStack : IExecuteData [ ] = [ ] ;
nodeExecutionStack . push (
{
node : workflowStartNode ,
data : {
main : [
[
{
2020-10-22 06:46:03 -07:00
json : workflowErrorData ,
} ,
] ,
2019-06-23 03:35:23 -07:00
] ,
} ,
2020-10-22 06:46:03 -07:00
}
2019-06-23 03:35:23 -07:00
) ;
const runExecutionData : IRunExecutionData = {
startData : {
} ,
resultData : {
runData : { } ,
} ,
executionData : {
contextData : { } ,
nodeExecutionStack ,
waitingExecution : { } ,
} ,
} ;
2019-08-08 11:38:25 -07:00
const credentials = await WorkflowCredentials ( workflowData . nodes ) ;
2019-06-23 03:35:23 -07:00
2019-08-08 11:38:25 -07:00
const runData : IWorkflowExecutionDataProcess = {
credentials ,
executionMode ,
executionData : runExecutionData ,
workflowData ,
} ;
const workflowRunner = new WorkflowRunner ( ) ;
await workflowRunner . run ( runData ) ;
2019-06-23 03:35:23 -07:00
} catch ( error ) {
console . error ( ` ERROR: Calling Error Workflow for " ${ workflowErrorData . workflow . id } ": ${ error . message } ` ) ;
}
}
2019-12-19 14:07:55 -08:00
/ * *
* Returns all the defined NodeTypes
*
* @export
* @returns { ITransferNodeTypes }
* /
export function getAllNodeTypeData ( ) : ITransferNodeTypes {
const nodeTypes = NodeTypes ( ) ;
// Get the data of all thenode types that they
// can be loaded again in the process
const returnData : ITransferNodeTypes = { } ;
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 ;
}
/ * *
* Returns the data of the node types that are needed
* to execute the given nodes
*
* @export
* @param { INode [ ] } nodes
* @returns { ITransferNodeTypes }
* /
export function getNodeTypeData ( nodes : INode [ ] ) : ITransferNodeTypes {
const nodeTypes = NodeTypes ( ) ;
// Check which node-types have to be loaded
const neededNodeTypes = getNeededNodeTypes ( nodes ) ;
// Get all the data of the needed node types that they
// can be loaded again in the process
const returnData : ITransferNodeTypes = { } ;
for ( const nodeTypeName of neededNodeTypes ) {
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 ;
}
2020-05-16 10:05:40 -07:00
/ * *
* Returns the credentials data of the given type and its parent types
* it extends
*
* @export
* @param { string } type The credential type to return data off
* @returns { ICredentialsTypeData }
* /
export function getCredentialsDataWithParents ( type : string ) : ICredentialsTypeData {
const credentialTypes = CredentialTypes ( ) ;
const credentialType = credentialTypes . getByName ( type ) ;
const credentialTypeData : ICredentialsTypeData = { } ;
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 ;
}
/ * *
* Returns all the credentialTypes which are needed to resolve
* the given workflow credentials
*
* @export
* @param { IWorkflowCredentials } credentials The credentials which have to be able to be resolved
* @returns { ICredentialsTypeData }
* /
export function getCredentialsData ( credentials : IWorkflowCredentials ) : ICredentialsTypeData {
const credentialTypeData : ICredentialsTypeData = { } ;
for ( const credentialType of Object . keys ( credentials ) ) {
if ( credentialTypeData [ credentialType ] !== undefined ) {
continue ;
}
Object . assign ( credentialTypeData , getCredentialsDataWithParents ( credentialType ) ) ;
}
return credentialTypeData ;
}
2019-12-19 14:07:55 -08:00
/ * *
* Returns the names of the NodeTypes which are are needed
* to execute the gives nodes
*
* @export
* @param { INode [ ] } nodes
* @returns { string [ ] }
* /
export function getNeededNodeTypes ( nodes : INode [ ] ) : string [ ] {
// Check which node-types have to be loaded
const neededNodeTypes : string [ ] = [ ] ;
for ( const node of nodes ) {
if ( ! neededNodeTypes . includes ( node . type ) ) {
neededNodeTypes . push ( node . type ) ;
}
}
return neededNodeTypes ;
}
2019-06-23 03:35:23 -07:00
/ * *
* Saves the static data if it changed
*
* @export
* @param { Workflow } workflow
* @returns { Promise < void > }
* /
export async function saveStaticData ( workflow : Workflow ) : Promise < void > {
if ( workflow . staticData . __dataChanged === true ) {
// Static data of workflow changed and so has to be saved
if ( isWorkflowIdValid ( workflow . id ) === true ) {
// Workflow is saved so update in database
try {
2019-08-08 11:38:25 -07:00
await saveStaticDataById ( workflow . id ! , workflow . staticData ) ;
2019-06-23 03:35:23 -07:00
workflow . staticData . __dataChanged = false ;
} catch ( e ) {
// TODO: Add proper logging!
console . error ( ` There was a problem saving the workflow with id " ${ workflow . id } " to save changed staticData: ${ e . message } ` ) ;
}
}
}
}
2019-08-08 11:38:25 -07:00
/ * *
* Saves the given static data on workflow
*
* @export
* @param { ( string | number ) } workflowId The id of the workflow to save data on
* @param { IDataObject } newStaticData The static data to save
* @returns { Promise < void > }
* /
export async function saveStaticDataById ( workflowId : string | number , newStaticData : IDataObject ) : Promise < void > {
await Db . collections . Workflow !
. update ( workflowId , {
staticData : newStaticData ,
} ) ;
}
2019-10-14 22:36:53 -07:00
/ * *
* Returns the static data of workflow
*
* @export
* @param { ( string | number ) } workflowId The id of the workflow to get static data of
* @returns
* /
export async function getStaticDataById ( workflowId : string | number ) {
const workflowData = await Db . collections . Workflow !
. findOne ( workflowId , { select : [ 'staticData' ] } ) ;
if ( workflowData === undefined ) {
return { } ;
}
return workflowData . staticData || { } ;
}