-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add partner.application_submitted webhook trigger
#3064
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a program-level webhook event Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Partner
participant WebApp as Web App
participant DB as Database
participant WebhookSvc as Workspace Webhook Sender
participant Integrations as Integrations (Slack, etc.)
Partner->>WebApp: Submit program application
WebApp->>DB: create application & program enrollment
DB-->>WebApp: application + programEnrollment
WebApp->>WebApp: formatApplicationFormData & formatWebsiteAndSocialsFields
alt program.workspace?.webhookEnabled
WebApp->>WebhookSvc: sendWorkspaceWebhook(event: partner.application_submitted, payload)
WebhookSvc->>Integrations: transform & dispatch (e.g., Slack template)
Integrations-->>WebhookSvc: delivery result
end
WebApp-->>Partner: confirm submission
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (15)
🧰 Additional context used🧠 Learnings (10)📚 Learning: 2025-10-17T08:18:19.278ZApplied to files:
📚 Learning: 2025-08-16T11:14:00.667ZApplied to files:
📚 Learning: 2025-08-14T05:00:23.224ZApplied to files:
📚 Learning: 2025-05-29T04:45:18.504ZApplied to files:
📚 Learning: 2025-08-26T15:05:55.081ZApplied to files:
📚 Learning: 2025-06-18T20:26:25.177ZApplied to files:
📚 Learning: 2025-09-17T17:44:03.965ZApplied to files:
📚 Learning: 2025-06-06T07:59:03.120ZApplied to files:
📚 Learning: 2025-08-25T17:33:45.072ZApplied to files:
📚 Learning: 2025-07-30T15:25:13.936ZApplied to files:
🔇 Additional comments (26)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…chema across relevant files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/lib/partners/complete-program-applications.ts (1)
120-207: Ensure webhook payload includes the freshly created enrollment data
partner.programsis the snapshot loaded before we callprogramEnrollment.createMany, soprogramEnrollmentisundefinedfor every newly completed application. As soon as we hitpartnerApplicationWebhookSchema.parse(...), the requiredstatusfield is missing and Zod throws synchronously, which also preventsPromise.allSettledfrom running and halts the rest of the completion flow. This means applications that should succeed will instead log an error and skip the webhook, auto-approve job, etc.Please hydrate the enrollment data from the rows we just inserted (or otherwise supply the required fields) before building the webhook payload. One possible fix:
@@ await prisma.programEnrollment.createMany({ data: programEnrollments, skipDuplicates: true, }); + const programEnrollmentsForWebhook = await prisma.programEnrollment.findMany({ + where: { + partnerId: user.partners[0].partnerId, + programId: { + in: filteredProgramApplications.map((p) => p.programId), + }, + }, + select: { + programId: true, + groupId: true, + tenantId: true, + status: true, + }, + }); + + const programEnrollmentsByProgramId = new Map( + programEnrollmentsForWebhook.map((enrollment) => [ + enrollment.programId, + enrollment, + ]), + ); @@ - const programEnrollment = partner.programs.find( - (p) => p.programId === programApplication.programId, - ); + const programEnrollment = programEnrollmentsByProgramId.get( + programApplication.programId, + ); @@ - workspacesByProgramId.has(program.id) && + workspacesByProgramId.has(program.id) && + programEnrollment && sendWorkspaceWebhook({ workspace: workspacesByProgramId.get(program.id)!, trigger: "partner.application_submitted", data: partnerApplicationWebhookSchema.parse({ id: application.id, partner: { ...partner, ...programEnrollment, }, applicationForm, }), }),
♻️ Duplicate comments (1)
apps/web/lib/integrations/common/ui/configure-webhook.tsx (1)
29-29: VerifydefaultProgramIdis returned byuseWorkspace.Same concern as in
add-edit-webhook-form.tsx- ensure thatdefaultProgramIdis actually returned by theuseWorkspacehook.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
apps/web/lib/actions/partners/create-program-application.ts(8 hunks)apps/web/lib/integrations/common/ui/configure-webhook.tsx(4 hunks)apps/web/lib/integrations/slack/transform.ts(3 hunks)apps/web/lib/partners/complete-program-applications.ts(4 hunks)apps/web/lib/webhook/constants.ts(2 hunks)apps/web/lib/webhook/sample-events/partner-application-submitted.json(1 hunks)apps/web/lib/webhook/sample-events/payload.ts(2 hunks)apps/web/lib/webhook/schemas.ts(2 hunks)apps/web/lib/webhook/types.ts(3 hunks)apps/web/lib/zod/schemas/program-application.ts(1 hunks)apps/web/lib/zod/schemas/programs.ts(1 hunks)apps/web/tests/webhooks/index.test.ts(2 hunks)apps/web/ui/webhooks/add-edit-webhook-form.tsx(4 hunks)
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2025-08-16T11:14:00.667Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2754
File: apps/web/lib/partnerstack/schemas.ts:47-52
Timestamp: 2025-08-16T11:14:00.667Z
Learning: The PartnerStack API always includes the `group` field in partner responses, so the schema should use `.nullable()` rather than `.nullish()` since the field is never omitted/undefined.
Applied to files:
apps/web/lib/zod/schemas/programs.ts
📚 Learning: 2025-08-14T05:00:23.224Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/app/api/og/program/route.tsx:63-64
Timestamp: 2025-08-14T05:00:23.224Z
Learning: In Dub's partner program system, the default partner group will always exist. When programs are created, a default partner group is automatically upserted using DEFAULT_PARTNER_GROUP constant, so accessing program.groups[0] in contexts where the default group is queried is safe.
Applied to files:
apps/web/lib/zod/schemas/programs.ts
📚 Learning: 2025-10-17T08:18:19.278Z
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.
Applied to files:
apps/web/tests/webhooks/index.test.tsapps/web/lib/zod/schemas/program-application.tsapps/web/lib/actions/partners/create-program-application.ts
📚 Learning: 2025-08-26T15:05:55.081Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.
Applied to files:
apps/web/ui/webhooks/add-edit-webhook-form.tsx
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.
Applied to files:
apps/web/lib/partners/complete-program-applications.tsapps/web/lib/zod/schemas/program-application.tsapps/web/lib/actions/partners/create-program-application.tsapps/web/lib/webhook/constants.ts
📚 Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.
Applied to files:
apps/web/lib/webhook/sample-events/payload.ts
📚 Learning: 2025-07-30T15:25:13.936Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.
Applied to files:
apps/web/lib/zod/schemas/program-application.ts
📚 Learning: 2025-05-29T04:45:18.504Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.
Applied to files:
apps/web/lib/integrations/slack/transform.tsapps/web/lib/webhook/constants.ts
📚 Learning: 2025-08-25T17:33:45.072Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts:12-12
Timestamp: 2025-08-25T17:33:45.072Z
Learning: The WorkflowTrigger enum in packages/prisma/schema/workflow.prisma contains three values: leadRecorded, saleRecorded, and commissionEarned. All three are properly used throughout the codebase.
Applied to files:
apps/web/lib/webhook/constants.ts
🧬 Code graph analysis (9)
apps/web/lib/webhook/schemas.ts (1)
apps/web/lib/zod/schemas/program-application.ts (1)
partnerApplicationWebhookSchema(5-39)
apps/web/tests/webhooks/index.test.ts (1)
apps/web/lib/zod/schemas/program-application.ts (1)
partnerApplicationWebhookSchema(5-39)
apps/web/ui/webhooks/add-edit-webhook-form.tsx (2)
apps/web/lib/swr/use-workspace.ts (1)
useWorkspace(6-46)apps/web/lib/webhook/constants.ts (2)
WORKSPACE_LEVEL_WEBHOOK_TRIGGERS(9-15)PROGRAM_LEVEL_WEBHOOK_TRIGGERS(17-23)
apps/web/lib/partners/complete-program-applications.ts (3)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/partners/format-application-form-data.ts (1)
formatApplicationFormData(4-48)apps/web/lib/zod/schemas/program-application.ts (1)
partnerApplicationWebhookSchema(5-39)
apps/web/lib/zod/schemas/program-application.ts (2)
apps/web/lib/zod/schemas/partners.ts (2)
EnrolledPartnerSchema(333-398)PartnerOnlinePresenceSchema(193-227)apps/web/lib/zod/schemas/programs.ts (1)
ProgramEnrollmentSchema(84-133)
apps/web/lib/integrations/slack/transform.ts (1)
apps/web/lib/webhook/types.ts (1)
PartnerApplicationWebhookPayload(21-23)
apps/web/lib/webhook/types.ts (1)
apps/web/lib/zod/schemas/program-application.ts (1)
partnerApplicationWebhookSchema(5-39)
apps/web/lib/integrations/common/ui/configure-webhook.tsx (2)
apps/web/lib/swr/use-workspace.ts (1)
useWorkspace(6-46)apps/web/lib/webhook/constants.ts (3)
WORKSPACE_LEVEL_WEBHOOK_TRIGGERS(9-15)PROGRAM_LEVEL_WEBHOOK_TRIGGERS(17-23)WEBHOOK_TRIGGER_DESCRIPTIONS(33-48)
apps/web/lib/actions/partners/create-program-application.ts (3)
apps/web/lib/zod/schemas/programs.ts (1)
createProgramApplicationSchema(164-171)apps/web/lib/partners/format-application-form-data.ts (1)
formatApplicationFormData(4-48)apps/web/lib/zod/schemas/program-application.ts (1)
partnerApplicationWebhookSchema(5-39)
🔇 Additional comments (9)
apps/web/lib/zod/schemas/programs.ts (1)
86-86: LGTM - TODO cleanup.Removing the TODO comment is appropriate since this PR introduces the program application webhook schema that likely addresses the concern.
apps/web/tests/webhooks/index.test.ts (1)
16-16: LGTM - Test coverage extended.The new webhook event is properly registered in the test suite, consistent with the existing pattern for other webhook events.
Also applies to: 71-71
apps/web/lib/webhook/sample-events/payload.ts (1)
10-10: LGTM - Sample payload wired correctly.The new event payload is properly integrated into the sample events mapping. However, ensure the sample JSON structure in
partner-application-submitted.jsonis corrected to match the schema definition.Also applies to: 21-21
apps/web/lib/webhook/types.ts (1)
6-6: LGTM - Type definition follows established pattern.The new webhook payload type is properly defined and integrated into the type system, consistent with existing webhook event types.
Also applies to: 21-23, 37-37
apps/web/ui/webhooks/add-edit-webhook-form.tsx (2)
122-125: Conditional program-level triggers logic looks correct.The logic appropriately shows program-level webhook triggers only when a default program exists (
defaultProgramIdis truthy), which prevents displaying irrelevant options when no program context is available.
37-42: No issues found. The review comment is incorrect.The
useWorkspace()hook does returndefaultProgramId. The hook spreads the entireworkspaceobject (typed asExtendedWorkspaceProps), which explicitly definesdefaultProgramId: string | null. This field comes from the API response and is correctly destructured in the webhook form at line 41. The conditional usage at line 124 is also valid.apps/web/lib/integrations/common/ui/configure-webhook.tsx (1)
94-97: Conditional trigger display with proper filtering.The implementation correctly combines program-level and workspace-level triggers when a program context exists, with appropriate filtering by permissions (
canManageWebhook) and integration capabilities (supportedEvents).Also applies to: 118-158
apps/web/lib/integrations/slack/transform.ts (2)
254-307: LGTM - Slack template follows established patterns.The template structure is consistent with existing webhook templates, properly formats the partner application data, and includes helpful context (Application ID and view link). The conditional rendering of
companyNameis a nice touch.
482-482: Template properly registered.The new template is correctly wired into the
slackTemplatesmap for thepartner.application_submittedevent.
…fields, update application form data structure, and enhance webhook payload schema for partner application submissions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/web/lib/partners/format-application-form-data.ts (1)
55-72: Review the empty string handling logic.On Line 66, the ternary expression
item.value !== "" ? item.value : ""will always returnitem.value(even if it's an empty string). This seems inconsistent with the return typeRecord<string, string | null>which suggestsnullcould be a value.Consider whether empty strings should be converted to
nullfor consistency:- result[item.type] = item.value !== "" ? item.value : ""; + result[item.type] = item.value !== "" ? item.value : null;Or if empty strings are intentional, update the return type to
Record<string, string>.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/lib/actions/partners/create-program-application.ts(8 hunks)apps/web/lib/partners/complete-program-applications.ts(4 hunks)apps/web/lib/partners/format-application-form-data.ts(2 hunks)apps/web/lib/webhook/sample-events/partner-application-submitted.json(1 hunks)apps/web/lib/webhook/sample-events/partner-enrolled.json(2 hunks)apps/web/lib/zod/schemas/program-application.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/lib/webhook/sample-events/partner-application-submitted.json
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.
Applied to files:
apps/web/lib/zod/schemas/program-application.tsapps/web/lib/partners/complete-program-applications.tsapps/web/lib/actions/partners/create-program-application.ts
📚 Learning: 2025-07-30T15:25:13.936Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.
Applied to files:
apps/web/lib/zod/schemas/program-application.ts
📚 Learning: 2025-10-17T08:18:19.278Z
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.
Applied to files:
apps/web/lib/zod/schemas/program-application.tsapps/web/lib/actions/partners/create-program-application.ts
🧬 Code graph analysis (4)
apps/web/lib/zod/schemas/program-application.ts (2)
apps/web/lib/zod/schemas/partners.ts (2)
EnrolledPartnerSchema(333-398)PartnerOnlinePresenceSchema(193-227)apps/web/lib/zod/schemas/programs.ts (1)
ProgramEnrollmentSchema(84-133)
apps/web/lib/partners/format-application-form-data.ts (1)
apps/web/lib/types.ts (1)
ProgramApplicationFormDataWithValues(476-478)
apps/web/lib/partners/complete-program-applications.ts (2)
apps/web/lib/partners/format-application-form-data.ts (2)
formatApplicationFormData(9-53)formatWebsiteAndSocialsFields(55-72)apps/web/lib/zod/schemas/program-application.ts (1)
partnerApplicationWebhookSchema(5-42)
apps/web/lib/actions/partners/create-program-application.ts (3)
apps/web/lib/zod/schemas/programs.ts (1)
createProgramApplicationSchema(164-171)apps/web/lib/partners/format-application-form-data.ts (2)
formatApplicationFormData(9-53)formatWebsiteAndSocialsFields(55-72)apps/web/lib/zod/schemas/program-application.ts (1)
partnerApplicationWebhookSchema(5-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (16)
apps/web/lib/webhook/sample-events/partner-enrolled.json (1)
2-2: LGTM! Sample data ID format updated consistently.The ID values have been updated to use a consistent format across both the top-level
idandpartnerIdfields.Also applies to: 11-11
apps/web/lib/zod/schemas/program-application.ts (1)
1-42: LGTM! Well-structured webhook schema.The schema correctly imports from the standard
zodpackage and combines multiple schemas to create a comprehensive webhook payload structure. TheapplicationFormDataarray structure withlabel/valuepairs is well-suited for webhook payloads.apps/web/lib/partners/complete-program-applications.ts (5)
7-12: LGTM! Necessary imports and data selection for webhook integration.The new imports support webhook publishing and application data formatting. Extending the program selection to include
tenantId,status, andgroupIdis appropriate for constructing the webhook payload.Also applies to: 30-34
104-121: LGTM! Efficient workspace fetching and mapping.The implementation efficiently fetches all necessary workspace data in a single query and creates a Map for O(1) lookups during webhook emission. This is a good pattern for batch processing.
131-160: LGTM! More readable social fields mapping.The explicit multi-line object literal improves readability while maintaining the same functionality. Each social field is clearly mapped with its conditional logic.
162-167: LGTM! Application form data properly formatted for webhook.The application form data is correctly transformed from the internal format to a label/value structure suitable for webhook payloads, with empty strings converted to
null.
196-212: LGTM! Webhook emission with proper validation and conditional execution.The webhook is conditionally sent only when a workspace exists, and the payload is validated against the schema before emission. This is good defensive programming. Note: ensure
programEnrollment(Line 206) is always defined as flagged in a separate comment.apps/web/lib/partners/format-application-form-data.ts (2)
4-7: LGTM! Good practice to define a shared interface.The
FormDataKeyValueinterface improves type safety and makes the return type explicit and reusable across the codebase.
9-53: LGTM! Consistent use of the new interface.The return type and type assertion have been updated to use the new
FormDataKeyValueinterface, improving type safety without changing functionality.apps/web/lib/actions/partners/create-program-application.ts (7)
8-11: LGTM! Necessary imports for webhook integration.All new imports support the webhook functionality: formatting utilities, webhook publishing, schema validation, and type definitions.
Also applies to: 17-18, 27-27
136-141: LGTM! Workspace data included for webhook emission.The program query now includes the workspace data necessary for webhook publishing, selecting only the required fields.
171-177: LGTM! Function call updated with workspace parameter.The function call correctly passes the workspace data to
createApplicationAndEnrollment.
197-209: LGTM! Function signature properly updated with new parameters.The function signature correctly includes the workspace parameter and updates the partner type to include the programs array, which is necessary for the enrollment check and webhook payload construction.
218-242: LGTM! Promise.all updated to include program enrollment.The Promise.all correctly creates both the application and program enrollment records, with proper destructuring to capture both results.
246-251: LGTM! Application form data properly formatted.The application form data is correctly transformed to the label/value structure needed for webhook payloads, with empty strings converted to
null.
273-287: LGTM! Webhook emission with proper validation.The webhook payload is correctly constructed and validated against the schema before emission. The workspace is guaranteed to exist from the program query (Lines 136-141), so no additional conditional check is needed here.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
Summary by CodeRabbit
New Features
UI
Tests