mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-06 21:54:01 -05: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,
|
||||
normalizeParallel,
|
||||
normalizeValue,
|
||||
normalizeVariables,
|
||||
sanitizeInputFormat,
|
||||
sanitizeTools,
|
||||
sanitizeVariable,
|
||||
sortEdges,
|
||||
} from './normalize'
|
||||
|
||||
@@ -228,11 +230,17 @@ export function hasWorkflowChanged(
|
||||
}
|
||||
|
||||
// 6. Compare variables
|
||||
const currentVariables = (currentState as any).variables || {}
|
||||
const deployedVariables = (deployedState as any).variables || {}
|
||||
const currentVariables = normalizeVariables((currentState as any).variables)
|
||||
const deployedVariables = normalizeVariables((deployedState as any).variables)
|
||||
|
||||
const normalizedCurrentVars = normalizeValue(currentVariables)
|
||||
const normalizedDeployedVars = normalizeValue(deployedVariables)
|
||||
const normalizedCurrentVars = normalizeValue(
|
||||
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)) {
|
||||
return true
|
||||
|
||||
@@ -88,6 +88,30 @@ export function sanitizeTools(tools: any[] | undefined): any[] {
|
||||
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
|
||||
* @param inputFormat - Array of input format configurations
|
||||
|
||||
Reference in New Issue
Block a user