Skip to content

refactor(webhooks): extract provider-specific logic into handler registry#3973

Merged
waleedlatif1 merged 18 commits intostagingfrom
waleedlatif1/refactor-panel-code
Apr 6, 2026
Merged

refactor(webhooks): extract provider-specific logic into handler registry#3973
waleedlatif1 merged 18 commits intostagingfrom
waleedlatif1/refactor-panel-code

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Extract 14+ provider-specific if-chains from processor.ts and webhook-execution.ts into a provider handler registry pattern
  • Each provider gets its own handler file in lib/webhooks/providers/ implementing only the methods it needs
  • Shared utilities: createHmacVerifier factory, verifyTokenAuth helper, skipByEventTypes filter
  • processor.ts reduced from 1468 → ~685 lines, webhook-execution.ts from 713 → ~570 lines
  • Removes verifyProviderWebhook from utils.server.ts (merged into per-provider auth)
  • Deletes provider-utils.ts (folded into registry)
  • 3 security improvements: timing-safe token comparison for generic, google-forms, and default handler
  • 1 bug fix: Jira event matching now correctly passes issueEventTypeName as string
  • Updates add-trigger skill with webhook provider handler documentation

Type of Change

  • Refactoring (no user-facing behavior changes)

Testing

  • All 24 webhook tests passing
  • Zero TypeScript errors
  • Validated via 4 parallel regression agents tracing every provider's auth, event matching, idempotency, responses, and execution lifecycle line-by-line against original code

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 5, 2026

PR Summary

Medium Risk
Touches core webhook ingestion/execution paths (auth, event filtering, subscription lifecycle, responses) by rerouting behavior through a new handler registry; regressions could impact delivery across many providers despite largely refactor intent.

Overview
Refactors webhook processing to use a centralized provider handler registry (lib/webhooks/providers/*) instead of large provider-specific if/else chains spread across processor.ts, provider-subscriptions.ts, webhook-execution.ts, deploy.ts, and the API webhooks route.

Provider behaviors (auth verification, challenge handling, event matching/skipping, idempotency key extraction/header enrichment, success/error response formatting, subscription create/delete, polling configuration, and optional file/input processing) are now invoked via handler methods, and the legacy provider-utils.ts and the large provider-specific implementation in provider-subscriptions.ts are removed.

Also tightens typing (unknown/Record<string, unknown>), centralizes execution-result handling in webhook-execution.ts, adds a deploy-time validation for generic auth config, updates tests/mocks accordingly, and expands internal docs to instruct implementing lifecycle + formatting on provider handlers (not orchestration files).

Reviewed by Cursor Bugbot for commit 98b4586. Configure here.

@gitguardian
Copy link
Copy Markdown

gitguardian bot commented Apr 5, 2026

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 6, 2026 6:39am

Request Review

@waleedlatif1 waleedlatif1 force-pushed the waleedlatif1/refactor-panel-code branch from f580455 to 6e95981 Compare April 5, 2026 03:35
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 5, 2026

Greptile Summary

This PR extracts 14+ provider-specific if-chains from processor.ts and webhook-execution.ts into a provider handler registry pattern. Each of the 28 providers now lives in lib/webhooks/providers/{provider}.ts implementing a WebhookProviderHandler strategy interface with 14 optional methods. The core files are slimmed dramatically (processor.ts: 1468→685 lines, utils.server.ts: 2572→268 lines, provider-subscriptions.ts: 1978→8 lines).

Key changes:

  • getProviderHandler(provider) is the single lookup point; unknown providers fall back to a default timing-safe bearer-token handler
  • Shared auth utilities (createHmacVerifier, verifyTokenAuth, skipByEventTypes) replace duplicated HMAC/token logic across multiple providers
  • Three security improvements: timing-safe token comparison added for generic, Google Forms, and default handler
  • Bug fix: Jira event matching now passes issueEventTypeName as string (not the full body object) to isJiraEventMatch
  • Deploy-time validation in deploy.ts now rejects generic webhook deployments where requireAuth: true but no token is configured, surfacing the misconfiguration before runtime
  • webhook-execution.ts unifies the Airtable special-case under handler.formatInput/handler.handleEmptyInput
  • A new handleExecutionResult helper in webhook-execution.ts eliminates timeout/pause/resume logic that was duplicated across provider branches

One P2 finding: The CHALLENGE_PROVIDERS constant in processor.ts (lines 116–125) is a hardcoded list that must be kept in sync with any future provider that adds a handleChallenge implementation.

Confidence Score: 5/5

Safe to merge — clean structural refactoring with no user-facing behavior changes; all prior P0/P1 concerns from previous review threads are resolved

All previous reviewer concerns (array-aware processed initializer, any types in the three new helper functions, duplicate generic file processing) have been addressed in subsequent commits. The Jira bug fix is correctly applied (issueEventTypeName passed as string). The new handler pattern is well-typed with no new any introductions. The only remaining finding is a P2 maintenance concern about the hardcoded CHALLENGE_PROVIDERS list, which does not affect correctness today.

apps/sim/lib/webhooks/processor.ts (CHALLENGE_PROVIDERS list at lines 116–125) — P2 maintenance concern only

Important Files Changed

Filename Overview
apps/sim/lib/webhooks/providers/registry.ts New: central registry mapping 28 provider keys to handler objects, with timing-safe default bearer-token fallback
apps/sim/lib/webhooks/providers/types.ts New: WebhookProviderHandler strategy interface with 14 optional methods and 10 supporting context/result types
apps/sim/lib/webhooks/providers/utils.ts New: createHmacVerifier factory, timing-safe verifyTokenAuth, and skipByEventTypes shared utilities
apps/sim/lib/webhooks/processor.ts Reduced 1468→~685 lines; if-chains replaced by registry delegation; pre-existing any in verifyProviderAuth unchanged
apps/sim/background/webhook-execution.ts Airtable special-case removed; unified handler.formatInput/handleEmptyInput flow; handleExecutionResult helper extracted
apps/sim/lib/webhooks/providers/generic.ts New: requireAuth/IP-allowlist verifyAuth, idempotency enrichHeaders, custom response, file processing
apps/sim/lib/webhooks/providers/jira.ts New: bug fix — issueEventTypeName now correctly passed as string (not body) to isJiraEventMatch
apps/sim/lib/webhooks/providers/slack.ts New: url_verification challenge, 200-on-queue-error, reaction event text fetch, DNS-pinned file download
apps/sim/lib/webhooks/providers/airtable.ts New: fetchAndProcessAirtablePayloads moved into formatInput; skip signal on no changes; subscription CRUD
apps/sim/lib/webhooks/providers/microsoft-teams.ts New (787 lines): HMAC auth, validationToken challenge, Graph subscription management, DNS-pinned attachment fetch
apps/sim/lib/webhooks/deploy.ts Refactored to use handler registry; added deploy-time validation for requireAuth+no token (returns 400)
apps/sim/lib/webhooks/provider-subscriptions.ts Reduced 1978→~8 lines; all subscription logic delegated to individual provider handler files
apps/sim/lib/webhooks/utils.server.ts Reduced 2572→268 lines; only syncWebhooksForCredentialSet remains; all provider logic moved to handlers
apps/sim/lib/webhooks/providers/index.ts New barrel export with extractProviderIdentifierFromBody convenience wrapper

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Webhook Request] --> B[handleProviderChallenges\nslack / teams / whatsapp]
    B -->|challenge| C[Return 200 challenge response]
    B -->|no match| D[parseWebhookBody]
    D --> E[handlePreLookupWebhookVerification]
    E --> F[findWebhookAndWorkflow]
    F --> G[handleProviderReachabilityTest\nhandler.handleReachabilityTest]
    G --> H[verifyProviderAuth\nhandler.verifyAuth]
    H -->|401/403| I[formatProviderErrorResponse\nhandler.formatErrorResponse]
    H -->|pass| J[handler.matchEvent]
    J -->|skip| K[Return 200 skip message]
    J -->|match| L[handler.enrichHeaders\nidempotency key injection]
    L --> M[Billing / idempotency check]
    M --> N[Enqueue executeWebhookJob]
    N --> O[handler.formatSuccessResponse\nor default JSON 200]
    N -.->|async| Q[executeWebhookJobInternal]
    Q --> R{handler.formatInput?}
    R -->|yes| S[Call formatInput]
    R -->|no| T[Use payload.body directly]
    S --> U{input null?}
    T --> U
    U -->|yes + handleEmptyInput| V[Log skip and return]
    U -->|no| W[processTriggerFileOutputs\n+ handler.processInputFiles]
    W --> X[executeWorkflowCore]
    X --> Y[handleExecutionResult\ntimeout / pause / resume]
Loading

Reviews (8): Last reviewed commit: "refactor(webhooks): remove remaining any..." | Re-trigger Greptile

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6e95981. Configure here.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 0d116fa. Configure here.

- Restore original fall-through behavior for generic requireAuth with no token
- Replace `any` params with proper types in processor helper functions
- Restore array-aware initializer in processTriggerFileOutputs
…ggerFileOutputs

Cast array initializer to Record<string, unknown> to allow string indexing
while preserving array runtime semantics for the return value.
…gured

If a user explicitly sets requireAuth: true, they expect auth to be enforced.
Returning 401 when no token is configured is the correct behavior — this is
an intentional improvement over the original code which silently allowed
unauthenticated access in this case.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

…execution

The generic provider's processInputFiles handler already handles file[] field
processing via the handler.processInputFiles call. The hardcoded block from
staging was incorrectly preserved during rebase, causing double processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 60610b7. Configure here.

… at deploy time

Rejects deployment with a clear error message if a generic webhook trigger
has requireAuth enabled but no authentication token configured, rather than
letting requests fail with 401 at runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…olling config

The refactored IMAP handler added a rejectUnauthorized field that was not
present in the original configureImapPolling function. This would default
to true for all existing IMAP webhooks, potentially breaking connections
to servers with self-signed certificates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… handler

Per project coding standards, use generateId() from @/lib/core/utils/uuid
instead of crypto.randomUUID() directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…m providers

- Standardize logger names to WebhookProvider:X pattern across 6 providers
  (fathom, gmail, imap, lemlist, outlook, rss)
- Replace all `any` types in airtable handler with proper types:
  - Add AirtableTableChanges interface for API response typing
  - Change function params from `any` to `Record<string, unknown>`
  - Change AirtableChange fields from Record<string, any> to Record<string, unknown>
  - Change all catch blocks from `error: any` to `error: unknown`
  - Change input object from `any` to `Record<string, unknown>`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Replace 3 `catch (error: any)` with `catch (error: unknown)` and
1 `Record<string, any>` with `Record<string, unknown>`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 98b4586. Configure here.

@waleedlatif1 waleedlatif1 merged commit 5ca66c3 into staging Apr 6, 2026
12 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/refactor-panel-code branch April 6, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant