mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
fix(autolayout): type issue if workflow deployed + remove dead state code (#1524)
* fix(autolayout): type issue if workflow deployed * remove dead code hasActiveWebhook field
This commit is contained in:
committed by
GitHub
parent
7aae108b87
commit
3509ce8ce4
@@ -237,7 +237,6 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
parallels: {},
|
||||
isDeployed: true,
|
||||
deploymentStatuses: { production: 'deployed' },
|
||||
hasActiveWebhook: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -287,7 +286,6 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
parallels: {},
|
||||
isDeployed: true,
|
||||
deploymentStatuses: { production: 'deployed' },
|
||||
hasActiveWebhook: false,
|
||||
lastSaved: 1640995200000,
|
||||
},
|
||||
},
|
||||
@@ -309,7 +307,6 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
parallels: {},
|
||||
isDeployed: true,
|
||||
deploymentStatuses: { production: 'deployed' },
|
||||
hasActiveWebhook: false,
|
||||
lastSaved: 1640995200000,
|
||||
}),
|
||||
}
|
||||
@@ -445,7 +442,6 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
parallels: {},
|
||||
isDeployed: false,
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
lastSaved: 1640995200000,
|
||||
})
|
||||
})
|
||||
@@ -722,7 +718,6 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
production: 'deployed',
|
||||
staging: 'pending',
|
||||
},
|
||||
hasActiveWebhook: true,
|
||||
deployedAt: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
}
|
||||
@@ -769,7 +764,6 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
production: 'deployed',
|
||||
staging: 'pending',
|
||||
},
|
||||
hasActiveWebhook: true,
|
||||
deployedAt: '2024-01-01T10:00:00.000Z',
|
||||
lastSaved: 1640995200000,
|
||||
})
|
||||
|
||||
@@ -73,7 +73,6 @@ export async function POST(request: NextRequest) {
|
||||
parallels: checkpointState?.parallels || {},
|
||||
isDeployed: checkpointState?.isDeployed || false,
|
||||
deploymentStatuses: checkpointState?.deploymentStatuses || {},
|
||||
hasActiveWebhook: checkpointState?.hasActiveWebhook || false,
|
||||
lastSaved: Date.now(),
|
||||
// Only include deployedAt if it's a valid date string that can be converted
|
||||
...(checkpointState?.deployedAt &&
|
||||
|
||||
@@ -76,7 +76,6 @@ export async function POST(
|
||||
isDeployed: true,
|
||||
deployedAt: new Date(),
|
||||
deploymentStatuses: deployedState.deploymentStatuses || {},
|
||||
hasActiveWebhook: deployedState.hasActiveWebhook || false,
|
||||
})
|
||||
|
||||
if (!saveResult.success) {
|
||||
|
||||
@@ -133,7 +133,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
|
||||
state: {
|
||||
// Default values for expected properties
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
// Data from normalized tables
|
||||
blocks: normalizedData.blocks,
|
||||
edges: normalizedData.edges,
|
||||
|
||||
@@ -89,13 +89,6 @@ const ParallelSchema = z.object({
|
||||
parallelType: z.enum(['count', 'collection']).optional(),
|
||||
})
|
||||
|
||||
const DeploymentStatusSchema = z.object({
|
||||
id: z.string(),
|
||||
status: z.enum(['deploying', 'deployed', 'failed', 'stopping', 'stopped']),
|
||||
deployedAt: z.date().optional(),
|
||||
error: z.string().optional(),
|
||||
})
|
||||
|
||||
const WorkflowStateSchema = z.object({
|
||||
blocks: z.record(BlockStateSchema),
|
||||
edges: z.array(EdgeSchema),
|
||||
@@ -103,9 +96,7 @@ const WorkflowStateSchema = z.object({
|
||||
parallels: z.record(ParallelSchema).optional(),
|
||||
lastSaved: z.number().optional(),
|
||||
isDeployed: z.boolean().optional(),
|
||||
deployedAt: z.date().optional(),
|
||||
deploymentStatuses: z.record(DeploymentStatusSchema).optional(),
|
||||
hasActiveWebhook: z.boolean().optional(),
|
||||
deployedAt: z.coerce.date().optional(),
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -204,8 +195,6 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
lastSaved: state.lastSaved || Date.now(),
|
||||
isDeployed: state.isDeployed || false,
|
||||
deployedAt: state.deployedAt,
|
||||
deploymentStatuses: state.deploymentStatuses || {},
|
||||
hasActiveWebhook: state.hasActiveWebhook || false,
|
||||
}
|
||||
|
||||
const saveResult = await saveWorkflowToNormalizedTables(workflowId, workflowState as any)
|
||||
|
||||
@@ -89,7 +89,6 @@ export async function GET(request: NextRequest) {
|
||||
// Use normalized table data - construct state from normalized tables
|
||||
workflowState = {
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
blocks: normalizedData.blocks,
|
||||
edges: normalizedData.edges,
|
||||
loops: normalizedData.loops,
|
||||
|
||||
@@ -86,7 +86,6 @@ export function DiffControls() {
|
||||
lastSaved: rawState.lastSaved || Date.now(),
|
||||
isDeployed: rawState.isDeployed || false,
|
||||
deploymentStatuses: rawState.deploymentStatuses || {},
|
||||
hasActiveWebhook: rawState.hasActiveWebhook || false,
|
||||
// Only include deployedAt if it's a valid date, never include null/undefined
|
||||
...(rawState.deployedAt && rawState.deployedAt instanceof Date
|
||||
? { deployedAt: rawState.deployedAt }
|
||||
|
||||
@@ -19,7 +19,6 @@ export interface CurrentWorkflow {
|
||||
deployedAt?: Date
|
||||
deploymentStatuses?: Record<string, DeploymentStatus>
|
||||
needsRedeployment?: boolean
|
||||
hasActiveWebhook?: boolean
|
||||
|
||||
// Mode information
|
||||
isDiffMode: boolean
|
||||
@@ -66,7 +65,6 @@ export function useCurrentWorkflow(): CurrentWorkflow {
|
||||
deployedAt: activeWorkflow.deployedAt,
|
||||
deploymentStatuses: activeWorkflow.deploymentStatuses,
|
||||
needsRedeployment: activeWorkflow.needsRedeployment,
|
||||
hasActiveWebhook: activeWorkflow.hasActiveWebhook,
|
||||
|
||||
// Mode information - update to reflect ready state
|
||||
isDiffMode: shouldUseDiff,
|
||||
|
||||
@@ -204,16 +204,19 @@ export async function applyAutoLayoutAndUpdateStore(
|
||||
useWorkflowStore.getState().updateLastSaved()
|
||||
|
||||
// Clean up the workflow state for API validation
|
||||
// Destructure out UI-only fields that shouldn't be persisted
|
||||
const { deploymentStatuses, needsRedeployment, dragStartPosition, ...stateToSave } =
|
||||
newWorkflowState
|
||||
|
||||
const cleanedWorkflowState = {
|
||||
...newWorkflowState,
|
||||
...stateToSave,
|
||||
// Convert null dates to undefined (since they're optional)
|
||||
deployedAt: newWorkflowState.deployedAt ? new Date(newWorkflowState.deployedAt) : undefined,
|
||||
deployedAt: stateToSave.deployedAt ? new Date(stateToSave.deployedAt) : undefined,
|
||||
// Ensure other optional fields are properly handled
|
||||
loops: newWorkflowState.loops || {},
|
||||
parallels: newWorkflowState.parallels || {},
|
||||
deploymentStatuses: newWorkflowState.deploymentStatuses || {},
|
||||
loops: stateToSave.loops || {},
|
||||
parallels: stateToSave.parallels || {},
|
||||
// Sanitize edges: remove null/empty handle fields to satisfy schema (optional strings)
|
||||
edges: (newWorkflowState.edges || []).map((edge: any) => {
|
||||
edges: (stateToSave.edges || []).map((edge: any) => {
|
||||
const { sourceHandle, targetHandle, ...rest } = edge || {}
|
||||
const sanitized: any = { ...rest }
|
||||
if (typeof sourceHandle === 'string' && sourceHandle.length > 0) {
|
||||
|
||||
@@ -382,7 +382,6 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
|
||||
isDeployed: workflowState.isDeployed ?? false,
|
||||
deployedAt: workflowState.deployedAt,
|
||||
deploymentStatuses: workflowState.deploymentStatuses || {},
|
||||
hasActiveWebhook: workflowState.hasActiveWebhook ?? false,
|
||||
})
|
||||
|
||||
// Replace subblock store values for this workflow
|
||||
|
||||
@@ -479,7 +479,6 @@ export function useCollaborativeWorkflow() {
|
||||
isDeployed: workflowData.state.isDeployed || false,
|
||||
deployedAt: workflowData.state.deployedAt,
|
||||
lastSaved: workflowData.state.lastSaved || Date.now(),
|
||||
hasActiveWebhook: workflowData.state.hasActiveWebhook || false,
|
||||
deploymentStatuses: workflowData.state.deploymentStatuses || {},
|
||||
})
|
||||
|
||||
|
||||
@@ -214,7 +214,6 @@ const mockWorkflowState: WorkflowState = {
|
||||
lastSaved: Date.now(),
|
||||
isDeployed: false,
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
}
|
||||
|
||||
describe('Database Helpers', () => {
|
||||
@@ -452,11 +451,6 @@ describe('Database Helpers', () => {
|
||||
)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.jsonBlob).toBeDefined()
|
||||
expect(result.jsonBlob.blocks).toEqual(mockWorkflowState.blocks)
|
||||
expect(result.jsonBlob.edges).toEqual(mockWorkflowState.edges)
|
||||
expect(result.jsonBlob.loops).toEqual(mockWorkflowState.loops)
|
||||
expect(result.jsonBlob.parallels).toEqual(mockWorkflowState.parallels)
|
||||
|
||||
// Verify transaction was called
|
||||
expect(mockTransaction).toHaveBeenCalledTimes(1)
|
||||
@@ -471,7 +465,6 @@ describe('Database Helpers', () => {
|
||||
lastSaved: Date.now(),
|
||||
isDeployed: false,
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
}
|
||||
|
||||
const mockTransaction = vi.fn().mockImplementation(async (callback) => {
|
||||
@@ -494,10 +487,6 @@ describe('Database Helpers', () => {
|
||||
)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.jsonBlob.blocks).toEqual({})
|
||||
expect(result.jsonBlob.edges).toEqual([])
|
||||
expect(result.jsonBlob.loops).toEqual({})
|
||||
expect(result.jsonBlob.parallels).toEqual({})
|
||||
})
|
||||
|
||||
it('should return error when transaction fails', async () => {
|
||||
@@ -650,7 +639,6 @@ describe('Database Helpers', () => {
|
||||
lastSaved: Date.now(),
|
||||
isDeployed: false,
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
}
|
||||
|
||||
it('should successfully migrate workflow from JSON to normalized tables', async () => {
|
||||
@@ -737,7 +725,6 @@ describe('Database Helpers', () => {
|
||||
lastSaved: Date.now(),
|
||||
isDeployed: false,
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
}
|
||||
|
||||
// Create 1000 blocks
|
||||
@@ -782,8 +769,6 @@ describe('Database Helpers', () => {
|
||||
)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(Object.keys(result.jsonBlob.blocks)).toHaveLength(1000)
|
||||
expect(result.jsonBlob.edges).toHaveLength(999)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1020,7 +1005,6 @@ describe('Database Helpers', () => {
|
||||
loops: {},
|
||||
parallels: {},
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
}
|
||||
|
||||
// Mock the transaction for save operation
|
||||
@@ -1058,10 +1042,6 @@ describe('Database Helpers', () => {
|
||||
)
|
||||
expect(saveResult.success).toBe(true)
|
||||
|
||||
// Step 6: Verify the JSON blob also preserves advancedMode
|
||||
expect(saveResult.jsonBlob?.blocks['agent-original'].advancedMode).toBe(true)
|
||||
expect(saveResult.jsonBlob?.blocks['agent-duplicate'].advancedMode).toBe(true)
|
||||
|
||||
// Verify the database insert was called with the correct values
|
||||
expect(mockTransaction).toHaveBeenCalled()
|
||||
})
|
||||
@@ -1161,7 +1141,6 @@ describe('Database Helpers', () => {
|
||||
loops: {},
|
||||
parallels: {},
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
}
|
||||
|
||||
// Mock successful save
|
||||
|
||||
@@ -216,12 +216,11 @@ export async function loadWorkflowFromNormalizedTables(
|
||||
|
||||
/**
|
||||
* Save workflow state to normalized tables
|
||||
* Also returns the JSON blob for backward compatibility
|
||||
*/
|
||||
export async function saveWorkflowToNormalizedTables(
|
||||
workflowId: string,
|
||||
state: WorkflowState
|
||||
): Promise<{ success: boolean; jsonBlob?: any; error?: string }> {
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// Start a transaction
|
||||
await db.transaction(async (tx) => {
|
||||
@@ -297,27 +296,9 @@ export async function saveWorkflowToNormalizedTables(
|
||||
if (subflowInserts.length > 0) {
|
||||
await tx.insert(workflowSubflows).values(subflowInserts)
|
||||
}
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
// Create JSON blob for backward compatibility
|
||||
const jsonBlob = {
|
||||
blocks: state.blocks,
|
||||
edges: state.edges,
|
||||
loops: state.loops || {},
|
||||
parallels: state.parallels || {},
|
||||
lastSaved: Date.now(),
|
||||
isDeployed: state.isDeployed,
|
||||
deployedAt: state.deployedAt,
|
||||
deploymentStatuses: state.deploymentStatuses,
|
||||
hasActiveWebhook: state.hasActiveWebhook,
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
jsonBlob,
|
||||
}
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
logger.error(`Error saving workflow ${workflowId} to normalized tables:`, error)
|
||||
return {
|
||||
@@ -354,6 +335,7 @@ export async function migrateWorkflowToNormalizedTables(
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// Convert JSON state to WorkflowState format
|
||||
// Only include fields that are actually persisted to normalized tables
|
||||
const workflowState: WorkflowState = {
|
||||
blocks: jsonState.blocks || {},
|
||||
edges: jsonState.edges || [],
|
||||
@@ -362,16 +344,9 @@ export async function migrateWorkflowToNormalizedTables(
|
||||
lastSaved: jsonState.lastSaved,
|
||||
isDeployed: jsonState.isDeployed,
|
||||
deployedAt: jsonState.deployedAt,
|
||||
deploymentStatuses: jsonState.deploymentStatuses || {},
|
||||
hasActiveWebhook: jsonState.hasActiveWebhook,
|
||||
}
|
||||
|
||||
const result = await saveWorkflowToNormalizedTables(workflowId, workflowState)
|
||||
|
||||
if (result.success) {
|
||||
return { success: true }
|
||||
}
|
||||
return { success: false, error: result.error }
|
||||
return await saveWorkflowToNormalizedTables(workflowId, workflowState)
|
||||
} catch (error) {
|
||||
logger.error(`Error migrating workflow ${workflowId} to normalized tables:`, error)
|
||||
return {
|
||||
|
||||
@@ -68,7 +68,6 @@ export function useWorkflowDiff(): UseWorkflowDiffReturn {
|
||||
isDeployed: currentState.isDeployed,
|
||||
deployedAt: currentState.deployedAt,
|
||||
deploymentStatuses: { ...currentState.deploymentStatuses },
|
||||
hasActiveWebhook: currentState.hasActiveWebhook,
|
||||
},
|
||||
subblockValues: JSON.parse(JSON.stringify(currentSubblockValues)), // Deep copy
|
||||
timestamp: Date.now(),
|
||||
@@ -107,7 +106,6 @@ export function useWorkflowDiff(): UseWorkflowDiffReturn {
|
||||
isDeployed: backup.workflowState.isDeployed,
|
||||
deployedAt: backup.workflowState.deployedAt,
|
||||
deploymentStatuses: backup.workflowState.deploymentStatuses,
|
||||
hasActiveWebhook: backup.workflowState.hasActiveWebhook,
|
||||
})
|
||||
|
||||
// Restore subblock values
|
||||
|
||||
@@ -365,6 +365,5 @@ export function mergeWithUIState(
|
||||
isDeployed: fullState.isDeployed,
|
||||
deployedAt: fullState.deployedAt,
|
||||
deploymentStatuses: fullState.deploymentStatuses,
|
||||
hasActiveWebhook: fullState.hasActiveWebhook,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,6 @@ export async function getWorkflowState(workflowId: string) {
|
||||
const finalState = {
|
||||
// Default values for expected properties
|
||||
deploymentStatuses: {},
|
||||
hasActiveWebhook: false,
|
||||
// Data from normalized tables
|
||||
blocks: normalizedData.blocks,
|
||||
edges: normalizedData.edges,
|
||||
|
||||
@@ -1953,7 +1953,6 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
isDeployed: !!reverted.isDeployed,
|
||||
...(reverted.deployedAt ? { deployedAt: new Date(reverted.deployedAt) } : {}),
|
||||
deploymentStatuses: reverted.deploymentStatuses || {},
|
||||
hasActiveWebhook: !!reverted.hasActiveWebhook,
|
||||
})
|
||||
|
||||
// Extract and apply subblock values
|
||||
|
||||
@@ -38,7 +38,6 @@ const initialState = {
|
||||
// New field for per-workflow deployment tracking
|
||||
deploymentStatuses: {},
|
||||
needsRedeployment: false,
|
||||
hasActiveWebhook: false,
|
||||
history: {
|
||||
past: [],
|
||||
present: {
|
||||
@@ -475,7 +474,6 @@ export const useWorkflowStore = create<WorkflowStoreWithHistory>()(
|
||||
lastSaved: Date.now(),
|
||||
isDeployed: false,
|
||||
isPublished: false,
|
||||
hasActiveWebhook: false,
|
||||
}
|
||||
set(newState)
|
||||
// Note: Socket.IO handles real-time sync automatically
|
||||
@@ -500,7 +498,6 @@ export const useWorkflowStore = create<WorkflowStoreWithHistory>()(
|
||||
deployedAt: state.deployedAt,
|
||||
deploymentStatuses: state.deploymentStatuses,
|
||||
needsRedeployment: state.needsRedeployment,
|
||||
hasActiveWebhook: state.hasActiveWebhook,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -902,15 +899,6 @@ export const useWorkflowStore = create<WorkflowStoreWithHistory>()(
|
||||
}))
|
||||
},
|
||||
|
||||
setWebhookStatus: (hasActiveWebhook: boolean) => {
|
||||
// Only update if the status has changed to avoid unnecessary rerenders
|
||||
if (get().hasActiveWebhook !== hasActiveWebhook) {
|
||||
set({ hasActiveWebhook })
|
||||
get().updateLastSaved()
|
||||
// Note: Socket.IO handles real-time sync automatically
|
||||
}
|
||||
},
|
||||
|
||||
revertToDeployedState: async (deployedState: WorkflowState) => {
|
||||
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
|
||||
|
||||
@@ -931,7 +919,6 @@ export const useWorkflowStore = create<WorkflowStoreWithHistory>()(
|
||||
parallels: deployedState.parallels || {},
|
||||
isDeployed: true,
|
||||
needsRedeployment: false,
|
||||
hasActiveWebhook: false, // Reset webhook status
|
||||
// Keep existing deployment statuses and update for the active workflow if needed
|
||||
deploymentStatuses: {
|
||||
...get().deploymentStatuses,
|
||||
@@ -966,14 +953,6 @@ export const useWorkflowStore = create<WorkflowStoreWithHistory>()(
|
||||
},
|
||||
})
|
||||
|
||||
// Check if there's an active webhook in the deployed state
|
||||
const starterBlock = Object.values(deployedState.blocks).find(
|
||||
(block) => block.type === 'starter'
|
||||
)
|
||||
if (starterBlock && starterBlock.subBlocks?.startWorkflow?.value === 'webhook') {
|
||||
set({ hasActiveWebhook: true })
|
||||
}
|
||||
|
||||
pushHistory(set, get, newState, 'Reverted to deployed state')
|
||||
get().updateLastSaved()
|
||||
|
||||
|
||||
@@ -154,7 +154,6 @@ export interface WorkflowState {
|
||||
// New field for per-workflow deployment status
|
||||
deploymentStatuses?: Record<string, DeploymentStatus>
|
||||
needsRedeployment?: boolean
|
||||
hasActiveWebhook?: boolean
|
||||
// Drag state for undo/redo
|
||||
dragStartPosition?: DragStartPosition | null
|
||||
}
|
||||
@@ -214,7 +213,6 @@ export interface WorkflowActions {
|
||||
generateLoopBlocks: () => Record<string, Loop>
|
||||
generateParallelBlocks: () => Record<string, Parallel>
|
||||
setNeedsRedeploymentFlag: (needsRedeployment: boolean) => void
|
||||
setWebhookStatus: (hasActiveWebhook: boolean) => void
|
||||
revertToDeployedState: (deployedState: WorkflowState) => void
|
||||
toggleBlockAdvancedMode: (id: string) => void
|
||||
toggleBlockTriggerMode: (id: string) => void
|
||||
|
||||
Reference in New Issue
Block a user