mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-03 19:24:57 -05:00
654 lines
21 KiB
TypeScript
654 lines
21 KiB
TypeScript
import { useEffect } from 'react'
|
|
import { createLogger } from '@sim/logger'
|
|
import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
import { getNextWorkflowColor } from '@/lib/workflows/colors'
|
|
import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults'
|
|
import { deploymentKeys } from '@/hooks/queries/deployments'
|
|
import {
|
|
createOptimisticMutationHandlers,
|
|
generateTempId,
|
|
} from '@/hooks/queries/utils/optimistic-mutation'
|
|
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
|
import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
|
|
import { generateCreativeWorkflowName } from '@/stores/workflows/registry/utils'
|
|
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
|
import type { WorkflowState } from '@/stores/workflows/workflow/types'
|
|
|
|
const logger = createLogger('WorkflowQueries')
|
|
|
|
export const workflowKeys = {
|
|
all: ['workflows'] as const,
|
|
lists: () => [...workflowKeys.all, 'list'] as const,
|
|
list: (workspaceId: string | undefined) => [...workflowKeys.lists(), workspaceId ?? ''] as const,
|
|
deploymentStatus: (workflowId: string | undefined) =>
|
|
[...workflowKeys.all, 'deploymentStatus', workflowId ?? ''] as const,
|
|
deploymentVersions: () => [...workflowKeys.all, 'deploymentVersion'] as const,
|
|
deploymentVersion: (workflowId: string | undefined, version: number | undefined) =>
|
|
[...workflowKeys.deploymentVersions(), workflowId ?? '', version ?? 0] as const,
|
|
state: (workflowId: string | undefined) =>
|
|
[...workflowKeys.all, 'state', workflowId ?? ''] as const,
|
|
}
|
|
|
|
/**
|
|
* Fetches workflow state from the API.
|
|
* Used as the base query for both state preview and input fields extraction.
|
|
*/
|
|
async function fetchWorkflowState(workflowId: string): Promise<WorkflowState | null> {
|
|
const response = await fetch(`/api/workflows/${workflowId}`)
|
|
if (!response.ok) throw new Error('Failed to fetch workflow')
|
|
const { data } = await response.json()
|
|
return data?.state ?? null
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch workflow state.
|
|
* Used by workflow blocks to show a preview of the child workflow
|
|
* and as a base query for input fields extraction.
|
|
*
|
|
* @param workflowId - The workflow ID to fetch state for
|
|
* @returns Query result with workflow state
|
|
*/
|
|
export function useWorkflowState(workflowId: string | undefined) {
|
|
return useQuery({
|
|
queryKey: workflowKeys.state(workflowId),
|
|
queryFn: () => fetchWorkflowState(workflowId!),
|
|
enabled: Boolean(workflowId),
|
|
staleTime: 30 * 1000, // 30 seconds
|
|
})
|
|
}
|
|
|
|
function mapWorkflow(workflow: any): WorkflowMetadata {
|
|
return {
|
|
id: workflow.id,
|
|
name: workflow.name,
|
|
description: workflow.description,
|
|
color: workflow.color,
|
|
workspaceId: workflow.workspaceId,
|
|
folderId: workflow.folderId,
|
|
sortOrder: workflow.sortOrder ?? 0,
|
|
createdAt: new Date(workflow.createdAt),
|
|
lastModified: new Date(workflow.updatedAt || workflow.createdAt),
|
|
}
|
|
}
|
|
|
|
async function fetchWorkflows(workspaceId: string): Promise<WorkflowMetadata[]> {
|
|
const response = await fetch(`/api/workflows?workspaceId=${workspaceId}`)
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch workflows')
|
|
}
|
|
|
|
const { data }: { data: any[] } = await response.json()
|
|
return data.map(mapWorkflow)
|
|
}
|
|
|
|
export function useWorkflows(workspaceId?: string, options?: { syncRegistry?: boolean }) {
|
|
const { syncRegistry = true } = options || {}
|
|
const beginMetadataLoad = useWorkflowRegistry((state) => state.beginMetadataLoad)
|
|
const completeMetadataLoad = useWorkflowRegistry((state) => state.completeMetadataLoad)
|
|
const failMetadataLoad = useWorkflowRegistry((state) => state.failMetadataLoad)
|
|
|
|
const query = useQuery({
|
|
queryKey: workflowKeys.list(workspaceId),
|
|
queryFn: () => fetchWorkflows(workspaceId as string),
|
|
enabled: Boolean(workspaceId),
|
|
placeholderData: keepPreviousData,
|
|
staleTime: 60 * 1000,
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (syncRegistry && workspaceId && query.status === 'pending') {
|
|
beginMetadataLoad(workspaceId)
|
|
}
|
|
}, [syncRegistry, workspaceId, query.status, beginMetadataLoad])
|
|
|
|
useEffect(() => {
|
|
if (syncRegistry && workspaceId && query.status === 'success' && query.data) {
|
|
completeMetadataLoad(workspaceId, query.data)
|
|
}
|
|
}, [syncRegistry, workspaceId, query.status, query.data, completeMetadataLoad])
|
|
|
|
useEffect(() => {
|
|
if (syncRegistry && workspaceId && query.status === 'error') {
|
|
const message =
|
|
query.error instanceof Error ? query.error.message : 'Failed to fetch workflows'
|
|
failMetadataLoad(workspaceId, message)
|
|
}
|
|
}, [syncRegistry, workspaceId, query.status, query.error, failMetadataLoad])
|
|
|
|
return query
|
|
}
|
|
|
|
interface CreateWorkflowVariables {
|
|
workspaceId: string
|
|
name?: string
|
|
description?: string
|
|
color?: string
|
|
folderId?: string | null
|
|
sortOrder?: number
|
|
}
|
|
|
|
interface CreateWorkflowResult {
|
|
id: string
|
|
name: string
|
|
description?: string
|
|
color: string
|
|
workspaceId: string
|
|
folderId?: string | null
|
|
sortOrder: number
|
|
}
|
|
|
|
interface DuplicateWorkflowVariables {
|
|
workspaceId: string
|
|
sourceId: string
|
|
name: string
|
|
description?: string
|
|
color: string
|
|
folderId?: string | null
|
|
}
|
|
|
|
interface DuplicateWorkflowResult {
|
|
id: string
|
|
name: string
|
|
description?: string
|
|
color: string
|
|
workspaceId: string
|
|
folderId?: string | null
|
|
sortOrder: number
|
|
blocksCount: number
|
|
edgesCount: number
|
|
subflowsCount: number
|
|
}
|
|
|
|
/**
|
|
* Creates optimistic mutation handlers for workflow operations
|
|
*/
|
|
function createWorkflowMutationHandlers<TVariables extends { workspaceId: string }>(
|
|
queryClient: ReturnType<typeof useQueryClient>,
|
|
name: string,
|
|
createOptimisticWorkflow: (variables: TVariables, tempId: string) => WorkflowMetadata
|
|
) {
|
|
return createOptimisticMutationHandlers<
|
|
CreateWorkflowResult | DuplicateWorkflowResult,
|
|
TVariables,
|
|
WorkflowMetadata
|
|
>(queryClient, {
|
|
name,
|
|
getQueryKey: (variables) => workflowKeys.list(variables.workspaceId),
|
|
getSnapshot: () => ({ ...useWorkflowRegistry.getState().workflows }),
|
|
generateTempId: () => generateTempId('temp-workflow'),
|
|
createOptimisticItem: createOptimisticWorkflow,
|
|
applyOptimisticUpdate: (tempId, item) => {
|
|
useWorkflowRegistry.setState((state) => ({
|
|
workflows: { ...state.workflows, [tempId]: item },
|
|
}))
|
|
},
|
|
replaceOptimisticEntry: (tempId, data) => {
|
|
useWorkflowRegistry.setState((state) => {
|
|
const { [tempId]: _, ...remainingWorkflows } = state.workflows
|
|
return {
|
|
workflows: {
|
|
...remainingWorkflows,
|
|
[data.id]: {
|
|
id: data.id,
|
|
name: data.name,
|
|
lastModified: new Date(),
|
|
createdAt: new Date(),
|
|
description: data.description,
|
|
color: data.color,
|
|
workspaceId: data.workspaceId,
|
|
folderId: data.folderId,
|
|
sortOrder: 'sortOrder' in data ? data.sortOrder : 0,
|
|
},
|
|
},
|
|
error: null,
|
|
}
|
|
})
|
|
},
|
|
rollback: (snapshot) => {
|
|
useWorkflowRegistry.setState({ workflows: snapshot })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useCreateWorkflow() {
|
|
const queryClient = useQueryClient()
|
|
|
|
const handlers = createWorkflowMutationHandlers<CreateWorkflowVariables>(
|
|
queryClient,
|
|
'CreateWorkflow',
|
|
(variables, tempId) => {
|
|
let sortOrder: number
|
|
if (variables.sortOrder !== undefined) {
|
|
sortOrder = variables.sortOrder
|
|
} else {
|
|
const currentWorkflows = useWorkflowRegistry.getState().workflows
|
|
const targetFolderId = variables.folderId || null
|
|
const workflowsInFolder = Object.values(currentWorkflows).filter(
|
|
(w) => w.folderId === targetFolderId
|
|
)
|
|
sortOrder = workflowsInFolder.reduce((min, w) => Math.min(min, w.sortOrder ?? 0), 1) - 1
|
|
}
|
|
|
|
return {
|
|
id: tempId,
|
|
name: variables.name || generateCreativeWorkflowName(),
|
|
lastModified: new Date(),
|
|
createdAt: new Date(),
|
|
description: variables.description || 'New workflow',
|
|
color: variables.color || getNextWorkflowColor(),
|
|
workspaceId: variables.workspaceId,
|
|
folderId: variables.folderId || null,
|
|
sortOrder,
|
|
}
|
|
}
|
|
)
|
|
|
|
return useMutation({
|
|
mutationFn: async (variables: CreateWorkflowVariables): Promise<CreateWorkflowResult> => {
|
|
const { workspaceId, name, description, color, folderId, sortOrder } = variables
|
|
|
|
logger.info(`Creating new workflow in workspace: ${workspaceId}`)
|
|
|
|
const createResponse = await fetch('/api/workflows', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
name: name || generateCreativeWorkflowName(),
|
|
description: description || 'New workflow',
|
|
color: color || getNextWorkflowColor(),
|
|
workspaceId,
|
|
folderId: folderId || null,
|
|
sortOrder,
|
|
}),
|
|
})
|
|
|
|
if (!createResponse.ok) {
|
|
const errorData = await createResponse.json()
|
|
throw new Error(
|
|
`Failed to create workflow: ${errorData.error || createResponse.statusText}`
|
|
)
|
|
}
|
|
|
|
const createdWorkflow = await createResponse.json()
|
|
const workflowId = createdWorkflow.id
|
|
|
|
logger.info(`Successfully created workflow ${workflowId}`)
|
|
|
|
const { workflowState } = buildDefaultWorkflowArtifacts()
|
|
|
|
const stateResponse = await fetch(`/api/workflows/${workflowId}/state`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(workflowState),
|
|
})
|
|
|
|
if (!stateResponse.ok) {
|
|
const text = await stateResponse.text()
|
|
logger.error('Failed to persist default Start block:', text)
|
|
} else {
|
|
logger.info('Successfully persisted default Start block')
|
|
}
|
|
|
|
return {
|
|
id: workflowId,
|
|
name: createdWorkflow.name,
|
|
description: createdWorkflow.description,
|
|
color: createdWorkflow.color,
|
|
workspaceId,
|
|
folderId: createdWorkflow.folderId,
|
|
sortOrder: createdWorkflow.sortOrder ?? 0,
|
|
}
|
|
},
|
|
...handlers,
|
|
onSuccess: (data, variables, context) => {
|
|
handlers.onSuccess(data, variables, context)
|
|
|
|
const { subBlockValues } = buildDefaultWorkflowArtifacts()
|
|
useSubBlockStore.setState((state) => ({
|
|
workflowValues: {
|
|
...state.workflowValues,
|
|
[data.id]: subBlockValues,
|
|
},
|
|
}))
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useDuplicateWorkflowMutation() {
|
|
const queryClient = useQueryClient()
|
|
|
|
const handlers = createWorkflowMutationHandlers<DuplicateWorkflowVariables>(
|
|
queryClient,
|
|
'DuplicateWorkflow',
|
|
(variables, tempId) => {
|
|
const currentWorkflows = useWorkflowRegistry.getState().workflows
|
|
const targetFolderId = variables.folderId || null
|
|
const workflowsInFolder = Object.values(currentWorkflows).filter(
|
|
(w) => w.folderId === targetFolderId
|
|
)
|
|
const minSortOrder = workflowsInFolder.reduce((min, w) => Math.min(min, w.sortOrder ?? 0), 1)
|
|
|
|
return {
|
|
id: tempId,
|
|
name: variables.name,
|
|
lastModified: new Date(),
|
|
createdAt: new Date(),
|
|
description: variables.description,
|
|
color: variables.color,
|
|
workspaceId: variables.workspaceId,
|
|
folderId: targetFolderId,
|
|
sortOrder: minSortOrder - 1,
|
|
}
|
|
}
|
|
)
|
|
|
|
return useMutation({
|
|
mutationFn: async (variables: DuplicateWorkflowVariables): Promise<DuplicateWorkflowResult> => {
|
|
const { workspaceId, sourceId, name, description, color, folderId } = variables
|
|
|
|
logger.info(`Duplicating workflow ${sourceId} in workspace: ${workspaceId}`)
|
|
|
|
const response = await fetch(`/api/workflows/${sourceId}/duplicate`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
name,
|
|
description,
|
|
color,
|
|
workspaceId,
|
|
folderId: folderId ?? null,
|
|
}),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}))
|
|
throw new Error(`Failed to duplicate workflow: ${errorData.error || response.statusText}`)
|
|
}
|
|
|
|
const duplicatedWorkflow = await response.json()
|
|
|
|
logger.info(`Successfully duplicated workflow ${sourceId} to ${duplicatedWorkflow.id}`, {
|
|
blocksCount: duplicatedWorkflow.blocksCount,
|
|
edgesCount: duplicatedWorkflow.edgesCount,
|
|
subflowsCount: duplicatedWorkflow.subflowsCount,
|
|
})
|
|
|
|
return {
|
|
id: duplicatedWorkflow.id,
|
|
name: duplicatedWorkflow.name || name,
|
|
description: duplicatedWorkflow.description || description,
|
|
color: duplicatedWorkflow.color || color,
|
|
workspaceId,
|
|
folderId: duplicatedWorkflow.folderId ?? folderId,
|
|
sortOrder: duplicatedWorkflow.sortOrder ?? 0,
|
|
blocksCount: duplicatedWorkflow.blocksCount || 0,
|
|
edgesCount: duplicatedWorkflow.edgesCount || 0,
|
|
subflowsCount: duplicatedWorkflow.subflowsCount || 0,
|
|
}
|
|
},
|
|
...handlers,
|
|
onSuccess: (data, variables, context) => {
|
|
handlers.onSuccess(data, variables, context)
|
|
|
|
// Copy subblock values from source if it's the active workflow
|
|
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
|
|
if (variables.sourceId === activeWorkflowId) {
|
|
const sourceSubblockValues =
|
|
useSubBlockStore.getState().workflowValues[variables.sourceId] || {}
|
|
useSubBlockStore.setState((state) => ({
|
|
workflowValues: {
|
|
...state.workflowValues,
|
|
[data.id]: { ...sourceSubblockValues },
|
|
},
|
|
}))
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
interface DeploymentVersionStateResponse {
|
|
deployedState: WorkflowState
|
|
}
|
|
|
|
async function fetchDeploymentVersionState(
|
|
workflowId: string,
|
|
version: number
|
|
): Promise<WorkflowState> {
|
|
const response = await fetch(`/api/workflows/${workflowId}/deployments/${version}`)
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch deployment version: ${response.statusText}`)
|
|
}
|
|
|
|
const data: DeploymentVersionStateResponse = await response.json()
|
|
if (!data.deployedState) {
|
|
throw new Error('No deployed state returned')
|
|
}
|
|
|
|
return data.deployedState
|
|
}
|
|
|
|
/**
|
|
* Hook for fetching the workflow state of a specific deployment version.
|
|
* Used in the deploy modal to preview historical versions.
|
|
*/
|
|
export function useDeploymentVersionState(workflowId: string | null, version: number | null) {
|
|
return useQuery({
|
|
queryKey: workflowKeys.deploymentVersion(workflowId ?? undefined, version ?? undefined),
|
|
queryFn: () => fetchDeploymentVersionState(workflowId as string, version as number),
|
|
enabled: Boolean(workflowId) && version !== null,
|
|
staleTime: 5 * 60 * 1000, // 5 minutes - deployment versions don't change
|
|
})
|
|
}
|
|
|
|
interface RevertToVersionVariables {
|
|
workflowId: string
|
|
version: number
|
|
}
|
|
|
|
/**
|
|
* Mutation hook for reverting (loading) a deployment version into the current workflow.
|
|
*/
|
|
export function useRevertToVersion() {
|
|
return useMutation({
|
|
mutationFn: async ({ workflowId, version }: RevertToVersionVariables): Promise<void> => {
|
|
const response = await fetch(`/api/workflows/${workflowId}/deployments/${version}/revert`, {
|
|
method: 'POST',
|
|
})
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to load deployment')
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
interface ReorderWorkflowsVariables {
|
|
workspaceId: string
|
|
updates: Array<{
|
|
id: string
|
|
sortOrder: number
|
|
folderId?: string | null
|
|
}>
|
|
}
|
|
|
|
export function useReorderWorkflows() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async (variables: ReorderWorkflowsVariables): Promise<void> => {
|
|
const response = await fetch('/api/workflows/reorder', {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(variables),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({}))
|
|
throw new Error(error.error || 'Failed to reorder workflows')
|
|
}
|
|
},
|
|
onMutate: async (variables) => {
|
|
await queryClient.cancelQueries({ queryKey: workflowKeys.list(variables.workspaceId) })
|
|
|
|
const snapshot = { ...useWorkflowRegistry.getState().workflows }
|
|
|
|
useWorkflowRegistry.setState((state) => {
|
|
const updated = { ...state.workflows }
|
|
for (const update of variables.updates) {
|
|
if (updated[update.id]) {
|
|
updated[update.id] = {
|
|
...updated[update.id],
|
|
sortOrder: update.sortOrder,
|
|
folderId:
|
|
update.folderId !== undefined ? update.folderId : updated[update.id].folderId,
|
|
}
|
|
}
|
|
}
|
|
return { workflows: updated }
|
|
})
|
|
|
|
return { snapshot }
|
|
},
|
|
onError: (_error, _variables, context) => {
|
|
if (context?.snapshot) {
|
|
useWorkflowRegistry.setState({ workflows: context.snapshot })
|
|
}
|
|
},
|
|
onSettled: (_data, _error, variables) => {
|
|
queryClient.invalidateQueries({ queryKey: workflowKeys.list(variables.workspaceId) })
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Child deployment status data returned from the API
|
|
*/
|
|
export interface ChildDeploymentStatus {
|
|
activeVersion: number | null
|
|
isDeployed: boolean
|
|
needsRedeploy: boolean
|
|
}
|
|
|
|
/**
|
|
* Fetches deployment status for a child workflow.
|
|
* Uses Promise.all to fetch status and deployments in parallel for better performance.
|
|
*/
|
|
async function fetchChildDeploymentStatus(workflowId: string): Promise<ChildDeploymentStatus> {
|
|
const fetchOptions = {
|
|
cache: 'no-store' as const,
|
|
headers: { 'Cache-Control': 'no-cache' },
|
|
}
|
|
|
|
const [statusRes, deploymentsRes] = await Promise.all([
|
|
fetch(`/api/workflows/${workflowId}/status`, fetchOptions),
|
|
fetch(`/api/workflows/${workflowId}/deployments`, fetchOptions),
|
|
])
|
|
|
|
if (!statusRes.ok) {
|
|
throw new Error('Failed to fetch workflow status')
|
|
}
|
|
|
|
const statusData = await statusRes.json()
|
|
|
|
let activeVersion: number | null = null
|
|
if (deploymentsRes.ok) {
|
|
const deploymentsJson = await deploymentsRes.json()
|
|
const versions = Array.isArray(deploymentsJson?.data?.versions)
|
|
? deploymentsJson.data.versions
|
|
: Array.isArray(deploymentsJson?.versions)
|
|
? deploymentsJson.versions
|
|
: []
|
|
|
|
const active = versions.find((v: { isActive?: boolean }) => v.isActive)
|
|
activeVersion = active ? Number(active.version) : null
|
|
}
|
|
|
|
return {
|
|
activeVersion,
|
|
isDeployed: statusData.isDeployed || false,
|
|
needsRedeploy: statusData.needsRedeployment || false,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch deployment status for a child workflow.
|
|
* Used by workflow selector blocks to show deployment badges.
|
|
*/
|
|
export function useChildDeploymentStatus(workflowId: string | undefined) {
|
|
return useQuery({
|
|
queryKey: workflowKeys.deploymentStatus(workflowId),
|
|
queryFn: () => fetchChildDeploymentStatus(workflowId!),
|
|
enabled: Boolean(workflowId),
|
|
staleTime: 0,
|
|
retry: false,
|
|
})
|
|
}
|
|
|
|
interface DeployChildWorkflowVariables {
|
|
workflowId: string
|
|
}
|
|
|
|
interface DeployChildWorkflowResult {
|
|
isDeployed: boolean
|
|
deployedAt?: Date
|
|
apiKey?: string
|
|
}
|
|
|
|
/**
|
|
* Mutation hook for deploying a child workflow.
|
|
* Invalidates the deployment status query on success.
|
|
*/
|
|
export function useDeployChildWorkflow() {
|
|
const queryClient = useQueryClient()
|
|
const setDeploymentStatus = useWorkflowRegistry((state) => state.setDeploymentStatus)
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
workflowId,
|
|
}: DeployChildWorkflowVariables): Promise<DeployChildWorkflowResult> => {
|
|
const response = await fetch(`/api/workflows/${workflowId}/deploy`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
deployChatEnabled: false,
|
|
}),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}))
|
|
throw new Error(errorData.error || 'Failed to deploy workflow')
|
|
}
|
|
|
|
const responseData = await response.json()
|
|
return {
|
|
isDeployed: responseData.isDeployed ?? false,
|
|
deployedAt: responseData.deployedAt ? new Date(responseData.deployedAt) : undefined,
|
|
apiKey: responseData.apiKey || '',
|
|
}
|
|
},
|
|
onSuccess: (data, variables) => {
|
|
logger.info('Child workflow deployed', { workflowId: variables.workflowId })
|
|
|
|
setDeploymentStatus(variables.workflowId, data.isDeployed, data.deployedAt, data.apiKey || '')
|
|
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowKeys.deploymentStatus(variables.workflowId),
|
|
})
|
|
// Also invalidate deployment queries
|
|
queryClient.invalidateQueries({
|
|
queryKey: deploymentKeys.info(variables.workflowId),
|
|
})
|
|
queryClient.invalidateQueries({
|
|
queryKey: deploymentKeys.versions(variables.workflowId),
|
|
})
|
|
},
|
|
onError: (error) => {
|
|
logger.error('Failed to deploy child workflow', { error })
|
|
},
|
|
})
|
|
}
|