Skip to content

Commit 4bbbde4

Browse files
waleedlatif1claude
andcommitted
refactor(webhooks): decompose provider-subscriptions into handler registry pattern
Move all provider-specific subscription create/delete logic from the monolithic provider-subscriptions.ts into individual provider handler files via new createSubscription/deleteSubscription methods on WebhookProviderHandler. Replace the two massive if-else dispatch chains (11 branches each) with simple registry lookups via getProviderHandler(). provider-subscriptions.ts reduced from 2,337 lines to 128 lines (orchestration only). Also migrate polling configuration (gmail, outlook, rss, imap) into provider handlers via configurePolling() method, and challenge/verification handling (slack, whatsapp, teams) via handleChallenge() method. Delete polling-config.ts. Create new handler files for fathom and lemlist providers. Extract shared subscription utilities into subscription-utils.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5ca16fb commit 4bbbde4

25 files changed

+2670
-2694
lines changed

apps/sim/app/api/webhooks/route.ts

Lines changed: 39 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,12 @@ import { generateRequestId } from '@/lib/core/utils/request'
1111
import { getProviderIdFromServiceId } from '@/lib/oauth'
1212
import { captureServerEvent } from '@/lib/posthog/server'
1313
import { resolveEnvVarsInObject } from '@/lib/webhooks/env-resolver'
14-
import {
15-
configureGmailPolling,
16-
configureOutlookPolling,
17-
configureRssPolling,
18-
} from '@/lib/webhooks/polling-config'
1914
import {
2015
cleanupExternalWebhook,
2116
createExternalWebhookSubscription,
2217
shouldRecreateExternalWebhookSubscription,
2318
} from '@/lib/webhooks/provider-subscriptions'
19+
import { getProviderHandler } from '@/lib/webhooks/providers'
2420
import { mergeNonUserFields } from '@/lib/webhooks/utils'
2521
import { syncWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
2622
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
@@ -402,24 +398,24 @@ export async function POST(request: NextRequest) {
402398
)
403399
}
404400

405-
const needsConfiguration = provider === 'gmail' || provider === 'outlook'
401+
const providerHandler = getProviderHandler(provider)
406402

407-
if (needsConfiguration) {
408-
const configureFunc =
409-
provider === 'gmail' ? configureGmailPolling : configureOutlookPolling
403+
if (providerHandler.configurePolling) {
410404
const configureErrors: string[] = []
411405

412406
for (const wh of syncResult.webhooks) {
413407
if (wh.isNew) {
414-
// Fetch the webhook data for configuration
415408
const webhookRows = await db
416409
.select()
417410
.from(webhook)
418411
.where(and(eq(webhook.id, wh.id), isNull(webhook.archivedAt)))
419412
.limit(1)
420413

421414
if (webhookRows.length > 0) {
422-
const success = await configureFunc(webhookRows[0], requestId)
415+
const success = await providerHandler.configurePolling({
416+
webhook: webhookRows[0],
417+
requestId,
418+
})
423419
if (!success) {
424420
configureErrors.push(
425421
`Failed to configure webhook for credential ${wh.credentialId}`
@@ -436,7 +432,6 @@ export async function POST(request: NextRequest) {
436432
configureErrors.length > 0 &&
437433
configureErrors.length === syncResult.webhooks.length
438434
) {
439-
// All configurations failed - roll back
440435
logger.error(`[${requestId}] All webhook configurations failed, rolling back`)
441436
for (const wh of syncResult.webhooks) {
442437
await db.delete(webhook).where(eq(webhook.id, wh.id))
@@ -629,115 +624,51 @@ export async function POST(request: NextRequest) {
629624
}
630625
}
631626

632-
// --- Gmail/Outlook webhook setup (these don't require external subscriptions, configure after DB save) ---
633-
if (savedWebhook && provider === 'gmail') {
634-
logger.info(`[${requestId}] Gmail provider detected. Setting up Gmail webhook configuration.`)
635-
try {
636-
const success = await configureGmailPolling(savedWebhook, requestId)
637-
638-
if (!success) {
639-
logger.error(`[${requestId}] Failed to configure Gmail polling, rolling back webhook`)
640-
await revertSavedWebhook(savedWebhook, existingWebhook, requestId)
641-
return NextResponse.json(
642-
{
643-
error: 'Failed to configure Gmail polling',
644-
details: 'Please check your Gmail account permissions and try again',
645-
},
646-
{ status: 500 }
647-
)
648-
}
649-
650-
logger.info(`[${requestId}] Successfully configured Gmail polling`)
651-
} catch (err) {
652-
logger.error(
653-
`[${requestId}] Error setting up Gmail webhook configuration, rolling back webhook`,
654-
err
655-
)
656-
await revertSavedWebhook(savedWebhook, existingWebhook, requestId)
657-
return NextResponse.json(
658-
{
659-
error: 'Failed to configure Gmail webhook',
660-
details: err instanceof Error ? err.message : 'Unknown error',
661-
},
662-
{ status: 500 }
627+
// --- Polling provider setup (Gmail, Outlook, RSS, IMAP, etc.) ---
628+
if (savedWebhook) {
629+
const pollingHandler = getProviderHandler(provider)
630+
if (pollingHandler.configurePolling) {
631+
logger.info(
632+
`[${requestId}] ${provider} provider detected. Setting up polling configuration.`
663633
)
664-
}
665-
}
666-
// --- End Gmail specific logic ---
634+
try {
635+
const success = await pollingHandler.configurePolling({
636+
webhook: savedWebhook,
637+
requestId,
638+
})
667639

668-
// --- Outlook webhook setup ---
669-
if (savedWebhook && provider === 'outlook') {
670-
logger.info(
671-
`[${requestId}] Outlook provider detected. Setting up Outlook webhook configuration.`
672-
)
673-
try {
674-
const success = await configureOutlookPolling(savedWebhook, requestId)
640+
if (!success) {
641+
logger.error(
642+
`[${requestId}] Failed to configure ${provider} polling, rolling back webhook`
643+
)
644+
await revertSavedWebhook(savedWebhook, existingWebhook, requestId)
645+
return NextResponse.json(
646+
{
647+
error: `Failed to configure ${provider} polling`,
648+
details: 'Please check your account permissions and try again',
649+
},
650+
{ status: 500 }
651+
)
652+
}
675653

676-
if (!success) {
677-
logger.error(`[${requestId}] Failed to configure Outlook polling, rolling back webhook`)
678-
await revertSavedWebhook(savedWebhook, existingWebhook, requestId)
679-
return NextResponse.json(
680-
{
681-
error: 'Failed to configure Outlook polling',
682-
details: 'Please check your Outlook account permissions and try again',
683-
},
684-
{ status: 500 }
654+
logger.info(`[${requestId}] Successfully configured ${provider} polling`)
655+
} catch (err) {
656+
logger.error(
657+
`[${requestId}] Error setting up ${provider} webhook configuration, rolling back webhook`,
658+
err
685659
)
686-
}
687-
688-
logger.info(`[${requestId}] Successfully configured Outlook polling`)
689-
} catch (err) {
690-
logger.error(
691-
`[${requestId}] Error setting up Outlook webhook configuration, rolling back webhook`,
692-
err
693-
)
694-
await revertSavedWebhook(savedWebhook, existingWebhook, requestId)
695-
return NextResponse.json(
696-
{
697-
error: 'Failed to configure Outlook webhook',
698-
details: err instanceof Error ? err.message : 'Unknown error',
699-
},
700-
{ status: 500 }
701-
)
702-
}
703-
}
704-
// --- End Outlook specific logic ---
705-
706-
// --- RSS webhook setup ---
707-
if (savedWebhook && provider === 'rss') {
708-
logger.info(`[${requestId}] RSS provider detected. Setting up RSS webhook configuration.`)
709-
try {
710-
const success = await configureRssPolling(savedWebhook, requestId)
711-
712-
if (!success) {
713-
logger.error(`[${requestId}] Failed to configure RSS polling, rolling back webhook`)
714660
await revertSavedWebhook(savedWebhook, existingWebhook, requestId)
715661
return NextResponse.json(
716662
{
717-
error: 'Failed to configure RSS polling',
718-
details: 'Please try again',
663+
error: `Failed to configure ${provider} webhook`,
664+
details: err instanceof Error ? err.message : 'Unknown error',
719665
},
720666
{ status: 500 }
721667
)
722668
}
723-
724-
logger.info(`[${requestId}] Successfully configured RSS polling`)
725-
} catch (err) {
726-
logger.error(
727-
`[${requestId}] Error setting up RSS webhook configuration, rolling back webhook`,
728-
err
729-
)
730-
await revertSavedWebhook(savedWebhook, existingWebhook, requestId)
731-
return NextResponse.json(
732-
{
733-
error: 'Failed to configure RSS webhook',
734-
details: err instanceof Error ? err.message : 'Unknown error',
735-
},
736-
{ status: 500 }
737-
)
738669
}
739670
}
740-
// --- End RSS specific logic ---
671+
// --- End polling provider setup ---
741672

742673
if (!targetWebhookId && savedWebhook) {
743674
try {

apps/sim/lib/webhooks/deploy.ts

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import { nanoid } from 'nanoid'
66
import type { NextRequest } from 'next/server'
77
import { getProviderIdFromServiceId } from '@/lib/oauth'
88
import { PendingWebhookVerificationTracker } from '@/lib/webhooks/pending-verification'
9-
import { configureGmailPolling, configureOutlookPolling } from '@/lib/webhooks/polling-config'
109
import {
1110
cleanupExternalWebhook,
1211
createExternalWebhookSubscription,
1312
shouldRecreateExternalWebhookSubscription,
1413
} from '@/lib/webhooks/provider-subscriptions'
14+
import { getProviderHandler } from '@/lib/webhooks/providers'
1515
import { syncWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
1616
import { getBlock } from '@/blocks'
1717
import type { SubBlockConfig } from '@/blocks/types'
@@ -230,29 +230,20 @@ function buildProviderConfig(
230230

231231
async function configurePollingIfNeeded(
232232
provider: string,
233-
savedWebhook: any,
233+
savedWebhook: Record<string, unknown>,
234234
requestId: string
235235
): Promise<TriggerSaveError | null> {
236-
if (provider === 'gmail') {
237-
const success = await configureGmailPolling(savedWebhook, requestId)
238-
if (!success) {
239-
await db.delete(webhook).where(eq(webhook.id, savedWebhook.id))
240-
return {
241-
message: 'Failed to configure Gmail polling. Please check your Gmail account permissions.',
242-
status: 500,
243-
}
244-
}
236+
const handler = getProviderHandler(provider)
237+
if (!handler.configurePolling) {
238+
return null
245239
}
246240

247-
if (provider === 'outlook') {
248-
const success = await configureOutlookPolling(savedWebhook, requestId)
249-
if (!success) {
250-
await db.delete(webhook).where(eq(webhook.id, savedWebhook.id))
251-
return {
252-
message:
253-
'Failed to configure Outlook polling. Please check your Outlook account permissions.',
254-
status: 500,
255-
}
241+
const success = await handler.configurePolling({ webhook: savedWebhook, requestId })
242+
if (!success) {
243+
await db.delete(webhook).where(eq(webhook.id, savedWebhook.id as string))
244+
return {
245+
message: `Failed to configure ${provider} polling. Please check your account permissions.`,
246+
status: 500,
256247
}
257248
}
258249

@@ -319,13 +310,13 @@ async function syncCredentialSetWebhooks(params: {
319310
}
320311
}
321312

322-
if (provider === 'gmail' || provider === 'outlook') {
323-
const configureFunc = provider === 'gmail' ? configureGmailPolling : configureOutlookPolling
313+
const handler = getProviderHandler(provider)
314+
if (handler.configurePolling) {
324315
for (const wh of syncResult.webhooks) {
325316
if (wh.isNew) {
326317
const rows = await db.select().from(webhook).where(eq(webhook.id, wh.id)).limit(1)
327318
if (rows.length > 0) {
328-
const success = await configureFunc(rows[0], requestId)
319+
const success = await handler.configurePolling({ webhook: rows[0], requestId })
329320
if (!success) {
330321
await db.delete(webhook).where(eq(webhook.id, wh.id))
331322
return {

0 commit comments

Comments
 (0)