Skip to content

Commit e499cc4

Browse files
improvement(webhooks): lifecycle management with external providers, remove save configuration (#2831)
* fix(webhooks): lifecycle code accuracy * remove save configuration button * remove useless instruction * address greptile comments * fix lint * on undeploy cleanup webhooks
1 parent 5e44357 commit e499cc4

File tree

19 files changed

+1584
-1485
lines changed

19 files changed

+1584
-1485
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { db, workflow } from '@sim/db'
22
import { createLogger } from '@sim/logger'
33
import { eq } from 'drizzle-orm'
4+
import { generateRequestId } from '@/lib/core/utils/request'
5+
import { cleanupWebhooksForWorkflow } from '@/lib/webhooks/deploy'
46
import {
57
deployWorkflow,
68
loadWorkflowFromNormalizedTables,
@@ -80,10 +82,11 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
8082

8183
export const DELETE = withAdminAuthParams<RouteParams>(async (request, context) => {
8284
const { id: workflowId } = await context.params
85+
const requestId = generateRequestId()
8386

8487
try {
8588
const [workflowRecord] = await db
86-
.select({ id: workflow.id })
89+
.select()
8790
.from(workflow)
8891
.where(eq(workflow.id, workflowId))
8992
.limit(1)
@@ -92,6 +95,13 @@ export const DELETE = withAdminAuthParams<RouteParams>(async (request, context)
9295
return notFoundResponse('Workflow')
9396
}
9497

98+
// Clean up external webhook subscriptions before undeploying
99+
await cleanupWebhooksForWorkflow(
100+
workflowId,
101+
workflowRecord as Record<string, unknown>,
102+
requestId
103+
)
104+
95105
const result = await undeployWorkflow({ workflowId })
96106
if (!result.success) {
97107
return internalErrorResponse(result.error || 'Failed to undeploy workflow')

apps/sim/app/api/webhooks/[id]/route.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { getSession } from '@/lib/auth'
77
import { validateInteger } from '@/lib/core/security/input-validation'
88
import { PlatformEvents } from '@/lib/core/telemetry'
99
import { generateRequestId } from '@/lib/core/utils/request'
10+
import {
11+
cleanupExternalWebhook,
12+
createExternalWebhookSubscription,
13+
shouldRecreateExternalWebhookSubscription,
14+
} from '@/lib/webhooks/provider-subscriptions'
1015
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
1116

1217
const logger = createLogger('WebhookAPI')
@@ -177,6 +182,46 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
177182
return NextResponse.json({ error: 'Access denied' }, { status: 403 })
178183
}
179184

185+
const existingProviderConfig =
186+
(webhookData.webhook.providerConfig as Record<string, unknown>) || {}
187+
let nextProviderConfig =
188+
providerConfig !== undefined &&
189+
resolvedProviderConfig &&
190+
typeof resolvedProviderConfig === 'object'
191+
? (resolvedProviderConfig as Record<string, unknown>)
192+
: existingProviderConfig
193+
const nextProvider = (provider ?? webhookData.webhook.provider) as string
194+
195+
if (
196+
providerConfig !== undefined &&
197+
shouldRecreateExternalWebhookSubscription({
198+
previousProvider: webhookData.webhook.provider as string,
199+
nextProvider,
200+
previousConfig: existingProviderConfig,
201+
nextConfig: nextProviderConfig,
202+
})
203+
) {
204+
await cleanupExternalWebhook(
205+
{ ...webhookData.webhook, providerConfig: existingProviderConfig },
206+
webhookData.workflow,
207+
requestId
208+
)
209+
210+
const result = await createExternalWebhookSubscription(
211+
request,
212+
{
213+
...webhookData.webhook,
214+
provider: nextProvider,
215+
providerConfig: nextProviderConfig,
216+
},
217+
webhookData.workflow,
218+
session.user.id,
219+
requestId
220+
)
221+
222+
nextProviderConfig = result.updatedProviderConfig as Record<string, unknown>
223+
}
224+
180225
logger.debug(`[${requestId}] Updating webhook properties`, {
181226
hasPathUpdate: path !== undefined,
182227
hasProviderUpdate: provider !== undefined,
@@ -188,16 +233,16 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
188233
// Merge providerConfig to preserve credential-related fields
189234
let finalProviderConfig = webhooks[0].webhook.providerConfig
190235
if (providerConfig !== undefined) {
191-
const existingConfig = (webhooks[0].webhook.providerConfig as Record<string, unknown>) || {}
236+
const existingConfig = existingProviderConfig
192237
finalProviderConfig = {
193-
...resolvedProviderConfig,
238+
...nextProviderConfig,
194239
credentialId: existingConfig.credentialId,
195240
credentialSetId: existingConfig.credentialSetId,
196241
userId: existingConfig.userId,
197242
historyId: existingConfig.historyId,
198243
lastCheckedTimestamp: existingConfig.lastCheckedTimestamp,
199244
setupCompleted: existingConfig.setupCompleted,
200-
externalId: existingConfig.externalId,
245+
externalId: nextProviderConfig.externalId ?? existingConfig.externalId,
201246
}
202247
}
203248

0 commit comments

Comments
 (0)