From 35683dddd49e77361219bb6419cc201c8456d686 Mon Sep 17 00:00:00 2001 From: waleed Date: Wed, 7 Jan 2026 07:53:07 -0800 Subject: [PATCH] fix(resolver): add both new and old workflow blocks for backwards compat --- .../variables/resolvers/block.test.ts | 106 ++++++++++++++++++ .../sim/executor/variables/resolvers/block.ts | 6 +- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/apps/sim/executor/variables/resolvers/block.test.ts b/apps/sim/executor/variables/resolvers/block.test.ts index 3d773d6bce..8dd10b388a 100644 --- a/apps/sim/executor/variables/resolvers/block.test.ts +++ b/apps/sim/executor/variables/resolvers/block.test.ts @@ -817,6 +817,112 @@ describe('BlockResolver', () => { expect(resolver.resolve('', ctx)).toBe('vibrant-cliff') } ) + + it.concurrent( + 'real-world scenario: accessing entire response object via (workflow type)', + () => { + const workflow = createTestWorkflow([ + { id: 'workflow-block', name: 'Workflow 1', type: 'workflow' }, + ]) + const resolver = new BlockResolver(workflow) + + // Child Response block output (new format - no wrapper) + const ctx = createTestContext('current', { + 'workflow-block': { + success: true, + childWorkflowName: 'response-workflow-child-editor', + result: { + data: { + s: 'example string', + nums: [1, 2, 3], + n: 42, + obj: { key1: 'value1', key2: 'value2' }, + }, + status: 206, + headers: { 'Content-Type': 'application/json', apple: 'banana' }, + }, + }, + }) + + // OLD reference: should return the entire result object + // This is used when the user wants to get data, status, headers all at once + const response = resolver.resolve('', ctx) + expect(response).toEqual({ + data: { + s: 'example string', + nums: [1, 2, 3], + n: 42, + obj: { key1: 'value1', key2: 'value2' }, + }, + status: 206, + headers: { 'Content-Type': 'application/json', apple: 'banana' }, + }) + + // Verify individual fields can be accessed from the returned object + expect(response.status).toBe(206) + expect(response.headers.apple).toBe('banana') + expect(response.data.s).toBe('example string') + expect(response.data.n).toBe(42) + expect(response.data.nums).toEqual([1, 2, 3]) + expect(response.data.obj.key1).toBe('value1') + } + ) + + it.concurrent( + 'real-world scenario: workflow_input type block with ', + () => { + /** + * CRITICAL: Workflow blocks can have type 'workflow' OR 'workflow_input'. + * Both must support backwards compatibility. + * + * This test uses 'workflow_input' which is the actual type in production. + */ + const workflow = createTestWorkflow([ + { id: 'workflow-block', name: 'Workflow 1', type: 'workflow_input' }, + ]) + const resolver = new BlockResolver(workflow) + + const ctx = createTestContext('current', { + 'workflow-block': { + success: true, + childWorkflowName: 'response-workflow-child-editor', + result: { + data: { + s: 'example string', + nums: [1, 2, 3], + n: 42, + obj: { key1: 'value1', key2: 'value2' }, + }, + status: 206, + headers: { 'Content-Type': 'application/json', apple: 'banana' }, + }, + }, + }) + + // OLD reference: should return the entire result object + const response = resolver.resolve('', ctx) + expect(response).toEqual({ + data: { + s: 'example string', + nums: [1, 2, 3], + n: 42, + obj: { key1: 'value1', key2: 'value2' }, + }, + status: 206, + headers: { 'Content-Type': 'application/json', apple: 'banana' }, + }) + + // Also test drilling into specific fields + expect(resolver.resolve('', ctx)).toEqual({ + s: 'example string', + nums: [1, 2, 3], + n: 42, + obj: { key1: 'value1', key2: 'value2' }, + }) + expect(resolver.resolve('', ctx)).toBe(206) + expect(resolver.resolve('', ctx)).toBe('example string') + } + ) }) describe('edge cases', () => { diff --git a/apps/sim/executor/variables/resolvers/block.ts b/apps/sim/executor/variables/resolvers/block.ts index e555c9e4fe..ba786b1675 100644 --- a/apps/sim/executor/variables/resolvers/block.ts +++ b/apps/sim/executor/variables/resolvers/block.ts @@ -90,11 +90,13 @@ export class BlockResolver implements Resolver { // Workflow block backwards compatibility: // Old: -> New: // Only apply fallback if: - // 1. Block type is 'workflow' + // 1. Block type is 'workflow' or 'workflow_input' // 2. Path starts with 'result.response.' // 3. output.result.response doesn't exist (confirming child used new format) + const isWorkflowBlock = + block?.metadata?.id === 'workflow' || block?.metadata?.id === 'workflow_input' if ( - block?.metadata?.id === 'workflow' && + isWorkflowBlock && pathParts[0] === 'result' && pathParts[1] === 'response' && output?.result?.response === undefined