feat: WhatsApp Business Cloud Node - new operation sendAndWait (#12941)

Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
This commit is contained in:
Michael Kret
2025-02-27 07:20:37 +02:00
committed by GitHub
parent 83d03d53eb
commit 97defb3a83
13 changed files with 457 additions and 74 deletions

View File

@@ -40,6 +40,7 @@ import type {
IRunNodeResponse,
IWorkflowIssues,
INodeIssues,
INodeType,
} from 'n8n-workflow';
import {
LoggerProxy as Logger,
@@ -49,6 +50,7 @@ import {
sleep,
ExecutionCancelledError,
Node,
UnexpectedError,
} from 'n8n-workflow';
import PCancelable from 'p-cancelable';
@@ -971,6 +973,26 @@ export class WorkflowExecute {
return workflowIssues;
}
private getCustomOperation(node: INode, type: INodeType) {
if (!type.customOperations) return undefined;
if (type.execute) {
throw new UnexpectedError('Node type cannot have both customOperations and execute defined');
}
if (!node.parameters) return undefined;
const { customOperations } = type;
const { resource, operation } = node.parameters;
if (typeof resource !== 'string' || typeof operation !== 'string') return undefined;
if (!customOperations[resource] || !customOperations[resource][operation]) return undefined;
const customOperation = customOperations[resource][operation];
return customOperation;
}
/** Executes the given node */
// eslint-disable-next-line complexity
async runNode(
@@ -1000,8 +1022,16 @@ export class WorkflowExecute {
const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
const isDeclarativeNode = nodeType.description.requestDefaults !== undefined;
const customOperation = this.getCustomOperation(node, nodeType);
let connectionInputData: INodeExecutionData[] = [];
if (nodeType.execute || (!nodeType.poll && !nodeType.trigger && !nodeType.webhook)) {
if (
nodeType.execute ||
customOperation ||
(!nodeType.poll && !nodeType.trigger && !nodeType.webhook)
) {
// Only stop if first input is empty for execute runs. For all others run anyways
// because then it is a trigger node. As they only pass data through and so the input-data
// becomes output-data it has to be possible.
@@ -1060,7 +1090,7 @@ export class WorkflowExecute {
inputData = newInputData;
}
if (nodeType.execute) {
if (nodeType.execute || customOperation) {
const closeFunctions: CloseFunction[] = [];
const context = new ExecuteContext(
workflow,
@@ -1076,10 +1106,16 @@ export class WorkflowExecute {
abortSignal,
);
const data =
nodeType instanceof Node
? await nodeType.execute(context)
: await nodeType.execute.call(context);
let data;
if (customOperation) {
data = await customOperation.call(context);
} else if (nodeType.execute) {
data =
nodeType instanceof Node
? await nodeType.execute(context)
: await nodeType.execute.call(context);
}
const closeFunctionsResults = await Promise.allSettled(
closeFunctions.map(async (fn) => await fn()),
@@ -1152,8 +1188,10 @@ export class WorkflowExecute {
}
// For trigger nodes in any mode except "manual" do we simply pass the data through
return { data: inputData.main as INodeExecutionData[][] };
} else if (nodeType.webhook) {
// For webhook nodes always simply pass the data through
} else if (nodeType.webhook && !isDeclarativeNode) {
// Check if the node have requestDefaults(Declarative Node),
// else for webhook nodes always simply pass the data through
// as webhook method would be called by WebhookService
return { data: inputData.main as INodeExecutionData[][] };
} else {
// NOTE: This block is only called by nodes tests.