Skip to content

Commit 12100e6

Browse files
improvement(webhooks): remove dead code (#2965)
* fix(webhooks): subscription recreation path * improvement(webhooks): remove dead code * fix tests * address bugbot comments * fix restoration edge case * fix more edge cases * address bugbot comments * fix gmail polling * add warnings for UI indication for credential sets
1 parent 2329468 commit 12100e6

File tree

24 files changed

+1108
-819
lines changed

24 files changed

+1108
-819
lines changed

apps/sim/app/api/v1/admin/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ export interface AdminDeployResult {
640640
isDeployed: boolean
641641
version: number
642642
deployedAt: string
643+
warnings?: string[]
643644
}
644645

645646
export interface AdminUndeployResult {

apps/sim/app/api/v1/admin/workflows/[id]/deploy/route.ts

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
import { db, workflow } from '@sim/db'
1+
import { db, workflow, workflowDeploymentVersion } from '@sim/db'
22
import { createLogger } from '@sim/logger'
3-
import { eq } from 'drizzle-orm'
3+
import { and, eq } from 'drizzle-orm'
44
import { generateRequestId } from '@/lib/core/utils/request'
5-
import { cleanupWebhooksForWorkflow } from '@/lib/webhooks/deploy'
5+
import { removeMcpToolsForWorkflow, syncMcpToolsForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
6+
import {
7+
cleanupWebhooksForWorkflow,
8+
restorePreviousVersionWebhooks,
9+
saveTriggerWebhooksForDeploy,
10+
} from '@/lib/webhooks/deploy'
611
import {
712
deployWorkflow,
813
loadWorkflowFromNormalizedTables,
914
undeployWorkflow,
1015
} from '@/lib/workflows/persistence/utils'
11-
import { createSchedulesForDeploy, validateWorkflowSchedules } from '@/lib/workflows/schedules'
16+
import {
17+
cleanupDeploymentVersion,
18+
createSchedulesForDeploy,
19+
validateWorkflowSchedules,
20+
} from '@/lib/workflows/schedules'
1221
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
1322
import {
1423
badRequestResponse,
@@ -28,10 +37,11 @@ interface RouteParams {
2837

2938
export const POST = withAdminAuthParams<RouteParams>(async (request, context) => {
3039
const { id: workflowId } = await context.params
40+
const requestId = generateRequestId()
3141

3242
try {
3343
const [workflowRecord] = await db
34-
.select({ id: workflow.id, name: workflow.name })
44+
.select()
3545
.from(workflow)
3646
.where(eq(workflow.id, workflowId))
3747
.limit(1)
@@ -50,6 +60,18 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
5060
return badRequestResponse(`Invalid schedule configuration: ${scheduleValidation.error}`)
5161
}
5262

63+
const [currentActiveVersion] = await db
64+
.select({ id: workflowDeploymentVersion.id })
65+
.from(workflowDeploymentVersion)
66+
.where(
67+
and(
68+
eq(workflowDeploymentVersion.workflowId, workflowId),
69+
eq(workflowDeploymentVersion.isActive, true)
70+
)
71+
)
72+
.limit(1)
73+
const previousVersionId = currentActiveVersion?.id
74+
5375
const deployResult = await deployWorkflow({
5476
workflowId,
5577
deployedBy: ADMIN_ACTOR_ID,
@@ -65,22 +87,91 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
6587
return internalErrorResponse('Failed to resolve deployment version')
6688
}
6789

90+
const workflowData = workflowRecord as Record<string, unknown>
91+
92+
const triggerSaveResult = await saveTriggerWebhooksForDeploy({
93+
request,
94+
workflowId,
95+
workflow: workflowData,
96+
userId: workflowRecord.userId,
97+
blocks: normalizedData.blocks,
98+
requestId,
99+
deploymentVersionId: deployResult.deploymentVersionId,
100+
previousVersionId,
101+
})
102+
103+
if (!triggerSaveResult.success) {
104+
await cleanupDeploymentVersion({
105+
workflowId,
106+
workflow: workflowData,
107+
requestId,
108+
deploymentVersionId: deployResult.deploymentVersionId,
109+
})
110+
await undeployWorkflow({ workflowId })
111+
return internalErrorResponse(
112+
triggerSaveResult.error?.message || 'Failed to sync trigger configuration'
113+
)
114+
}
115+
68116
const scheduleResult = await createSchedulesForDeploy(
69117
workflowId,
70118
normalizedData.blocks,
71119
db,
72120
deployResult.deploymentVersionId
73121
)
74122
if (!scheduleResult.success) {
75-
logger.warn(`Schedule creation failed for workflow ${workflowId}: ${scheduleResult.error}`)
123+
logger.error(
124+
`[${requestId}] Admin API: Schedule creation failed for workflow ${workflowId}: ${scheduleResult.error}`
125+
)
126+
await cleanupDeploymentVersion({
127+
workflowId,
128+
workflow: workflowData,
129+
requestId,
130+
deploymentVersionId: deployResult.deploymentVersionId,
131+
})
132+
if (previousVersionId) {
133+
await restorePreviousVersionWebhooks({
134+
request,
135+
workflow: workflowData,
136+
userId: workflowRecord.userId,
137+
previousVersionId,
138+
requestId,
139+
})
140+
}
141+
await undeployWorkflow({ workflowId })
142+
return internalErrorResponse(scheduleResult.error || 'Failed to create schedule')
76143
}
77144

78-
logger.info(`Admin API: Deployed workflow ${workflowId} as v${deployResult.version}`)
145+
if (previousVersionId && previousVersionId !== deployResult.deploymentVersionId) {
146+
try {
147+
logger.info(`[${requestId}] Admin API: Cleaning up previous version ${previousVersionId}`)
148+
await cleanupDeploymentVersion({
149+
workflowId,
150+
workflow: workflowData,
151+
requestId,
152+
deploymentVersionId: previousVersionId,
153+
skipExternalCleanup: true,
154+
})
155+
} catch (cleanupError) {
156+
logger.error(
157+
`[${requestId}] Admin API: Failed to clean up previous version ${previousVersionId}`,
158+
cleanupError
159+
)
160+
}
161+
}
162+
163+
logger.info(
164+
`[${requestId}] Admin API: Deployed workflow ${workflowId} as v${deployResult.version}`
165+
)
166+
167+
// Sync MCP tools with the latest parameter schema
168+
await syncMcpToolsForWorkflow({ workflowId, requestId, context: 'deploy' })
79169

80170
const response: AdminDeployResult = {
81171
isDeployed: true,
82172
version: deployResult.version!,
83173
deployedAt: deployResult.deployedAt!.toISOString(),
174+
warnings: triggerSaveResult.warnings,
84175
}
85176

86177
return singleResponse(response)
@@ -105,7 +196,6 @@ export const DELETE = withAdminAuthParams<RouteParams>(async (request, context)
105196
return notFoundResponse('Workflow')
106197
}
107198

108-
// Clean up external webhook subscriptions before undeploying
109199
await cleanupWebhooksForWorkflow(
110200
workflowId,
111201
workflowRecord as Record<string, unknown>,
@@ -117,6 +207,8 @@ export const DELETE = withAdminAuthParams<RouteParams>(async (request, context)
117207
return internalErrorResponse(result.error || 'Failed to undeploy workflow')
118208
}
119209

210+
await removeMcpToolsForWorkflow(workflowId, requestId)
211+
120212
logger.info(`Admin API: Undeployed workflow ${workflowId}`)
121213

122214
const response: AdminUndeployResult = {

apps/sim/app/api/v1/admin/workflows/[id]/versions/[versionId]/activate/route.ts

Lines changed: 153 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
import { db, workflow } from '@sim/db'
1+
import { db, workflow, workflowDeploymentVersion } from '@sim/db'
22
import { createLogger } from '@sim/logger'
3-
import { eq } from 'drizzle-orm'
3+
import { and, eq } from 'drizzle-orm'
4+
import { generateRequestId } from '@/lib/core/utils/request'
5+
import { syncMcpToolsForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
6+
import { restorePreviousVersionWebhooks, saveTriggerWebhooksForDeploy } from '@/lib/webhooks/deploy'
47
import { activateWorkflowVersion } from '@/lib/workflows/persistence/utils'
8+
import {
9+
cleanupDeploymentVersion,
10+
createSchedulesForDeploy,
11+
validateWorkflowSchedules,
12+
} from '@/lib/workflows/schedules'
513
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
614
import {
715
badRequestResponse,
816
internalErrorResponse,
917
notFoundResponse,
1018
singleResponse,
1119
} from '@/app/api/v1/admin/responses'
20+
import type { BlockState } from '@/stores/workflows/workflow/types'
1221

1322
const logger = createLogger('AdminWorkflowActivateVersionAPI')
1423

@@ -18,11 +27,12 @@ interface RouteParams {
1827
}
1928

2029
export const POST = withAdminAuthParams<RouteParams>(async (request, context) => {
30+
const requestId = generateRequestId()
2131
const { id: workflowId, versionId } = await context.params
2232

2333
try {
2434
const [workflowRecord] = await db
25-
.select({ id: workflow.id })
35+
.select()
2636
.from(workflow)
2737
.where(eq(workflow.id, workflowId))
2838
.limit(1)
@@ -36,23 +46,161 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
3646
return badRequestResponse('Invalid version number')
3747
}
3848

49+
const [versionRow] = await db
50+
.select({
51+
id: workflowDeploymentVersion.id,
52+
state: workflowDeploymentVersion.state,
53+
})
54+
.from(workflowDeploymentVersion)
55+
.where(
56+
and(
57+
eq(workflowDeploymentVersion.workflowId, workflowId),
58+
eq(workflowDeploymentVersion.version, versionNum)
59+
)
60+
)
61+
.limit(1)
62+
63+
if (!versionRow?.state) {
64+
return notFoundResponse('Deployment version')
65+
}
66+
67+
const [currentActiveVersion] = await db
68+
.select({ id: workflowDeploymentVersion.id })
69+
.from(workflowDeploymentVersion)
70+
.where(
71+
and(
72+
eq(workflowDeploymentVersion.workflowId, workflowId),
73+
eq(workflowDeploymentVersion.isActive, true)
74+
)
75+
)
76+
.limit(1)
77+
78+
const previousVersionId = currentActiveVersion?.id
79+
80+
const deployedState = versionRow.state as { blocks?: Record<string, BlockState> }
81+
const blocks = deployedState.blocks
82+
if (!blocks || typeof blocks !== 'object') {
83+
return internalErrorResponse('Invalid deployed state structure')
84+
}
85+
86+
const workflowData = workflowRecord as Record<string, unknown>
87+
88+
const scheduleValidation = validateWorkflowSchedules(blocks)
89+
if (!scheduleValidation.isValid) {
90+
return badRequestResponse(`Invalid schedule configuration: ${scheduleValidation.error}`)
91+
}
92+
93+
const triggerSaveResult = await saveTriggerWebhooksForDeploy({
94+
request,
95+
workflowId,
96+
workflow: workflowData,
97+
userId: workflowRecord.userId,
98+
blocks,
99+
requestId,
100+
deploymentVersionId: versionRow.id,
101+
previousVersionId,
102+
forceRecreateSubscriptions: true,
103+
})
104+
105+
if (!triggerSaveResult.success) {
106+
logger.error(
107+
`[${requestId}] Admin API: Failed to sync triggers for workflow ${workflowId}`,
108+
triggerSaveResult.error
109+
)
110+
return internalErrorResponse(
111+
triggerSaveResult.error?.message || 'Failed to sync trigger configuration'
112+
)
113+
}
114+
115+
const scheduleResult = await createSchedulesForDeploy(workflowId, blocks, db, versionRow.id)
116+
117+
if (!scheduleResult.success) {
118+
await cleanupDeploymentVersion({
119+
workflowId,
120+
workflow: workflowData,
121+
requestId,
122+
deploymentVersionId: versionRow.id,
123+
})
124+
if (previousVersionId) {
125+
await restorePreviousVersionWebhooks({
126+
request,
127+
workflow: workflowData,
128+
userId: workflowRecord.userId,
129+
previousVersionId,
130+
requestId,
131+
})
132+
}
133+
return internalErrorResponse(scheduleResult.error || 'Failed to sync schedules')
134+
}
135+
39136
const result = await activateWorkflowVersion({ workflowId, version: versionNum })
40137
if (!result.success) {
138+
await cleanupDeploymentVersion({
139+
workflowId,
140+
workflow: workflowData,
141+
requestId,
142+
deploymentVersionId: versionRow.id,
143+
})
144+
if (previousVersionId) {
145+
await restorePreviousVersionWebhooks({
146+
request,
147+
workflow: workflowData,
148+
userId: workflowRecord.userId,
149+
previousVersionId,
150+
requestId,
151+
})
152+
}
41153
if (result.error === 'Deployment version not found') {
42154
return notFoundResponse('Deployment version')
43155
}
44156
return internalErrorResponse(result.error || 'Failed to activate version')
45157
}
46158

47-
logger.info(`Admin API: Activated version ${versionNum} for workflow ${workflowId}`)
159+
if (previousVersionId && previousVersionId !== versionRow.id) {
160+
try {
161+
logger.info(
162+
`[${requestId}] Admin API: Cleaning up previous version ${previousVersionId} webhooks/schedules`
163+
)
164+
await cleanupDeploymentVersion({
165+
workflowId,
166+
workflow: workflowData,
167+
requestId,
168+
deploymentVersionId: previousVersionId,
169+
skipExternalCleanup: true,
170+
})
171+
logger.info(`[${requestId}] Admin API: Previous version cleanup completed`)
172+
} catch (cleanupError) {
173+
logger.error(
174+
`[${requestId}] Admin API: Failed to clean up previous version ${previousVersionId}`,
175+
cleanupError
176+
)
177+
}
178+
}
179+
180+
await syncMcpToolsForWorkflow({
181+
workflowId,
182+
requestId,
183+
state: versionRow.state,
184+
context: 'activate',
185+
})
186+
187+
logger.info(
188+
`[${requestId}] Admin API: Activated version ${versionNum} for workflow ${workflowId}`
189+
)
48190

49191
return singleResponse({
50192
success: true,
51193
version: versionNum,
52194
deployedAt: result.deployedAt!.toISOString(),
195+
warnings: triggerSaveResult.warnings,
53196
})
54197
} catch (error) {
55-
logger.error(`Admin API: Failed to activate version for workflow ${workflowId}`, { error })
198+
logger.error(
199+
`[${requestId}] Admin API: Failed to activate version for workflow ${workflowId}`,
200+
{
201+
error,
202+
}
203+
)
56204
return internalErrorResponse('Failed to activate deployment version')
57205
}
58206
})

0 commit comments

Comments
 (0)