mirror of
https://github.com/simstudioai/sim.git
synced 2026-10-31 20:53:15 +08:00
fix(deploy): fix workflow change detection to handle old variable reference format (#2623)
This commit is contained in:
@@ -2523,4 +2523,181 @@ describe('hasWorkflowChanged', () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Variables (UI-only fields should not trigger change)', () => {
|
||||||
|
it.concurrent('should not detect change when validationError differs', () => {
|
||||||
|
const deployedState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(deployedState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'plain',
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(currentState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'plain',
|
||||||
|
value: 'test',
|
||||||
|
validationError: undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(hasWorkflowChanged(currentState, deployedState)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it.concurrent('should not detect change when validationError has value vs missing', () => {
|
||||||
|
const deployedState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(deployedState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'number',
|
||||||
|
value: 'invalid',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(currentState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'number',
|
||||||
|
value: 'invalid',
|
||||||
|
validationError: 'Not a valid number',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(hasWorkflowChanged(currentState, deployedState)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it.concurrent('should detect change when variable value differs', () => {
|
||||||
|
const deployedState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(deployedState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'plain',
|
||||||
|
value: 'old value',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(currentState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'plain',
|
||||||
|
value: 'new value',
|
||||||
|
validationError: undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(hasWorkflowChanged(currentState, deployedState)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it.concurrent('should detect change when variable is added', () => {
|
||||||
|
const deployedState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(deployedState as any).variables = {}
|
||||||
|
|
||||||
|
const currentState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(currentState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'plain',
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(hasWorkflowChanged(currentState, deployedState)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it.concurrent('should detect change when variable is removed', () => {
|
||||||
|
const deployedState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(deployedState as any).variables = {
|
||||||
|
var1: {
|
||||||
|
id: 'var1',
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
name: 'myVar',
|
||||||
|
type: 'plain',
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(currentState as any).variables = {}
|
||||||
|
|
||||||
|
expect(hasWorkflowChanged(currentState, deployedState)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it.concurrent('should not detect change when empty array vs empty object', () => {
|
||||||
|
const deployedState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(deployedState as any).variables = []
|
||||||
|
|
||||||
|
const currentState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
;(currentState as any).variables = {}
|
||||||
|
|
||||||
|
expect(hasWorkflowChanged(currentState, deployedState)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import {
|
|||||||
normalizeLoop,
|
normalizeLoop,
|
||||||
normalizeParallel,
|
normalizeParallel,
|
||||||
normalizeValue,
|
normalizeValue,
|
||||||
|
normalizeVariables,
|
||||||
sanitizeInputFormat,
|
sanitizeInputFormat,
|
||||||
sanitizeTools,
|
sanitizeTools,
|
||||||
|
sanitizeVariable,
|
||||||
sortEdges,
|
sortEdges,
|
||||||
} from './normalize'
|
} from './normalize'
|
||||||
|
|
||||||
@@ -228,11 +230,17 @@ export function hasWorkflowChanged(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 6. Compare variables
|
// 6. Compare variables
|
||||||
const currentVariables = (currentState as any).variables || {}
|
const currentVariables = normalizeVariables((currentState as any).variables)
|
||||||
const deployedVariables = (deployedState as any).variables || {}
|
const deployedVariables = normalizeVariables((deployedState as any).variables)
|
||||||
|
|
||||||
const normalizedCurrentVars = normalizeValue(currentVariables)
|
const normalizedCurrentVars = normalizeValue(
|
||||||
const normalizedDeployedVars = normalizeValue(deployedVariables)
|
Object.fromEntries(Object.entries(currentVariables).map(([id, v]) => [id, sanitizeVariable(v)]))
|
||||||
|
)
|
||||||
|
const normalizedDeployedVars = normalizeValue(
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries(deployedVariables).map(([id, v]) => [id, sanitizeVariable(v)])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if (normalizedStringify(normalizedCurrentVars) !== normalizedStringify(normalizedDeployedVars)) {
|
if (normalizedStringify(normalizedCurrentVars) !== normalizedStringify(normalizedDeployedVars)) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -88,6 +88,30 @@ export function sanitizeTools(tools: any[] | undefined): any[] {
|
|||||||
return tools.map(({ isExpanded, ...rest }) => rest)
|
return tools.map(({ isExpanded, ...rest }) => rest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a variable by removing UI-only fields like validationError
|
||||||
|
* @param variable - The variable object
|
||||||
|
* @returns Sanitized variable object
|
||||||
|
*/
|
||||||
|
export function sanitizeVariable(variable: any): any {
|
||||||
|
if (!variable || typeof variable !== 'object') return variable
|
||||||
|
const { validationError, ...rest } = variable
|
||||||
|
return rest
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the variables structure to always be an object.
|
||||||
|
* Handles legacy data where variables might be stored as an empty array.
|
||||||
|
* @param variables - The variables to normalize
|
||||||
|
* @returns A normalized variables object
|
||||||
|
*/
|
||||||
|
export function normalizeVariables(variables: any): Record<string, any> {
|
||||||
|
if (!variables) return {}
|
||||||
|
if (Array.isArray(variables)) return {}
|
||||||
|
if (typeof variables !== 'object') return {}
|
||||||
|
return variables
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitizes inputFormat array by removing UI-only fields like value and collapsed
|
* Sanitizes inputFormat array by removing UI-only fields like value and collapsed
|
||||||
* @param inputFormat - Array of input format configurations
|
* @param inputFormat - Array of input format configurations
|
||||||
|
|||||||
Reference in New Issue
Block a user