diff --git a/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts b/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts index 675670a9fa4..5e2e221aa1a 100644 --- a/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts +++ b/packages/cli/src/workflows/triggers/__tests__/workflow-trigger-activator.test.ts @@ -167,6 +167,54 @@ describe('WorkflowTriggerActivator', () => { ); }); + test('brackets the registrar call with an acquired isolate so path expressions resolve', async () => { + const callOrder: string[] = []; + vi.spyOn(WorkflowExpression.prototype, 'acquireIsolate').mockImplementation(async () => { + callOrder.push('acquire'); + }); + vi.spyOn(WorkflowExpression.prototype, 'releaseIsolate').mockImplementation(async () => { + callOrder.push('release'); + }); + vi.spyOn(WorkflowExecuteAdditionalData, 'getBase').mockResolvedValue( + mock(), + ); + const webhookTriggerRegistrar = mock(); + webhookTriggerRegistrar.getNodesWithUnregisteredWebhooks.mockImplementation(async () => { + callOrder.push('resolve'); + return new Set(['w']); + }); + const activator = buildActivator({ webhookTriggerRegistrar }); + + await activator.getNodesWithUnregisteredWebhooks( + mock({ id: 'wf-1', name: 'Test workflow', staticData: {}, settings: {} }), + { nodes: [node('w', 'webhook')], connections: {} }, + ); + + expect(callOrder).toEqual(['acquire', 'resolve', 'release']); + }); + + test('releases the isolate when the registrar throws', async () => { + vi.spyOn(WorkflowExecuteAdditionalData, 'getBase').mockResolvedValue( + mock(), + ); + const releaseIsolate = vi + .spyOn(WorkflowExpression.prototype, 'releaseIsolate') + .mockResolvedValue(undefined); + vi.spyOn(WorkflowExpression.prototype, 'acquireIsolate').mockResolvedValue(undefined); + const webhookTriggerRegistrar = mock(); + webhookTriggerRegistrar.getNodesWithUnregisteredWebhooks.mockRejectedValue(new Error('boom')); + const activator = buildActivator({ webhookTriggerRegistrar }); + + await expect( + activator.getNodesWithUnregisteredWebhooks( + mock({ id: 'wf-1', name: 'Test workflow', staticData: {}, settings: {} }), + { nodes: [node('w', 'webhook')], connections: {} }, + ), + ).rejects.toThrow('boom'); + + expect(releaseIsolate).toHaveBeenCalledTimes(1); + }); + test('returns empty without calling the registrar when there are no trigger nodes', async () => { const webhookTriggerRegistrar = mock(); const activator = buildActivator({ webhookTriggerRegistrar }); diff --git a/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts b/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts index 1303705a07f..bd2cfeabb14 100644 --- a/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts +++ b/packages/cli/src/workflows/triggers/workflow-trigger-activator.ts @@ -155,11 +155,18 @@ export class WorkflowTriggerActivator { workflowSettings: dbWorkflow.settings, }); - return await this.webhookTriggerRegistrar.getNodesWithUnregisteredWebhooks( - workflow, - additionalData, - desiredNodes, - ); + // Resolving webhook triggers evaluates each node's `path`/`httpMethod` + // expressions (e.g. the Form Trigger's dynamic path), which needs an isolate. + await workflow.expression.acquireIsolate(); + try { + return await this.webhookTriggerRegistrar.getNodesWithUnregisteredWebhooks( + workflow, + additionalData, + desiredNodes, + ); + } finally { + await workflow.expression.releaseIsolate(); + } } /**