mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 16:44:07 -08:00
merge in Jan's branch to master, handle conflicts
This commit is contained in:
parent
973e48ffea
commit
c49c4bb55a
|
@ -17,6 +17,7 @@ import { Document } from '@langchain/core/documents';
|
||||||
|
|
||||||
import type { SetField, SetNodeOptions } from 'n8n-nodes-base/dist/nodes/Set/v2/helpers/interfaces';
|
import type { SetField, SetNodeOptions } from 'n8n-nodes-base/dist/nodes/Set/v2/helpers/interfaces';
|
||||||
import * as manual from 'n8n-nodes-base/dist/nodes/Set/v2/manual.mode';
|
import * as manual from 'n8n-nodes-base/dist/nodes/Set/v2/manual.mode';
|
||||||
|
import get from 'lodash/get';
|
||||||
import type { CallbackManagerForRetrieverRun } from '@langchain/core/callbacks/manager';
|
import type { CallbackManagerForRetrieverRun } from '@langchain/core/callbacks/manager';
|
||||||
import { logWrapper } from '../../../utils/logWrapper';
|
import { logWrapper } from '../../../utils/logWrapper';
|
||||||
|
|
||||||
|
@ -293,6 +294,8 @@ export class RetrieverWorkflow implements INodeType {
|
||||||
};
|
};
|
||||||
|
|
||||||
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
|
const workflowProxy = this.getWorkflowDataProxy(0);
|
||||||
|
|
||||||
class WorkflowRetriever extends BaseRetriever {
|
class WorkflowRetriever extends BaseRetriever {
|
||||||
lc_namespace = ['n8n-nodes-langchain', 'retrievers', 'workflow'];
|
lc_namespace = ['n8n-nodes-langchain', 'retrievers', 'workflow'];
|
||||||
|
|
||||||
|
@ -384,21 +387,30 @@ export class RetrieverWorkflow implements INodeType {
|
||||||
|
|
||||||
const items = [newItem] as INodeExecutionData[];
|
const items = [newItem] as INodeExecutionData[];
|
||||||
|
|
||||||
let receivedItems: INodeExecutionData[][];
|
let receivedData;
|
||||||
try {
|
try {
|
||||||
receivedItems = (await this.executeFunctions.executeWorkflow(
|
receivedData = await this.executeFunctions.executeWorkflow(
|
||||||
workflowInfo,
|
workflowInfo,
|
||||||
items,
|
items,
|
||||||
config?.getChild(),
|
config?.getChild(),
|
||||||
)) as INodeExecutionData[][];
|
{
|
||||||
|
startMetadata: {
|
||||||
|
executionId: workflowProxy.$execution.id,
|
||||||
|
workflowId: workflowProxy.$workflow.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Make sure a valid error gets returned that can by json-serialized else it will
|
// Make sure a valid error gets returned that can by json-serialized else it will
|
||||||
// not show up in the frontend
|
// not show up in the frontend
|
||||||
throw new NodeOperationError(this.executeFunctions.getNode(), error as Error);
|
throw new NodeOperationError(this.executeFunctions.getNode(), error as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line lodash/path-style
|
||||||
|
const receivedItems = get(receivedData, ['data', 0]) ?? [];
|
||||||
|
|
||||||
const returnData: Document[] = [];
|
const returnData: Document[] = [];
|
||||||
for (const [index, itemData] of receivedItems[0].entries()) {
|
for (const [index, itemData] of receivedItems.entries()) {
|
||||||
const pageContent = objectToString(itemData.json);
|
const pageContent = objectToString(itemData.json);
|
||||||
returnData.push(
|
returnData.push(
|
||||||
new Document({
|
new Document({
|
||||||
|
|
|
@ -14,8 +14,10 @@ import type {
|
||||||
ISupplyDataFunctions,
|
ISupplyDataFunctions,
|
||||||
SupplyData,
|
SupplyData,
|
||||||
ExecutionError,
|
ExecutionError,
|
||||||
|
ExecuteWorkflowData,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
INodeParameterResourceLocator,
|
INodeParameterResourceLocator,
|
||||||
|
ITaskMetadata,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeConnectionType, NodeOperationError, jsonParse } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError, jsonParse } from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -360,6 +362,7 @@ export class ToolWorkflow implements INodeType {
|
||||||
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
|
||||||
const name = this.getNodeParameter('name', itemIndex) as string;
|
const name = this.getNodeParameter('name', itemIndex) as string;
|
||||||
const description = this.getNodeParameter('description', itemIndex) as string;
|
const description = this.getNodeParameter('description', itemIndex) as string;
|
||||||
|
let executionId: string | undefined = undefined;
|
||||||
|
|
||||||
const useSchema = this.getNodeParameter('specifyInputSchema', itemIndex) as boolean;
|
const useSchema = this.getNodeParameter('specifyInputSchema', itemIndex) as boolean;
|
||||||
let tool: DynamicTool | DynamicStructuredTool | undefined = undefined;
|
let tool: DynamicTool | DynamicStructuredTool | undefined = undefined;
|
||||||
|
@ -440,13 +443,17 @@ export class ToolWorkflow implements INodeType {
|
||||||
|
|
||||||
const items = [newItem] as INodeExecutionData[];
|
const items = [newItem] as INodeExecutionData[];
|
||||||
|
|
||||||
let receivedData: INodeExecutionData;
|
const workflowProxy = this.getWorkflowDataProxy(0);
|
||||||
|
|
||||||
|
let receivedData: ExecuteWorkflowData;
|
||||||
try {
|
try {
|
||||||
receivedData = (await this.executeWorkflow(
|
receivedData = await this.executeWorkflow(workflowInfo, items, runManager?.getChild(), {
|
||||||
workflowInfo,
|
startMetadata: {
|
||||||
items,
|
executionId: workflowProxy.$execution.id,
|
||||||
runManager?.getChild(),
|
workflowId: workflowProxy.$workflow.id,
|
||||||
)) as INodeExecutionData;
|
},
|
||||||
|
});
|
||||||
|
executionId = receivedData.executionId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Make sure a valid error gets returned that can by json-serialized else it will
|
// Make sure a valid error gets returned that can by json-serialized else it will
|
||||||
// not show up in the frontend
|
// not show up in the frontend
|
||||||
|
@ -454,6 +461,7 @@ export class ToolWorkflow implements INodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
const response: string | undefined = get(receivedData, [
|
const response: string | undefined = get(receivedData, [
|
||||||
|
'data',
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
'json',
|
'json',
|
||||||
|
@ -503,10 +511,22 @@ export class ToolWorkflow implements INodeType {
|
||||||
response = `There was an error: "${executionError.message}"`;
|
response = `There was an error: "${executionError.message}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let metadata: ITaskMetadata | undefined;
|
||||||
|
if (executionId) {
|
||||||
|
metadata = {
|
||||||
|
executionId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (executionError) {
|
if (executionError) {
|
||||||
void this.addOutputData(NodeConnectionType.AiTool, index, executionError);
|
void this.addOutputData(NodeConnectionType.AiTool, index, executionError, metadata);
|
||||||
} else {
|
} else {
|
||||||
void this.addOutputData(NodeConnectionType.AiTool, index, [[{ json: { response } }]]);
|
void this.addOutputData(
|
||||||
|
NodeConnectionType.AiTool,
|
||||||
|
index,
|
||||||
|
[[{ json: { response } }]],
|
||||||
|
metadata,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import type { Scope } from '@n8n/permissions';
|
import type { Scope } from '@n8n/permissions';
|
||||||
import type { Application } from 'express';
|
import type { Application } from 'express';
|
||||||
import type { WorkflowExecute } from 'n8n-core';
|
|
||||||
import type {
|
import type {
|
||||||
ExecutionError,
|
ExecutionError,
|
||||||
ICredentialDataDecryptedObject,
|
ICredentialDataDecryptedObject,
|
||||||
|
@ -14,7 +13,6 @@ import type {
|
||||||
ITelemetryTrackProperties,
|
ITelemetryTrackProperties,
|
||||||
IWorkflowBase,
|
IWorkflowBase,
|
||||||
CredentialLoadingDetails,
|
CredentialLoadingDetails,
|
||||||
Workflow,
|
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
ExecutionStatus,
|
ExecutionStatus,
|
||||||
ExecutionSummary,
|
ExecutionSummary,
|
||||||
|
@ -296,12 +294,6 @@ export interface IWorkflowErrorData {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWorkflowExecuteProcess {
|
|
||||||
startedAt: Date;
|
|
||||||
workflow: Workflow;
|
|
||||||
workflowExecute: WorkflowExecute;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWorkflowStatisticsDataLoaded {
|
export interface IWorkflowStatisticsDataLoaded {
|
||||||
dataLoaded: boolean;
|
dataLoaded: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ import type {
|
||||||
ExecuteWorkflowOptions,
|
ExecuteWorkflowOptions,
|
||||||
IWorkflowExecutionDataProcess,
|
IWorkflowExecutionDataProcess,
|
||||||
EnvProviderState,
|
EnvProviderState,
|
||||||
|
ITaskMetadata,
|
||||||
|
ExecuteWorkflowData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
|
@ -45,11 +47,7 @@ import { CredentialsHelper } from '@/credentials-helper';
|
||||||
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
||||||
import type { AiEventMap, AiEventPayload } from '@/events/maps/ai.event-map';
|
import type { AiEventMap, AiEventPayload } from '@/events/maps/ai.event-map';
|
||||||
import { ExternalHooks } from '@/external-hooks';
|
import { ExternalHooks } from '@/external-hooks';
|
||||||
import type {
|
import type { IWorkflowErrorData, UpdateExecutionPayload } from '@/interfaces';
|
||||||
IWorkflowExecuteProcess,
|
|
||||||
IWorkflowErrorData,
|
|
||||||
UpdateExecutionPayload,
|
|
||||||
} from '@/interfaces';
|
|
||||||
import { NodeTypes } from '@/node-types';
|
import { NodeTypes } from '@/node-types';
|
||||||
import { Push } from '@/push';
|
import { Push } from '@/push';
|
||||||
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
||||||
|
@ -681,6 +679,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks {
|
||||||
export async function getRunData(
|
export async function getRunData(
|
||||||
workflowData: IWorkflowBase,
|
workflowData: IWorkflowBase,
|
||||||
inputData?: INodeExecutionData[],
|
inputData?: INodeExecutionData[],
|
||||||
|
metadata?: ITaskMetadata,
|
||||||
): Promise<IWorkflowExecutionDataProcess> {
|
): Promise<IWorkflowExecutionDataProcess> {
|
||||||
const mode = 'integrated';
|
const mode = 'integrated';
|
||||||
|
|
||||||
|
@ -700,6 +699,7 @@ export async function getRunData(
|
||||||
data: {
|
data: {
|
||||||
main: [inputData],
|
main: [inputData],
|
||||||
},
|
},
|
||||||
|
metadata,
|
||||||
source: null,
|
source: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -771,14 +771,12 @@ export async function executeWorkflow(
|
||||||
workflowInfo: IExecuteWorkflowInfo,
|
workflowInfo: IExecuteWorkflowInfo,
|
||||||
additionalData: IWorkflowExecuteAdditionalData,
|
additionalData: IWorkflowExecuteAdditionalData,
|
||||||
options: ExecuteWorkflowOptions,
|
options: ExecuteWorkflowOptions,
|
||||||
): Promise<Array<INodeExecutionData[] | null> | IWorkflowExecuteProcess> {
|
): Promise<ExecuteWorkflowData> {
|
||||||
const externalHooks = Container.get(ExternalHooks);
|
const externalHooks = Container.get(ExternalHooks);
|
||||||
await externalHooks.init();
|
await externalHooks.init();
|
||||||
|
|
||||||
const nodeTypes = Container.get(NodeTypes);
|
const nodeTypes = Container.get(NodeTypes);
|
||||||
const activeExecutions = Container.get(ActiveExecutions);
|
const activeExecutions = Container.get(ActiveExecutions);
|
||||||
const eventService = Container.get(EventService);
|
|
||||||
const executionRepository = Container.get(ExecutionRepository);
|
|
||||||
|
|
||||||
const workflowData =
|
const workflowData =
|
||||||
options.loadedWorkflowData ??
|
options.loadedWorkflowData ??
|
||||||
|
@ -796,10 +794,44 @@ export async function executeWorkflow(
|
||||||
settings: workflowData.settings,
|
settings: workflowData.settings,
|
||||||
});
|
});
|
||||||
|
|
||||||
const runData = options.loadedRunData ?? (await getRunData(workflowData, options.inputData));
|
const runData =
|
||||||
|
options.loadedRunData ??
|
||||||
|
(await getRunData(workflowData, options.inputData, options.startMetadata));
|
||||||
|
|
||||||
const executionId = await activeExecutions.add(runData);
|
const executionId = await activeExecutions.add(runData);
|
||||||
|
|
||||||
|
// We wrap it in another promise that we can depending on the setting return
|
||||||
|
// the execution ID before the execution is finished
|
||||||
|
const executionPromise = startExecution(
|
||||||
|
additionalData,
|
||||||
|
options,
|
||||||
|
workflow,
|
||||||
|
executionId,
|
||||||
|
runData,
|
||||||
|
workflowData,
|
||||||
|
externalHooks,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (options.doNotWaitToFinish) {
|
||||||
|
return { executionId, data: [null] };
|
||||||
|
}
|
||||||
|
|
||||||
|
return await executionPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startExecution(
|
||||||
|
additionalData: IWorkflowExecuteAdditionalData,
|
||||||
|
options: ExecuteWorkflowOptions,
|
||||||
|
workflow: Workflow,
|
||||||
|
executionId: string,
|
||||||
|
runData: IWorkflowExecutionDataProcess,
|
||||||
|
workflowData: IWorkflowBase,
|
||||||
|
externalHooks: ExternalHooks,
|
||||||
|
): Promise<ExecuteWorkflowData> {
|
||||||
|
const eventService = Container.get(EventService);
|
||||||
|
const activeExecutions = Container.get(ActiveExecutions);
|
||||||
|
const executionRepository = Container.get(ExecutionRepository);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A subworkflow execution in queue mode is not enqueued, but rather runs in the
|
* A subworkflow execution in queue mode is not enqueued, but rather runs in the
|
||||||
* same worker process as the parent execution. Hence ensure the subworkflow
|
* same worker process as the parent execution. Hence ensure the subworkflow
|
||||||
|
@ -921,7 +953,10 @@ export async function executeWorkflow(
|
||||||
|
|
||||||
activeExecutions.finalizeExecution(executionId, data);
|
activeExecutions.finalizeExecution(executionId, data);
|
||||||
const returnData = WorkflowHelpers.getDataLastExecutedNodeData(data);
|
const returnData = WorkflowHelpers.getDataLastExecutedNodeData(data);
|
||||||
return returnData!.data!.main;
|
return {
|
||||||
|
executionId,
|
||||||
|
data: returnData!.data!.main,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
activeExecutions.finalizeExecution(executionId, data);
|
activeExecutions.finalizeExecution(executionId, data);
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,14 @@ import get from 'lodash/get';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
|
import set from 'lodash/set';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { extension, lookup } from 'mime-types';
|
import { extension, lookup } from 'mime-types';
|
||||||
import type {
|
import type {
|
||||||
BinaryHelperFunctions,
|
BinaryHelperFunctions,
|
||||||
CloseFunction,
|
CloseFunction,
|
||||||
ContextType,
|
ContextType,
|
||||||
|
ExecuteWorkflowData,
|
||||||
FieldType,
|
FieldType,
|
||||||
FileSystemHelperFunctions,
|
FileSystemHelperFunctions,
|
||||||
FunctionsBase,
|
FunctionsBase,
|
||||||
|
@ -78,6 +80,7 @@ import type {
|
||||||
IRunExecutionData,
|
IRunExecutionData,
|
||||||
ITaskData,
|
ITaskData,
|
||||||
ITaskDataConnections,
|
ITaskDataConnections,
|
||||||
|
ITaskMetadata,
|
||||||
ITriggerFunctions,
|
ITriggerFunctions,
|
||||||
IWebhookData,
|
IWebhookData,
|
||||||
IWebhookDescription,
|
IWebhookDescription,
|
||||||
|
@ -2710,6 +2713,7 @@ const addExecutionDataFunctions = async (
|
||||||
sourceNodeName: string,
|
sourceNodeName: string,
|
||||||
sourceNodeRunIndex: number,
|
sourceNodeRunIndex: number,
|
||||||
currentNodeRunIndex: number,
|
currentNodeRunIndex: number,
|
||||||
|
metadata?: ITaskMetadata,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
if (connectionType === NodeConnectionType.Main) {
|
if (connectionType === NodeConnectionType.Main) {
|
||||||
throw new ApplicationError('Setting type is not supported for main connection', {
|
throw new ApplicationError('Setting type is not supported for main connection', {
|
||||||
|
@ -2735,6 +2739,7 @@ const addExecutionDataFunctions = async (
|
||||||
if (taskData === undefined) {
|
if (taskData === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
taskData.metadata = metadata;
|
||||||
}
|
}
|
||||||
taskData = taskData!;
|
taskData = taskData!;
|
||||||
|
|
||||||
|
@ -3568,6 +3573,13 @@ export function getExecuteTriggerFunctions(
|
||||||
return new TriggerContext(workflow, node, additionalData, mode, activation);
|
return new TriggerContext(workflow, node, additionalData, mode, activation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setMetadata(executeData: IExecuteData, key: string, value: string) {
|
||||||
|
if (!executeData.metadata) {
|
||||||
|
executeData.metadata = {};
|
||||||
|
}
|
||||||
|
set(executeData.metadata, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the execute functions regular nodes have access to.
|
* Returns the execute functions regular nodes have access to.
|
||||||
*/
|
*/
|
||||||
|
@ -3603,6 +3615,9 @@ export function getExecuteFunctions(
|
||||||
itemIndex,
|
itemIndex,
|
||||||
),
|
),
|
||||||
getExecuteData: () => executeData,
|
getExecuteData: () => executeData,
|
||||||
|
setMetadata: (key: string, value: string): void => {
|
||||||
|
return setMetadata(executeData, key, value);
|
||||||
|
},
|
||||||
continueOnFail: () => {
|
continueOnFail: () => {
|
||||||
return continueOnFail(node);
|
return continueOnFail(node);
|
||||||
},
|
},
|
||||||
|
@ -3624,23 +3639,28 @@ export function getExecuteFunctions(
|
||||||
workflowInfo: IExecuteWorkflowInfo,
|
workflowInfo: IExecuteWorkflowInfo,
|
||||||
inputData?: INodeExecutionData[],
|
inputData?: INodeExecutionData[],
|
||||||
parentCallbackManager?: CallbackManager,
|
parentCallbackManager?: CallbackManager,
|
||||||
): Promise<any> {
|
options?: {
|
||||||
|
doNotWaitToFinish?: boolean;
|
||||||
|
startMetadata?: ITaskMetadata;
|
||||||
|
},
|
||||||
|
): Promise<ExecuteWorkflowData> {
|
||||||
return await additionalData
|
return await additionalData
|
||||||
.executeWorkflow(workflowInfo, additionalData, {
|
.executeWorkflow(workflowInfo, additionalData, {
|
||||||
|
...options,
|
||||||
parentWorkflowId: workflow.id?.toString(),
|
parentWorkflowId: workflow.id?.toString(),
|
||||||
inputData,
|
inputData,
|
||||||
parentWorkflowSettings: workflow.settings,
|
parentWorkflowSettings: workflow.settings,
|
||||||
node,
|
node,
|
||||||
parentCallbackManager,
|
parentCallbackManager,
|
||||||
})
|
})
|
||||||
.then(
|
.then(async (result) => {
|
||||||
async (result) =>
|
const data = await Container.get(BinaryDataService).duplicateBinaryData(
|
||||||
await Container.get(BinaryDataService).duplicateBinaryData(
|
workflow.id,
|
||||||
workflow.id,
|
additionalData.executionId!,
|
||||||
additionalData.executionId!,
|
result.data,
|
||||||
result,
|
);
|
||||||
),
|
return { ...result, data };
|
||||||
);
|
});
|
||||||
},
|
},
|
||||||
getContext(type: ContextType): IContextObject {
|
getContext(type: ContextType): IContextObject {
|
||||||
return NodeHelpers.getContext(runExecutionData, type, node);
|
return NodeHelpers.getContext(runExecutionData, type, node);
|
||||||
|
@ -3834,6 +3854,7 @@ export function getExecuteFunctions(
|
||||||
connectionType: NodeConnectionType,
|
connectionType: NodeConnectionType,
|
||||||
currentNodeRunIndex: number,
|
currentNodeRunIndex: number,
|
||||||
data: INodeExecutionData[][] | ExecutionBaseError,
|
data: INodeExecutionData[][] | ExecutionBaseError,
|
||||||
|
metadata?: ITaskMetadata,
|
||||||
): void {
|
): void {
|
||||||
addExecutionDataFunctions(
|
addExecutionDataFunctions(
|
||||||
'output',
|
'output',
|
||||||
|
@ -3845,6 +3866,7 @@ export function getExecuteFunctions(
|
||||||
node.name,
|
node.name,
|
||||||
runIndex,
|
runIndex,
|
||||||
currentNodeRunIndex,
|
currentNodeRunIndex,
|
||||||
|
metadata,
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
Logger.warn(
|
Logger.warn(
|
||||||
`There was a problem logging output data of node "${this.getNode().name}": ${
|
`There was a problem logging output data of node "${this.getNode().name}": ${
|
||||||
|
@ -3953,7 +3975,11 @@ export function getSupplyDataFunctions(
|
||||||
workflowInfo: IExecuteWorkflowInfo,
|
workflowInfo: IExecuteWorkflowInfo,
|
||||||
inputData?: INodeExecutionData[],
|
inputData?: INodeExecutionData[],
|
||||||
parentCallbackManager?: CallbackManager,
|
parentCallbackManager?: CallbackManager,
|
||||||
) =>
|
options?: {
|
||||||
|
doNotWaitToFinish?: boolean;
|
||||||
|
startMetadata?: ITaskMetadata;
|
||||||
|
},
|
||||||
|
): Promise<ExecuteWorkflowData> =>
|
||||||
await additionalData
|
await additionalData
|
||||||
.executeWorkflow(workflowInfo, additionalData, {
|
.executeWorkflow(workflowInfo, additionalData, {
|
||||||
parentWorkflowId: workflow.id?.toString(),
|
parentWorkflowId: workflow.id?.toString(),
|
||||||
|
@ -3961,15 +3987,16 @@ export function getSupplyDataFunctions(
|
||||||
parentWorkflowSettings: workflow.settings,
|
parentWorkflowSettings: workflow.settings,
|
||||||
node,
|
node,
|
||||||
parentCallbackManager,
|
parentCallbackManager,
|
||||||
|
...options,
|
||||||
})
|
})
|
||||||
.then(
|
.then(async (result) => {
|
||||||
async (result) =>
|
const data = await Container.get(BinaryDataService).duplicateBinaryData(
|
||||||
await Container.get(BinaryDataService).duplicateBinaryData(
|
workflow.id,
|
||||||
workflow.id,
|
additionalData.executionId!,
|
||||||
additionalData.executionId!,
|
result.data,
|
||||||
result,
|
);
|
||||||
),
|
return { ...result, data };
|
||||||
),
|
}),
|
||||||
getNodeOutputs() {
|
getNodeOutputs() {
|
||||||
const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
|
||||||
return NodeHelpers.getNodeOutputs(workflow, node, nodeType.description).map((output) => {
|
return NodeHelpers.getNodeOutputs(workflow, node, nodeType.description).map((output) => {
|
||||||
|
|
|
@ -1448,6 +1448,7 @@ export class WorkflowExecute {
|
||||||
startTime,
|
startTime,
|
||||||
executionTime: new Date().getTime() - startTime,
|
executionTime: new Date().getTime() - startTime,
|
||||||
source: !executionData.source ? [] : executionData.source.main,
|
source: !executionData.source ? [] : executionData.source.main,
|
||||||
|
metadata: executionData.metadata,
|
||||||
executionStatus: 'success',
|
executionStatus: 'success',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import set from 'lodash/set';
|
||||||
import type {
|
import type {
|
||||||
ICredentialDataDecryptedObject,
|
ICredentialDataDecryptedObject,
|
||||||
IGetNodeParameterOptions,
|
IGetNodeParameterOptions,
|
||||||
|
@ -36,6 +37,13 @@ import { BinaryHelpers } from './helpers/binary-helpers';
|
||||||
import { RequestHelpers } from './helpers/request-helpers';
|
import { RequestHelpers } from './helpers/request-helpers';
|
||||||
import { NodeExecutionContext } from './node-execution-context';
|
import { NodeExecutionContext } from './node-execution-context';
|
||||||
|
|
||||||
|
function setMetadata(executeData: IExecuteData, key: string, value: string) {
|
||||||
|
if (!executeData.metadata) {
|
||||||
|
executeData.metadata = {};
|
||||||
|
}
|
||||||
|
set(executeData.metadata, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
export class ExecuteSingleContext extends NodeExecutionContext implements IExecuteSingleFunctions {
|
export class ExecuteSingleContext extends NodeExecutionContext implements IExecuteSingleFunctions {
|
||||||
readonly helpers: IExecuteSingleFunctions['helpers'];
|
readonly helpers: IExecuteSingleFunctions['helpers'];
|
||||||
|
|
||||||
|
@ -79,6 +87,10 @@ export class ExecuteSingleContext extends NodeExecutionContext implements IExecu
|
||||||
this.abortSignal?.addEventListener('abort', fn);
|
this.abortSignal?.addEventListener('abort', fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMetadata(key: string, value: string): void {
|
||||||
|
return setMetadata(this.executeData, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
continueOnFail() {
|
continueOnFail() {
|
||||||
return continueOnFail(this.node);
|
return continueOnFail(this.node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
|
ExecuteWorkflowData,
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeType,
|
INodeType,
|
||||||
|
@ -209,8 +210,10 @@ export class ExecuteWorkflow implements INodeType {
|
||||||
const mode = this.getNodeParameter('mode', 0, false) as string;
|
const mode = this.getNodeParameter('mode', 0, false) as string;
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
|
|
||||||
|
const workflowProxy = this.getWorkflowDataProxy(0);
|
||||||
|
|
||||||
if (mode === 'each') {
|
if (mode === 'each') {
|
||||||
let returnData: INodeExecutionData[][] = [];
|
const returnData: INodeExecutionData[][] = [];
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
try {
|
try {
|
||||||
|
@ -222,14 +225,26 @@ export class ExecuteWorkflow implements INodeType {
|
||||||
const workflowInfo = await getWorkflowInfo.call(this, source, i);
|
const workflowInfo = await getWorkflowInfo.call(this, source, i);
|
||||||
|
|
||||||
if (waitForSubWorkflow) {
|
if (waitForSubWorkflow) {
|
||||||
const workflowResult: INodeExecutionData[][] = await this.executeWorkflow(
|
const executionResult: ExecuteWorkflowData = await this.executeWorkflow(
|
||||||
workflowInfo,
|
workflowInfo,
|
||||||
[items[i]],
|
[items[i]],
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
startMetadata: {
|
||||||
|
executionId: workflowProxy.$execution.id,
|
||||||
|
workflowId: workflowProxy.$workflow.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
const workflowResult = executionResult.data as INodeExecutionData[][];
|
||||||
|
|
||||||
for (const [outputIndex, outputData] of workflowResult.entries()) {
|
for (const [outputIndex, outputData] of workflowResult.entries()) {
|
||||||
for (const item of outputData) {
|
for (const item of outputData) {
|
||||||
item.pairedItem = { item: i };
|
item.pairedItem = { item: i };
|
||||||
|
item.metadata = {
|
||||||
|
executionId: executionResult.executionId,
|
||||||
|
workflowId: workflowInfo.id,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnData[outputIndex] === undefined) {
|
if (returnData[outputIndex] === undefined) {
|
||||||
|
@ -239,8 +254,29 @@ export class ExecuteWorkflow implements INodeType {
|
||||||
returnData[outputIndex].push(...outputData);
|
returnData[outputIndex].push(...outputData);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
void this.executeWorkflow(workflowInfo, [items[i]]);
|
const executionResult: ExecuteWorkflowData = await this.executeWorkflow(
|
||||||
returnData = [items];
|
workflowInfo,
|
||||||
|
[items[i]],
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
doNotWaitToFinish: true,
|
||||||
|
startMetadata: {
|
||||||
|
executionId: workflowProxy.$execution.id,
|
||||||
|
workflowId: workflowProxy.$workflow.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (returnData.length === 0) {
|
||||||
|
returnData.push([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnData[0].push({
|
||||||
|
...items[i],
|
||||||
|
metadata: {
|
||||||
|
executionId: executionResult.executionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.continueOnFail()) {
|
if (this.continueOnFail()) {
|
||||||
|
@ -268,15 +304,29 @@ export class ExecuteWorkflow implements INodeType {
|
||||||
) as boolean;
|
) as boolean;
|
||||||
const workflowInfo = await getWorkflowInfo.call(this, source);
|
const workflowInfo = await getWorkflowInfo.call(this, source);
|
||||||
|
|
||||||
|
const executionResult: ExecuteWorkflowData = await this.executeWorkflow(
|
||||||
|
workflowInfo,
|
||||||
|
items,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
doNotWaitToFinish: !waitForSubWorkflow,
|
||||||
|
startMetadata: {
|
||||||
|
executionId: workflowProxy.$execution.id,
|
||||||
|
workflowId: workflowProxy.$workflow.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.setMetadata('executionId', executionResult.executionId);
|
||||||
|
if (workflowInfo.id !== undefined) {
|
||||||
|
this.setMetadata('workflowId', workflowInfo.id);
|
||||||
|
}
|
||||||
|
|
||||||
if (!waitForSubWorkflow) {
|
if (!waitForSubWorkflow) {
|
||||||
void this.executeWorkflow(workflowInfo, items);
|
|
||||||
return [items];
|
return [items];
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowResult: INodeExecutionData[][] = await this.executeWorkflow(
|
const workflowResult = executionResult.data as INodeExecutionData[][];
|
||||||
workflowInfo,
|
|
||||||
items,
|
|
||||||
);
|
|
||||||
|
|
||||||
const fallbackPairedItemData = generatePairedItemData(items.length);
|
const fallbackPairedItemData = generatePairedItemData(items.length);
|
||||||
|
|
||||||
|
|
|
@ -484,6 +484,7 @@ export interface ISourceDataConnections {
|
||||||
|
|
||||||
export interface IExecuteData {
|
export interface IExecuteData {
|
||||||
data: ITaskDataConnections;
|
data: ITaskDataConnections;
|
||||||
|
metadata?: ITaskMetadata;
|
||||||
node: INode;
|
node: INode;
|
||||||
source: ITaskDataConnectionsSource | null;
|
source: ITaskDataConnectionsSource | null;
|
||||||
}
|
}
|
||||||
|
@ -935,6 +936,7 @@ export type ContextType = 'flow' | 'node';
|
||||||
|
|
||||||
type BaseExecutionFunctions = FunctionsBaseWithRequiredKeys<'getMode'> & {
|
type BaseExecutionFunctions = FunctionsBaseWithRequiredKeys<'getMode'> & {
|
||||||
continueOnFail(): boolean;
|
continueOnFail(): boolean;
|
||||||
|
setMetadata(key: string, value: string): void;
|
||||||
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
|
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
|
||||||
getContext(type: ContextType): IContextObject;
|
getContext(type: ContextType): IContextObject;
|
||||||
getExecuteData(): IExecuteData;
|
getExecuteData(): IExecuteData;
|
||||||
|
@ -952,7 +954,11 @@ export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn &
|
||||||
workflowInfo: IExecuteWorkflowInfo,
|
workflowInfo: IExecuteWorkflowInfo,
|
||||||
inputData?: INodeExecutionData[],
|
inputData?: INodeExecutionData[],
|
||||||
parentCallbackManager?: CallbackManager,
|
parentCallbackManager?: CallbackManager,
|
||||||
): Promise<any>;
|
options?: {
|
||||||
|
doNotWaitToFinish?: boolean;
|
||||||
|
startMetadata?: ITaskMetadata;
|
||||||
|
},
|
||||||
|
): Promise<ExecuteWorkflowData>;
|
||||||
getInputConnectionData(
|
getInputConnectionData(
|
||||||
inputName: NodeConnectionType,
|
inputName: NodeConnectionType,
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
|
@ -975,6 +981,7 @@ export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn &
|
||||||
connectionType: NodeConnectionType,
|
connectionType: NodeConnectionType,
|
||||||
currentNodeRunIndex: number,
|
currentNodeRunIndex: number,
|
||||||
data: INodeExecutionData[][] | ExecutionError,
|
data: INodeExecutionData[][] | ExecutionError,
|
||||||
|
metadata?: ITaskMetadata,
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
nodeHelpers: NodeHelperFunctions;
|
nodeHelpers: NodeHelperFunctions;
|
||||||
|
@ -1207,6 +1214,10 @@ export interface INodeExecutionData {
|
||||||
binary?: IBinaryKeyData;
|
binary?: IBinaryKeyData;
|
||||||
error?: NodeApiError | NodeOperationError;
|
error?: NodeApiError | NodeOperationError;
|
||||||
pairedItem?: IPairedItemData | IPairedItemData[] | number;
|
pairedItem?: IPairedItemData | IPairedItemData[] | number;
|
||||||
|
metadata?: {
|
||||||
|
executionId?: string;
|
||||||
|
workflowId?: string;
|
||||||
|
};
|
||||||
index?: number;
|
index?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,6 +1554,11 @@ export interface ITriggerResponse {
|
||||||
manualTriggerResponse?: Promise<INodeExecutionData[][]>;
|
manualTriggerResponse?: Promise<INodeExecutionData[][]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExecuteWorkflowData {
|
||||||
|
executionId: string;
|
||||||
|
data: Array<INodeExecutionData[] | null>;
|
||||||
|
}
|
||||||
|
|
||||||
export type WebhookSetupMethodNames = 'checkExists' | 'create' | 'delete';
|
export type WebhookSetupMethodNames = 'checkExists' | 'create' | 'delete';
|
||||||
|
|
||||||
export namespace MultiPartFormData {
|
export namespace MultiPartFormData {
|
||||||
|
@ -2127,6 +2143,8 @@ export interface ITaskSubRunMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITaskMetadata {
|
export interface ITaskMetadata {
|
||||||
|
executionId?: string;
|
||||||
|
workflowId?: string;
|
||||||
subRun?: ITaskSubRunMetadata[];
|
subRun?: ITaskSubRunMetadata[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2254,6 +2272,8 @@ export interface ExecuteWorkflowOptions {
|
||||||
loadedRunData?: IWorkflowExecutionDataProcess;
|
loadedRunData?: IWorkflowExecutionDataProcess;
|
||||||
parentWorkflowSettings?: IWorkflowSettings;
|
parentWorkflowSettings?: IWorkflowSettings;
|
||||||
parentCallbackManager?: CallbackManager;
|
parentCallbackManager?: CallbackManager;
|
||||||
|
startMetadata?: ITaskMetadata;
|
||||||
|
doNotWaitToFinish?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AiEvent =
|
export type AiEvent =
|
||||||
|
@ -2287,7 +2307,7 @@ export interface IWorkflowExecuteAdditionalData {
|
||||||
workflowInfo: IExecuteWorkflowInfo,
|
workflowInfo: IExecuteWorkflowInfo,
|
||||||
additionalData: IWorkflowExecuteAdditionalData,
|
additionalData: IWorkflowExecuteAdditionalData,
|
||||||
options: ExecuteWorkflowOptions,
|
options: ExecuteWorkflowOptions,
|
||||||
) => Promise<any>;
|
) => Promise<ExecuteWorkflowData>;
|
||||||
executionId?: string;
|
executionId?: string;
|
||||||
restartExecutionId?: string;
|
restartExecutionId?: string;
|
||||||
hooks?: WorkflowHooks;
|
hooks?: WorkflowHooks;
|
||||||
|
|
Loading…
Reference in a new issue