Skip to content

Commit 83f4c8e

Browse files
committed
fix: parse inputs following schema when forking workflows
1 parent c854534 commit 83f4c8e

File tree

3 files changed

+245
-4
lines changed

3 files changed

+245
-4
lines changed

apps/api/src/workflows/services/workflow.service.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BaseService } from '@app/common/base/base.service'
2-
import { replaceTemplateFields } from '@app/definitions/utils/field.utils'
2+
import { fixObjectTypes, replaceTemplateFields } from '@app/definitions/utils/field.utils'
33
import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common'
44
import { DeepPartial, DeleteOneOptions, UpdateOneOptions } from '@ptc-org/nestjs-query-core'
55
import { ReturnModelType } from '@typegoose/typegoose'
@@ -224,7 +224,10 @@ export class WorkflowService extends BaseService<Workflow> {
224224
name: trigger.name,
225225
inputs: {
226226
...templateInputs,
227-
...replaceTemplateFields(idsMap, trigger.inputs ?? {}, templateInputs),
227+
...fixObjectTypes(
228+
replaceTemplateFields(idsMap, trigger.inputs ?? {}, templateInputs),
229+
workflow.templateSchema ?? {},
230+
),
228231
},
229232
credentials: credentialsForTrigger?.id,
230233
schedule: {
@@ -274,7 +277,10 @@ export class WorkflowService extends BaseService<Workflow> {
274277
name: action.name,
275278
inputs: {
276279
...templateInputs,
277-
...replaceTemplateFields(idsMap, action.inputs ?? {}, templateInputs),
280+
...fixObjectTypes(
281+
replaceTemplateFields(idsMap, action.inputs ?? {}, templateInputs),
282+
workflow.templateSchema ?? {},
283+
),
278284
},
279285
previousAction: idsMap.get(previousAction?.id ?? '') as any,
280286
previousActionCondition: previousAction?.condition,

libs/definitions/src/utils/field.utils.spec.ts

Lines changed: 187 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { getInterpolatedVariables, replaceTemplateFields } from './field.utils'
1+
import { JSONSchema7 } from 'json-schema'
2+
import { fixObjectTypes, getInterpolatedVariables, replaceTemplateFields } from './field.utils'
23

34
describe('FieldUtils', () => {
45
describe('getInterpolatedVariables', () => {
@@ -125,4 +126,189 @@ describe('FieldUtils', () => {
125126
})
126127
})
127128
})
129+
130+
describe('fixObjectTypes', () => {
131+
const schema: JSONSchema7 = {
132+
type: 'object',
133+
properties: {
134+
name: { type: 'string' },
135+
age: { type: 'integer' },
136+
active: { type: 'boolean' },
137+
score: { type: 'number' },
138+
hobbies: {
139+
type: 'array',
140+
items: { type: 'string' },
141+
},
142+
address: {
143+
type: 'object',
144+
properties: {
145+
street: { type: 'string' },
146+
city: { type: 'string' },
147+
},
148+
},
149+
friends: {
150+
type: 'array',
151+
items: {
152+
type: 'object',
153+
properties: {
154+
name: { type: 'string' },
155+
age: { type: 'integer' },
156+
active: { type: 'boolean' },
157+
},
158+
},
159+
},
160+
},
161+
}
162+
163+
it('should fix object types according to schema', () => {
164+
const input = {
165+
name: 'John Doe',
166+
age: '30',
167+
active: 'true',
168+
score: '100.5',
169+
hobbies: ['reading', 'sports'],
170+
address: {
171+
street: '123 Main St',
172+
city: 'New York',
173+
},
174+
}
175+
176+
const expectedOutput = {
177+
name: 'John Doe',
178+
age: 30,
179+
active: true,
180+
score: 100.5,
181+
hobbies: ['reading', 'sports'],
182+
address: {
183+
street: '123 Main St',
184+
city: 'New York',
185+
},
186+
}
187+
188+
expect(fixObjectTypes(input, schema)).toEqual(expectedOutput)
189+
})
190+
191+
it('should not change the value of items not following the schema', () => {
192+
const input = {
193+
name: 'John Doe',
194+
age: '{{template.age}}',
195+
active: '{{template.active}}',
196+
score: '{{template.score}}',
197+
hobbies: '{{template.hobbies}}',
198+
address: '{{template.address}}',
199+
}
200+
201+
const expectedOutput = {
202+
name: 'John Doe',
203+
age: '{{template.age}}',
204+
active: '{{template.active}}',
205+
score: '{{template.score}}',
206+
hobbies: '{{template.hobbies}}',
207+
address: '{{template.address}}',
208+
}
209+
210+
expect(fixObjectTypes(input, schema)).toEqual(expectedOutput)
211+
})
212+
213+
it('should not modify values with the correct type', () => {
214+
const input = {
215+
name: 'Jane Doe',
216+
age: 28,
217+
active: false,
218+
hobbies: ['painting', 'music'],
219+
address: {
220+
street: '456 Elm St',
221+
city: 'Los Angeles',
222+
},
223+
}
224+
225+
expect(fixObjectTypes(input, schema)).toEqual(input)
226+
})
227+
228+
it('should handle nested objects and arrays', () => {
229+
const input = {
230+
name: 'John Doe',
231+
age: '30',
232+
active: 'true',
233+
hobbies: ['reading', 'sports'],
234+
address: {
235+
street: '123 Main St',
236+
city: 'New York',
237+
},
238+
friends: [
239+
{
240+
name: 'Alice',
241+
age: '35',
242+
active: 'false',
243+
},
244+
{
245+
name: 'Bob',
246+
age: '32',
247+
active: 'true',
248+
},
249+
],
250+
}
251+
252+
const expectedOutput = {
253+
name: 'John Doe',
254+
age: 30,
255+
active: true,
256+
hobbies: ['reading', 'sports'],
257+
address: {
258+
street: '123 Main St',
259+
city: 'New York',
260+
},
261+
friends: [
262+
{
263+
name: 'Alice',
264+
age: 35,
265+
active: false,
266+
},
267+
{
268+
name: 'Bob',
269+
age: 32,
270+
active: true,
271+
},
272+
],
273+
}
274+
275+
expect(fixObjectTypes(input, schema)).toEqual(expectedOutput)
276+
})
277+
278+
it('should handle empty objects and arrays', () => {
279+
const input = {
280+
name: 'John Doe',
281+
age: '30',
282+
active: 'true',
283+
hobbies: [],
284+
address: {},
285+
}
286+
287+
const expectedOutput = {
288+
name: 'John Doe',
289+
age: 30,
290+
active: true,
291+
hobbies: [],
292+
address: {},
293+
}
294+
295+
expect(fixObjectTypes(input, schema)).toEqual(expectedOutput)
296+
})
297+
298+
it('should handle missing properties', () => {
299+
const input = {
300+
name: 'John Doe',
301+
age: '30',
302+
active: 'true',
303+
}
304+
305+
const expectedOutput = {
306+
name: 'John Doe',
307+
age: 30,
308+
active: true,
309+
}
310+
311+
expect(fixObjectTypes(input, schema)).toEqual(expectedOutput)
312+
})
313+
})
128314
})

libs/definitions/src/utils/field.utils.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getAddress, isAddress } from 'ethers/lib/utils'
2+
import { JSONSchema7, JSONSchema7TypeName } from 'json-schema'
23
import { VarEvm } from '../operation-evm'
34

45
export function hasInterpolation(str: string) {
@@ -135,3 +136,51 @@ export function replaceTemplateFields(
135136
}
136137
return result
137138
}
139+
140+
export function fixObjectTypes(obj: Record<string, any>, schema: JSONSchema7): Record<string, any> {
141+
if (!schema?.properties) {
142+
return obj
143+
}
144+
if (!obj || typeof obj !== 'object') {
145+
return obj
146+
}
147+
for (const [key, value] of Object.entries(obj)) {
148+
const type: JSONSchema7TypeName = schema.properties[key] && (schema.properties[key] as any).type
149+
switch (type) {
150+
case 'object':
151+
obj[key] = fixObjectTypes(value, schema.properties![key] as JSONSchema7)
152+
break
153+
case 'array':
154+
if (Array.isArray(value)) {
155+
obj[key] = value.map((item) => {
156+
if (typeof item === 'object') {
157+
return fixObjectTypes(item, (schema.properties![key] as any).items as JSONSchema7)
158+
} else {
159+
return item
160+
}
161+
})
162+
} else {
163+
obj[key] = value
164+
}
165+
166+
break
167+
case 'boolean':
168+
obj[key] = value === 'true' ? true : value === 'false' ? false : value
169+
break
170+
case 'number':
171+
obj[key] = Number.isFinite(Number(value)) ? Number(value) : value
172+
break
173+
case 'integer':
174+
obj[key] = Number.isInteger(Number(value)) ? Number(value) : value
175+
break
176+
case 'null':
177+
obj[key] = value === 'null' ? null : value
178+
break
179+
case 'string':
180+
default:
181+
obj[key] = value
182+
break
183+
}
184+
}
185+
return obj
186+
}

0 commit comments

Comments
 (0)