Skip to content

Commit f44e7e3

Browse files
authored
fix(input): allow test value if no real value provided for inputs in deployed executions (#2186)
* fix(input): allow test value if no real value provided for inputs in deployed executions * ack PR comments
1 parent 6bfb643 commit f44e7e3

File tree

3 files changed

+128
-16
lines changed

3 files changed

+128
-16
lines changed

apps/sim/executor/execution/executor.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ export class DAGExecutor {
250250
const blockOutput = buildStartBlockOutput({
251251
resolution: startResolution,
252252
workflowInput: this.workflowInput,
253-
isDeployedExecution: this.contextExtensions?.isDeployedContext === true,
254253
})
255254

256255
state.setBlockState(startResolution.block.id, {

apps/sim/executor/utils/start-block.test.ts

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ function createBlock(
3333
}
3434

3535
describe('start-block utilities', () => {
36-
it('buildResolutionFromBlock returns null when metadata id missing', () => {
36+
it.concurrent('buildResolutionFromBlock returns null when metadata id missing', () => {
3737
const block = createBlock('api_trigger')
3838
;(block.metadata as Record<string, unknown>).id = undefined
3939

4040
expect(buildResolutionFromBlock(block)).toBeNull()
4141
})
4242

43-
it('resolveExecutorStartBlock prefers unified start block', () => {
43+
it.concurrent('resolveExecutorStartBlock prefers unified start block', () => {
4444
const blocks = [
4545
createBlock('api_trigger', 'api'),
4646
createBlock('starter', 'starter'),
@@ -56,7 +56,7 @@ describe('start-block utilities', () => {
5656
expect(resolution?.path).toBe(StartBlockPath.UNIFIED)
5757
})
5858

59-
it('buildStartBlockOutput normalizes unified start payload', () => {
59+
it.concurrent('buildStartBlockOutput normalizes unified start payload', () => {
6060
const block = createBlock('start_trigger', 'start')
6161
const resolution = {
6262
blockId: 'start',
@@ -67,15 +67,14 @@ describe('start-block utilities', () => {
6767
const output = buildStartBlockOutput({
6868
resolution,
6969
workflowInput: { payload: 'value' },
70-
isDeployedExecution: true,
7170
})
7271

7372
expect(output.payload).toBe('value')
7473
expect(output.input).toBeUndefined()
7574
expect(output.conversationId).toBeUndefined()
7675
})
7776

78-
it('buildStartBlockOutput uses trigger schema for API triggers', () => {
77+
it.concurrent('buildStartBlockOutput uses trigger schema for API triggers', () => {
7978
const apiBlock = createBlock('api_trigger', 'api', {
8079
subBlocks: {
8180
inputFormat: {
@@ -113,11 +112,108 @@ describe('start-block utilities', () => {
113112
},
114113
files,
115114
},
116-
isDeployedExecution: false,
117115
})
118116

119117
expect(output.name).toBe('Ada')
120118
expect(output.input).toEqual({ name: 'Ada', count: 5 })
121119
expect(output.files).toEqual(files)
122120
})
121+
122+
describe('inputFormat default values', () => {
123+
it.concurrent('uses default value when runtime does not provide the field', () => {
124+
const block = createBlock('start_trigger', 'start', {
125+
subBlocks: {
126+
inputFormat: {
127+
value: [
128+
{ name: 'input', type: 'string' },
129+
{ name: 'customField', type: 'string', value: 'defaultValue' },
130+
],
131+
},
132+
},
133+
})
134+
135+
const resolution = {
136+
blockId: 'start',
137+
block,
138+
path: StartBlockPath.UNIFIED,
139+
} as const
140+
141+
const output = buildStartBlockOutput({
142+
resolution,
143+
workflowInput: { input: 'hello' },
144+
})
145+
146+
expect(output.input).toBe('hello')
147+
expect(output.customField).toBe('defaultValue')
148+
})
149+
150+
it.concurrent('runtime value overrides default value', () => {
151+
const block = createBlock('start_trigger', 'start', {
152+
subBlocks: {
153+
inputFormat: {
154+
value: [{ name: 'customField', type: 'string', value: 'defaultValue' }],
155+
},
156+
},
157+
})
158+
159+
const resolution = {
160+
blockId: 'start',
161+
block,
162+
path: StartBlockPath.UNIFIED,
163+
} as const
164+
165+
const output = buildStartBlockOutput({
166+
resolution,
167+
workflowInput: { customField: 'runtimeValue' },
168+
})
169+
170+
expect(output.customField).toBe('runtimeValue')
171+
})
172+
173+
it.concurrent('empty string from runtime overrides default value', () => {
174+
const block = createBlock('start_trigger', 'start', {
175+
subBlocks: {
176+
inputFormat: {
177+
value: [{ name: 'customField', type: 'string', value: 'defaultValue' }],
178+
},
179+
},
180+
})
181+
182+
const resolution = {
183+
blockId: 'start',
184+
block,
185+
path: StartBlockPath.UNIFIED,
186+
} as const
187+
188+
const output = buildStartBlockOutput({
189+
resolution,
190+
workflowInput: { customField: '' },
191+
})
192+
193+
expect(output.customField).toBe('')
194+
})
195+
196+
it.concurrent('null from runtime does not override default value', () => {
197+
const block = createBlock('start_trigger', 'start', {
198+
subBlocks: {
199+
inputFormat: {
200+
value: [{ name: 'customField', type: 'string', value: 'defaultValue' }],
201+
},
202+
},
203+
})
204+
205+
const resolution = {
206+
blockId: 'start',
207+
block,
208+
path: StartBlockPath.UNIFIED,
209+
} as const
210+
211+
const output = buildStartBlockOutput({
212+
resolution,
213+
workflowInput: { customField: null },
214+
})
215+
216+
expect(output.customField).toBe('defaultValue')
217+
})
218+
})
123219
})

apps/sim/executor/utils/start-block.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,7 @@ interface DerivedInputResult {
176176

177177
function deriveInputFromFormat(
178178
inputFormat: InputFormatField[],
179-
workflowInput: unknown,
180-
isDeployedExecution: boolean
179+
workflowInput: unknown
181180
): DerivedInputResult {
182181
const structuredInput: Record<string, unknown> = {}
183182

@@ -205,7 +204,8 @@ function deriveInputFromFormat(
205204
}
206205
}
207206

208-
if ((fieldValue === undefined || fieldValue === null) && !isDeployedExecution) {
207+
// Use the default value from inputFormat if the field value wasn't provided at runtime
208+
if (fieldValue === undefined || fieldValue === null) {
209209
fieldValue = field.value
210210
}
211211

@@ -255,13 +255,28 @@ function ensureString(value: unknown): string {
255255
return typeof value === 'string' ? value : ''
256256
}
257257

258-
function buildUnifiedStartOutput(workflowInput: unknown): NormalizedBlockOutput {
258+
function buildUnifiedStartOutput(
259+
workflowInput: unknown,
260+
structuredInput: Record<string, unknown>,
261+
hasStructured: boolean
262+
): NormalizedBlockOutput {
259263
const output: NormalizedBlockOutput = {}
260264

265+
if (hasStructured) {
266+
for (const [key, value] of Object.entries(structuredInput)) {
267+
output[key] = value
268+
}
269+
}
270+
261271
if (isPlainObject(workflowInput)) {
262272
for (const [key, value] of Object.entries(workflowInput)) {
263273
if (key === 'onUploadError') continue
264-
output[key] = value
274+
// Runtime values override defaults (except undefined/null which mean "not provided")
275+
if (value !== undefined && value !== null) {
276+
output[key] = value
277+
} else if (!Object.hasOwn(output, key)) {
278+
output[key] = value
279+
}
265280
}
266281
}
267282

@@ -401,17 +416,19 @@ function extractSubBlocks(block: SerializedBlock): Record<string, unknown> | und
401416
export interface StartBlockOutputOptions {
402417
resolution: ExecutorStartResolution
403418
workflowInput: unknown
404-
isDeployedExecution: boolean
405419
}
406420

407421
export function buildStartBlockOutput(options: StartBlockOutputOptions): NormalizedBlockOutput {
408-
const { resolution, workflowInput, isDeployedExecution } = options
422+
const { resolution, workflowInput } = options
409423
const inputFormat = extractInputFormat(resolution.block)
410-
const { finalInput } = deriveInputFromFormat(inputFormat, workflowInput, isDeployedExecution)
424+
const { finalInput, structuredInput, hasStructured } = deriveInputFromFormat(
425+
inputFormat,
426+
workflowInput
427+
)
411428

412429
switch (resolution.path) {
413430
case StartBlockPath.UNIFIED:
414-
return buildUnifiedStartOutput(workflowInput)
431+
return buildUnifiedStartOutput(workflowInput, structuredInput, hasStructured)
415432

416433
case StartBlockPath.SPLIT_API:
417434
case StartBlockPath.SPLIT_INPUT:

0 commit comments

Comments
 (0)