-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Partner group filters #3071
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
Partner group filters #3071
Conversation
…d discount data retrieval, and include program enrollment in link transformations. Update related functions to accommodate new data structure.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds programEnrollment.groupId propagation across link workflows: new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as Link Create/Update API
participant Enrollment as Enrollment Service
participant DB as Database
participant Tinybird as Tinybird
Client->>API: Create/Update link
API->>Enrollment: getPartnerEnrollmentInfo(programId, partnerId)
Enrollment->>DB: select programEnrollment.groupId, partner, discount
DB-->>Enrollment: { groupId, partner, discount }
Enrollment-->>API: { group, partner, discount }
API->>DB: create/update link (includeProgramEnrollment)
DB-->>API: link record (includes programEnrollment.groupId)
API->>Tinybird: recordLink(link, { partner_group_id: groupId })
Tinybird-->>API: ack
API-->>Client: success
sequenceDiagram
participant Admin
participant GroupAPI as Group DELETE API
participant DB as Database
participant QStash as QStash
participant Cache as LinkCache
participant Tinybird as Tinybird
Admin->>GroupAPI: DELETE /groups/:id
GroupAPI->>DB: fetch partnerLinks (includeTags + includeProgramEnrollment)
DB-->>GroupAPI: partnerLinks[]
rect rgba(220,235,255,0.6)
GroupAPI->>QStash: publish remap-default-links (await in batch)
GroupAPI->>QStash: publish remap-discount-codes
GroupAPI->>DB: delete group & related rows
GroupAPI->>Cache: expireMany(partnerLinks)
GroupAPI->>Tinybird: recordLink(partnerLinks, { deleted: true })
end
Tinybird-->>GroupAPI: recorded
GroupAPI-->>Admin: success
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 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 (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
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 |
…ting various scripts to include program enrollment alongside existing tags.
…handling of asynchronous operations.
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: 8
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (38)
apps/web/app/(ee)/api/admin/delete-partner-account/route.ts(3 hunks)apps/web/app/(ee)/api/cron/cleanup/e2e-tests/route.ts(2 hunks)apps/web/app/(ee)/api/cron/domains/delete/route.ts(2 hunks)apps/web/app/(ee)/api/cron/domains/update/route.ts(2 hunks)apps/web/app/(ee)/api/cron/folders/delete/route.ts(2 hunks)apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts(2 hunks)apps/web/app/(ee)/api/groups/[groupIdOrSlug]/partners/route.ts(2 hunks)apps/web/app/(ee)/api/groups/[groupIdOrSlug]/route.ts(2 hunks)apps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.ts(2 hunks)apps/web/app/api/links/bulk/route.ts(2 hunks)apps/web/app/api/tags/[id]/route.ts(2 hunks)apps/web/app/api/webhooks/route.ts(1 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx(2 hunks)apps/web/lib/actions/partners/revoke-program-invite.ts(3 hunks)apps/web/lib/actions/partners/update-partner-enrollment.ts(3 hunks)apps/web/lib/analytics/constants.ts(4 hunks)apps/web/lib/analytics/get-analytics.ts(2 hunks)apps/web/lib/api/links/bulk-create-links.ts(3 hunks)apps/web/lib/api/links/bulk-update-links.ts(2 hunks)apps/web/lib/api/links/complete-ab-tests.ts(2 hunks)apps/web/lib/api/links/create-link.ts(3 hunks)apps/web/lib/api/links/delete-link.ts(2 hunks)apps/web/lib/api/links/include-program-enrollment.ts(1 hunks)apps/web/lib/api/links/update-link.ts(2 hunks)apps/web/lib/api/links/utils/transform-link.ts(3 hunks)apps/web/lib/middleware/link.ts(2 hunks)apps/web/lib/planetscale/getPartnerEnrollmentInfo.ts(5 hunks)apps/web/lib/tinybird/record-link.ts(2 hunks)apps/web/lib/zod/schemas/analytics-response.ts(1 hunks)apps/web/lib/zod/schemas/analytics.ts(2 hunks)apps/web/scripts/bulk-delete-links.ts(2 hunks)apps/web/scripts/bulk-update-links.ts(2 hunks)apps/web/scripts/migrations/backfill-link-partner-group-ids.ts(1 hunks)apps/web/scripts/migrations/backfill-partner-links.ts(2 hunks)apps/web/scripts/move-links-to-folder.ts(2 hunks)apps/web/scripts/partners/merge-partner-profile.ts(2 hunks)apps/web/scripts/perplexity/backfill-tenantids.ts(1 hunks)apps/web/ui/analytics/use-analytics-filters.tsx(4 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:254-254
Timestamp: 2025-05-29T04:49:42.842Z
Learning: In the Dub codebase, it's acceptable to keep `partners.dub.co` hardcoded rather than making it configurable for different environments.
📚 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/app/(ee)/api/cron/cleanup/e2e-tests/route.tsapps/web/lib/api/links/complete-ab-tests.tsapps/web/scripts/bulk-update-links.tsapps/web/scripts/perplexity/backfill-tenantids.tsapps/web/app/(ee)/api/cron/merge-partner-accounts/route.tsapps/web/app/(ee)/api/cron/folders/delete/route.tsapps/web/lib/api/links/delete-link.tsapps/web/app/api/links/bulk/route.tsapps/web/lib/api/links/update-link.tsapps/web/scripts/migrations/backfill-link-partner-group-ids.tsapps/web/lib/api/links/utils/transform-link.tsapps/web/app/(ee)/api/groups/[groupIdOrSlug]/partners/route.tsapps/web/lib/actions/partners/revoke-program-invite.tsapps/web/scripts/move-links-to-folder.tsapps/web/app/(ee)/api/cron/domains/update/route.tsapps/web/lib/api/links/create-link.tsapps/web/scripts/bulk-delete-links.tsapps/web/app/(ee)/api/groups/[groupIdOrSlug]/route.tsapps/web/app/(ee)/api/admin/delete-partner-account/route.tsapps/web/lib/api/links/bulk-update-links.tsapps/web/lib/api/links/bulk-create-links.ts
📚 Learning: 2025-10-28T19:17:44.390Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2985
File: apps/web/app/(ee)/api/network/programs/[programSlug]/route.ts:32-37
Timestamp: 2025-10-28T19:17:44.390Z
Learning: In Prisma queries, the `include` clause is only used for relationships (one-to-one, one-to-many, many-to-many). Regular scalar fields, JSON fields, and other non-relational columns are automatically included in the query result and do not need to be specified in the `include` object.
Applied to files:
apps/web/app/(ee)/api/cron/cleanup/e2e-tests/route.tsapps/web/lib/api/links/complete-ab-tests.tsapps/web/scripts/bulk-update-links.tsapps/web/scripts/migrations/backfill-partner-links.tsapps/web/app/(ee)/api/cron/folders/delete/route.tsapps/web/app/api/links/bulk/route.tsapps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.tsapps/web/lib/actions/partners/update-partner-enrollment.tsapps/web/app/api/tags/[id]/route.tsapps/web/scripts/partners/merge-partner-profile.tsapps/web/lib/actions/partners/revoke-program-invite.tsapps/web/app/(ee)/api/cron/domains/update/route.tsapps/web/scripts/bulk-delete-links.tsapps/web/lib/api/links/bulk-update-links.tsapps/web/app/(ee)/api/cron/domains/delete/route.tsapps/web/lib/api/links/bulk-create-links.tsapps/web/lib/api/links/include-program-enrollment.ts
📚 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/app/(ee)/api/cron/merge-partner-accounts/route.tsapps/web/lib/actions/partners/update-partner-enrollment.tsapps/web/lib/actions/partners/revoke-program-invite.tsapps/web/app/(ee)/api/admin/delete-partner-account/route.ts
📚 Learning: 2025-06-16T19:21:23.506Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
Applied to files:
apps/web/ui/analytics/use-analytics-filters.tsxapps/web/lib/analytics/constants.tsapps/web/lib/analytics/get-analytics.ts
📚 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/tinybird/record-link.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/analytics/constants.ts
📚 Learning: 2025-10-15T01:05:43.266Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx:432-457
Timestamp: 2025-10-15T01:05:43.266Z
Learning: In apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx, defer refactoring the custom MenuItem component (lines 432-457) to use the shared dub/ui MenuItem component to a future PR, as requested by steven-tey.
Applied to files:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx
📚 Learning: 2025-09-24T16:10:37.349Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/ui/partners/partner-about.tsx:11-11
Timestamp: 2025-09-24T16:10:37.349Z
Learning: In the Dub codebase, the team prefers to import Icon as a runtime value from "dub/ui" and uses Icon as both a type and variable name in component props, even when this creates shadowing. This is their established pattern and should not be suggested for refactoring.
Applied to files:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx
📚 Learning: 2025-08-14T05:57:35.546Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/lib/actions/partners/update-discount.ts:60-66
Timestamp: 2025-08-14T05:57:35.546Z
Learning: In the partner groups system, discounts should always belong to a group. The partnerGroup relation should never be null when updating discounts, so optional chaining on partnerGroup?.id may be unnecessary defensive programming.
Applied to files:
apps/web/lib/planetscale/getPartnerEnrollmentInfo.ts
🧬 Code graph analysis (32)
apps/web/app/(ee)/api/cron/cleanup/e2e-tests/route.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/lib/api/links/complete-ab-tests.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/scripts/bulk-update-links.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/scripts/perplexity/backfill-tenantids.ts (1)
apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/scripts/migrations/backfill-partner-links.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/app/(ee)/api/cron/folders/delete/route.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/lib/api/links/delete-link.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/app/api/links/bulk/route.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/lib/api/links/update-link.ts (3)
apps/web/lib/planetscale/getPartnerEnrollmentInfo.ts (1)
getPartnerEnrollmentInfo(17-82)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)apps/web/lib/storage.ts (2)
isNotHostedImage(257-259)storage(251-251)
apps/web/ui/analytics/use-analytics-filters.tsx (1)
apps/web/ui/analytics/utils.ts (1)
useAnalyticsFilterOption(22-75)
apps/web/lib/actions/partners/update-partner-enrollment.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/app/api/tags/[id]/route.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/scripts/migrations/backfill-link-partner-group-ids.ts (3)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/lib/middleware/link.ts (1)
apps/web/lib/planetscale/getPartnerEnrollmentInfo.ts (1)
getPartnerEnrollmentInfo(17-82)
apps/web/scripts/partners/merge-partner-profile.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/lib/api/links/utils/transform-link.ts (1)
apps/web/lib/types.ts (1)
ProgramEnrollmentProps(483-483)
apps/web/app/(ee)/api/groups/[groupIdOrSlug]/partners/route.ts (4)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)apps/web/lib/api/bounties/trigger-draft-bounty-submissions.ts (1)
triggerDraftBountySubmissionCreation(8-93)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/lib/actions/partners/revoke-program-invite.ts (2)
apps/web/lib/api/links/utils/transform-link.ts (1)
ExpandedLink(12-22)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/scripts/move-links-to-folder.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/app/(ee)/api/cron/domains/update/route.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/lib/api/links/create-link.ts (4)
apps/web/lib/planetscale/getPartnerEnrollmentInfo.ts (1)
getPartnerEnrollmentInfo(17-82)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)apps/web/lib/storage.ts (2)
isNotHostedImage(257-259)storage(251-251)packages/prisma/index.ts (1)
prisma(3-9)
apps/web/scripts/bulk-delete-links.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/lib/analytics/get-analytics.ts (1)
packages/prisma/index.ts (1)
prisma(3-9)
apps/web/app/(ee)/api/groups/[groupIdOrSlug]/route.ts (5)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)apps/web/lib/api/discounts/queue-discount-code-deletion.ts (1)
queueDiscountCodeDeletion(13-37)apps/web/lib/api/audit-logs/record-audit-log.ts (1)
recordAuditLog(47-74)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/app/(ee)/api/admin/delete-partner-account/route.ts (1)
apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/lib/api/links/bulk-update-links.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/app/(ee)/api/cron/domains/delete/route.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx (1)
packages/ui/src/menu-item.tsx (1)
MenuItem(43-86)
apps/web/lib/api/links/bulk-create-links.ts (1)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)
apps/web/lib/api/links/include-program-enrollment.ts (1)
packages/prisma/client.ts (1)
Prisma(29-29)
⏰ 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 (47)
apps/web/app/api/webhooks/route.ts (1)
109-109: Good fix, but unrelated to PR objective.Adding
awaitensures the async IIFE properly waits for all operations (webhook toggling, email, cache updates) to complete before the function lifecycle ends. Without it,waitUntilreceives a promise that resolves immediately, potentially terminating the function before background operations finish.However, this change appears unrelated to the PR's stated objective of "Partner group filters." Consider moving this fix to a separate PR for clarity.
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx (1)
24-32: LGTM!The LinesY icon import is correctly added and used in the new "View analytics" menu item.
apps/web/lib/zod/schemas/analytics.ts (1)
217-220: Schema keeps filters in sync.Adding
groupIdhere keeps the validation surface aligned with the new Tinybird filters. Looks good.apps/web/ui/analytics/use-analytics-filters.tsx (1)
409-425: Group filter wiring looks solid.Thanks for slotting the new Group filter into the program dashboard with the existing
getFilterOptionTotalpattern andGroupColorCirclevisuals—nice, consistent integration.apps/web/lib/analytics/constants.ts (1)
84-138: Constants updated coherently.
VALID_ANALYTICS_ENDPOINTS,SINGULAR_ANALYTICS_ENDPOINTS, andVALID_ANALYTICS_FILTERSnow agree ontop_groups/groupId, so downstream consumers stay consistent. 👍apps/web/lib/zod/schemas/analytics-response.ts (1)
534-553: Response schema matches the new dimension.The
top_groupsobject mirrors the other aggregated dimensions and exposes the full group metadata the UI expects. Nicely done.apps/web/scripts/move-links-to-folder.ts (1)
47-50: LGTM! Program enrollment data included for tracking.The addition of
includeProgramEnrollmentis consistent with the PR's objective to track program enrollment group data. The fetched data is used byrecordLinkat line 55 for analytics tracking.apps/web/app/api/tags/[id]/route.ts (1)
76-77: LGTM! Consistent data enrichment for link tracking.The addition of
includeProgramEnrollmentalongsideincludeTagsensures complete link metadata is available when recording deletions to Tinybird (line 94).apps/web/lib/api/links/utils/transform-link.ts (2)
21-21: LGTM! Type extended to accept program enrollment data.The
ExpandedLinktype now acceptsprogramEnrollmentdata, which is used internally for analytics tracking but intentionally excluded from API responses (line 45).
45-45: LGTM! Program enrollment data excluded from API response.The
programEnrollmentfield is destructured and removed from the API response, keeping it internal-only for analytics purposes. This is appropriate for a draft PR preparing infrastructure for future group filtering features.apps/web/lib/api/links/delete-link.ts (1)
19-19: LGTM! Consistent inclusion of program enrollment data.The addition ensures program enrollment data is captured for Tinybird tracking when deleting links (line 35), while remaining hidden from the API response via
transformLink(line 51).apps/web/app/(ee)/api/cron/cleanup/e2e-tests/route.ts (1)
38-39: LGTM! Complete link data for E2E cleanup.Including both
includeTagsandincludeProgramEnrollmentensures complete link metadata is available for proper cleanup and tracking when removing E2E test data.apps/web/lib/actions/partners/update-partner-enrollment.ts (1)
61-61: LGTM! Enriched link data for enrollment updates.Including
includeProgramEnrollmentalongsideincludeTagsensures complete link metadata is captured when recording enrollment updates to Tinybird (line 69).apps/web/scripts/bulk-delete-links.ts (1)
14-17: LGTM! Complete metadata for bulk deletion tracking.The includes ensure full link metadata (tags and program enrollment) is captured for Tinybird tracking before bulk deletion (line 23).
apps/web/app/(ee)/api/stripe/webhook/customer-subscription-deleted.ts (1)
42-43: LGTM! Complete link data for subscription cancellation handling.Including
includeTagsandincludeProgramEnrollmentensures root domain links have complete metadata for cache invalidation (line 169) and Tinybird recording (line 172) during subscription cancellations.apps/web/lib/tinybird/record-link.ts (2)
29-32: LGTM!The
partner_group_idfield follows the established pattern for optional fields, using.nullish()with empty string transformation to align with Tinybird's schema expectations.
75-75: LGTM!The optional chaining with fallback correctly handles cases where
programEnrollmentmight not be loaded, maintaining consistency with the schema's empty string default.apps/web/scripts/bulk-update-links.ts (1)
1-1: LGTM!The addition of
includeProgramEnrollmentenriches the link data with group information, enabling accurate Tinybird recording. The spread pattern correctly merges both include objects.Also applies to: 37-40
apps/web/lib/api/links/bulk-update-links.ts (1)
9-10: LGTM!Refactoring to use shared
includeTagsandincludeProgramEnrollmentutilities improves maintainability and ensures consistent data loading across the codebase. The spread pattern correctly merges both include objects.Also applies to: 104-105
apps/web/scripts/perplexity/backfill-tenantids.ts (1)
87-92: LGTM!The manual enrichment correctly adds
programEnrollment.groupIdto each link before recording. This approach is appropriate since theprogramEnrollmentis already fetched at the parent level (line 68), avoiding redundant nested includes.apps/web/scripts/migrations/backfill-partner-links.ts (1)
1-1: LGTM!The addition of
includeProgramEnrollmentensures the migration script enriches links with group information before recording to Tinybird, maintaining consistency with the broader enrollment tracking changes.Also applies to: 19-22
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (1)
3-3: LGTM!The addition of
includeProgramEnrollmentensures that merged partner links carry group information during Tinybird recording, maintaining data consistency throughout the merge operation.Also applies to: 169-172
apps/web/app/(ee)/api/cron/domains/update/route.ts (1)
4-5: LGTM!Refactoring to use shared utilities improves consistency and enriches domain-updated links with group information for accurate Tinybird recording.
Also applies to: 72-73
apps/web/lib/actions/partners/revoke-program-invite.ts (2)
3-3: LGTM! Excellent inline documentation.The refactor to use
includeTagsmaintains consistency, and the inline comment clearly explains whyincludeProgramEnrollmentisn't needed here—preventing redundant nested includes since theprogramEnrollmentis already fetched at the parent level.Also applies to: 5-5, 35-41
77-82: LGTM!The manual enrichment correctly constructs
ExpandedLinkobjects withprogramEnrollment.groupIdfrom the parent enrollment, ensuring Tinybird receives complete group information for deleted links.Also applies to: 90-90
apps/web/app/api/links/bulk/route.ts (2)
10-11: LGTM: Imports added for program enrollment tracking.The imports align with the PR's objective to add program enrollment group tracking across link operations.
512-513: LGTM: Enhanced link data retrieval for deletion.The spread pattern correctly includes both tags and program enrollment data, ensuring deleted links have sufficient context for downstream operations like analytics and audit trails.
apps/web/app/(ee)/api/cron/folders/delete/route.ts (2)
3-3: LGTM: Import added for program enrollment tracking.
35-38: LGTM: Enriched link data for folder deletion.The merged include ensures that when links are disassociated from folders and recorded to Tinybird (line 51), they include program enrollment context for accurate analytics.
apps/web/lib/api/links/complete-ab-tests.ts (2)
9-9: LGTM: Import added for program enrollment tracking.
70-70: LGTM: A/B test completion now includes enrollment data.Including program enrollment ensures that when the winning URL is selected and the link is updated, downstream operations (recordLink at line 80 and webhooks at line 82-87) have access to group context for analytics and notifications.
apps/web/app/(ee)/api/cron/domains/delete/route.ts (2)
4-5: LGTM: Imports added for enriched link data.
43-44: LGTM: Domain deletion now tracks program enrollment.The enriched link data ensures that batch deletions maintain accurate analytics records with program enrollment context when links are recorded to Tinybird (line 63).
apps/web/lib/api/links/include-program-enrollment.ts (1)
1-9: LGTM: Clean utility for program enrollment inclusion.The helper provides a consistent, type-safe pattern for including program enrollment data across link queries. Selecting only
groupIdminimizes the performance impact while providing necessary context for group-level analytics and filtering.apps/web/app/(ee)/api/admin/delete-partner-account/route.ts (2)
21-21: LGTM: Extended program selection to include groupId.
36-36: LGTM: Deleted links now recorded with program enrollment context.The code correctly enriches each link with its
programEnrollment.groupIdbefore recording deletions to Tinybird. This ensures group-level analytics remain accurate even for deleted links. Note thatgroupIdmay be null for partners not assigned to a group, which is acceptable here.Also applies to: 46-52
apps/web/lib/middleware/link.ts (2)
34-34: LGTM: Refactored to use enriched enrollment function.The import change aligns with the PR's refactoring of enrollment retrieval via
getPartnerEnrollmentInfo.
124-127: LGTM: Function call updated with backward compatibility.The refactored
getPartnerEnrollmentInfomaintains the same{ partner, discount }destructuring pattern, ensuring backward compatibility while enabling future use of the additionalgroupdata returned by the new function.apps/web/app/(ee)/api/groups/[groupIdOrSlug]/partners/route.ts (2)
4-5: LGTM: Imports added for enriched partner link handling.Also applies to: 10-10
63-106: LGTM: Enhanced workflow with enriched partner link data.The async IIFE pattern correctly coordinates the workflow:
- Fetches partner links with tags and program enrollment data
- Queues remap operations for default links and discount codes
- Triggers draft bounty submission creation
- Records partner links to Tinybird with enrollment context
The
Promise.allSettledensures all operations complete independently, and the enriched link data enables accurate group-level analytics. The added complexity is justified by the need to await link data before proceeding with dependent operations.apps/web/scripts/partners/merge-partner-profile.ts (1)
68-71: Thanks for stitching program enrollment into the merge script
Including...includeProgramEnrollmentkeeps the Tinybird replay in sync with group metadata. Looks good.apps/web/lib/api/links/bulk-create-links.ts (1)
89-92: Enrollment include fits the bulk flow
PullingprogramEnrollmentalongside the bulk-created links keeps later transforms and analytics consistent. No concerns here.apps/web/app/(ee)/api/groups/[groupIdOrSlug]/route.ts (1)
343-395: Good call on recording partner links before cleanup
The added fetch withincludeProgramEnrollmentensures Tinybird receives the updated group signal when a group is deleted. This aligns with the broader enrollment tracking work.apps/web/lib/planetscale/getPartnerEnrollmentInfo.ts (4)
13-13: LGTM: QueryResult interface extended correctly.The nullable
groupIdfield is appropriately typed for the LEFT JOIN query.
16-17: LGTM: Function name and comment updated appropriately.The rename to
getPartnerEnrollmentInfobetter reflects the expanded scope of returning partner, group, and discount information.
42-43: LGTM: SELECT clause extended correctly.Both
couponTestIdandgroupIdare properly retrieved and align with the QueryResult interface.
24-30: Early return logic is correct.The
group: nullreturns are appropriate for missing inputs (lines 24-30) and no-result scenarios (lines 54-60), and will be consistent once the type inconsistency at lines 68-70 is resolved.Also applies to: 54-60
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx
Show resolved
Hide resolved
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: 2
♻️ Duplicate comments (5)
apps/web/lib/api/links/update-link.ts (1)
180-184: Cache still missing groupId for partner group filters.The cache operation spreads
partneranddiscountbut omitsgroup/programEnrollment, while Tinybird recording (lines 187-190) correctly includes it. This inconsistency means cache-hit responses won't reflect the partner's group, causing group filters to return stale data until the cache misses.Apply this diff to align with the Tinybird flow:
linkCache.set({ ...response, ...(partner && { partner }), ...(discount && { discount }), + ...(group && { programEnrollment: { groupId: group.id } }), }),apps/web/lib/planetscale/get-partner-enrollment-info.ts (1)
68-70: Type inconsistency remains: group should be null when groupId is null.The past review comment on these lines is still applicable. When
result.groupIdisnull, this returnsgroup: { id: null }, but lines 27 and 57 returngroup: null. This inconsistency forces consumers to handle both!groupand!group?.idchecks.Apply this diff to fix:
- group: { - id: result.groupId, - }, + group: result.groupId ? { id: result.groupId } : null,apps/web/lib/api/links/create-link.ts (1)
151-155: Redis cache still missing group data - duplicate of previous review.The past review comment on lines 151-161 remains valid. The Redis cache payload includes
partneranddiscountbut omitsgroup/programEnrollment, while the Tinybird payload (lines 158-161) correctly includesprogramEnrollment.groupId. This inconsistency means cached links won't have group data for analytics filters.Apply the suggested diff from the previous review:
linkCache.set({ ...response, ...(partner && { partner }), ...(discount && { discount }), + ...(group && { programEnrollment: { groupId: group.id } }), }),apps/web/scripts/migrations/backfill-link-partner-group-ids.ts (2)
15-60: Add batch-level error handling before running this migration in prod.A single throw from
prisma.link.findManyorrecordLinkwill crash the script with no cursor checkpoint, forcing a full restart and risking duplicate Tinybird writes. Wrap each batch in atry/catch, log the cursor you reached, and surface the failure so you can resume safely. See the diff below.- while (true) { - // Find the program links - const links = await prisma.link.findMany({ - where: { - programId: { - not: null, - }, - partnerId: { - not: null, - }, - createdAt: { - lte: PR_MERGE_TIMESTAMP, - }, - }, - include: { - ...includeTags, - ...includeProgramEnrollment, - }, - orderBy: { - id: "asc", - }, - take: LINKS_PER_BATCH, - ...(cursor - ? { - cursor: { id: cursor }, - skip: 1, - } - : {}), - }); - - console.log(`Found ${links.length} links to process.`); - - if (links.length === 0) { - break; - } - - cursor = links[links.length - 1].id; - - const { successful_rows, quarantined_rows } = await recordLink(links); - - if (successful_rows !== links.length) { - console.log(`Failed to record ${links.length - successful_rows} links.`); - } - - if (quarantined_rows !== 0) { - console.log(`Quarantined ${quarantined_rows} links.`); - } - } + while (true) { + try { + const links = await prisma.link.findMany({ + where: { + programId: { + not: null, + }, + partnerId: { + not: null, + }, + createdAt: { + lte: PR_MERGE_TIMESTAMP, + }, + }, + include: { + ...includeTags, + ...includeProgramEnrollment, + }, + orderBy: { + id: "asc", + }, + take: LINKS_PER_BATCH, + ...(cursor + ? { + cursor: { id: cursor }, + skip: 1, + } + : {}), + }); + + console.log( + `Found ${links.length} links to process (cursor: ${cursor ?? "start"}).`, + ); + + if (links.length === 0) { + break; + } + + cursor = links[links.length - 1].id; + + const { successful_rows, quarantined_rows } = await recordLink(links); + + if (successful_rows !== links.length) { + console.error( + `Failed to record ${links.length - successful_rows} links (cursor: ${cursor}).`, + ); + } + + if (quarantined_rows !== 0) { + console.warn( + `Quarantined ${quarantined_rows} links (cursor: ${cursor}).`, + ); + } + } catch (error) { + console.error( + `Error processing batch at cursor ${cursor ?? "start"}:`, + error, + ); + console.log(`Resume from cursor: ${cursor}`); + throw error; + } + }
63-63: Catch failures frommain()and close the Prisma pool.Right now a rejection from
main()becomes an unhandled promise rejection and Prisma keeps the process alive. Please attach.catch/.finallyso failures surface and the client disconnects cleanly.-main(); +main() + .catch((error) => { + console.error("Migration failed:", error); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + });
🧹 Nitpick comments (3)
apps/web/scripts/migrations/backfill-banned-partner-links.ts (2)
58-58: Add error handling for the migration script.The script executes
main()without a try/catch wrapper, which means unhandled errors will crash the process without proper logging or cleanup.Apply this diff to add error handling:
-main(); +main() + .then(() => { + console.log('Migration completed successfully'); + process.exit(0); + }) + .catch((error) => { + console.error('Migration failed:', error); + process.exit(1); + });
9-56: Consider adding start log and total counter for better observability.The script logs batch progress but lacks an initial start message and running total, which would help track overall progress during long migrations.
Add logging at the start and track cumulative progress:
async function main() { + console.log('Starting backfill of banned partner links...'); + let totalProcessed = 0; let cursor: string | undefined = undefined; while (true) { // Find links for partners that were banned const links = await prisma.link.findMany({ // ... query config }); - console.log(`Found ${links.length} links to process.`); + totalProcessed += links.length; + console.log(`Found ${links.length} links in batch. Total processed: ${totalProcessed}`); if (links.length === 0) { + console.log(`Backfill complete. Total links processed: ${totalProcessed}`); break; } // ... rest of processing } }apps/web/lib/api/links/create-link.ts (1)
149-214: Consider logging failures in critical operations.
Promise.allSettledprovides resilience by preventing cascading failures, but critical operations like caching and Tinybird recording may fail silently. Consider adding error logging to track which operations fail.Example approach:
- await Promise.allSettled([ + const results = await Promise.allSettled([ // ... all operations ]); + + // Log any critical failures + if (results[0].status === 'rejected') { + console.error('Failed to cache link:', results[0].reason); + } + if (results[1].status === 'rejected') { + console.error('Failed to record link in Tinybird:', results[1].reason); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/web/lib/actions/partners/ban-partner.ts(3 hunks)apps/web/lib/actions/partners/bulk-ban-partners.ts(3 hunks)apps/web/lib/actions/partners/unban-partner.ts(3 hunks)apps/web/lib/api/links/create-link.ts(3 hunks)apps/web/lib/api/links/update-link.ts(2 hunks)apps/web/lib/middleware/link.ts(2 hunks)apps/web/lib/planetscale/get-partner-enrollment-info.ts(5 hunks)apps/web/scripts/migrations/backfill-banned-partner-links.ts(1 hunks)apps/web/scripts/migrations/backfill-link-partner-group-ids.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/lib/middleware/link.ts
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:254-254
Timestamp: 2025-05-29T04:49:42.842Z
Learning: In the Dub codebase, it's acceptable to keep `partners.dub.co` hardcoded rather than making it configurable for different environments.
📚 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/actions/partners/bulk-ban-partners.tsapps/web/scripts/migrations/backfill-link-partner-group-ids.tsapps/web/lib/api/links/update-link.tsapps/web/scripts/migrations/backfill-banned-partner-links.tsapps/web/lib/actions/partners/ban-partner.tsapps/web/lib/api/links/create-link.tsapps/web/lib/actions/partners/unban-partner.ts
📚 Learning: 2025-10-06T15:48:14.205Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.205Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.
Applied to files:
apps/web/lib/actions/partners/bulk-ban-partners.tsapps/web/lib/actions/partners/ban-partner.ts
📚 Learning: 2025-08-14T05:57:35.546Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/lib/actions/partners/update-discount.ts:60-66
Timestamp: 2025-08-14T05:57:35.546Z
Learning: In the partner groups system, discounts should always belong to a group. The partnerGroup relation should never be null when updating discounts, so optional chaining on partnerGroup?.id may be unnecessary defensive programming.
Applied to files:
apps/web/lib/api/links/update-link.tsapps/web/lib/planetscale/get-partner-enrollment-info.tsapps/web/lib/api/links/create-link.ts
📚 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/actions/partners/ban-partner.tsapps/web/lib/api/links/create-link.tsapps/web/lib/actions/partners/unban-partner.ts
📚 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/planetscale/get-partner-enrollment-info.ts
📚 Learning: 2025-07-30T15:29:54.131Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.
Applied to files:
apps/web/lib/planetscale/get-partner-enrollment-info.ts
🧬 Code graph analysis (7)
apps/web/lib/actions/partners/bulk-ban-partners.ts (2)
apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)apps/web/lib/api/discounts/queue-discount-code-deletion.ts (1)
queueDiscountCodeDeletion(13-37)
apps/web/scripts/migrations/backfill-link-partner-group-ids.ts (3)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/lib/api/links/update-link.ts (3)
apps/web/lib/planetscale/get-partner-enrollment-info.ts (1)
getPartnerEnrollmentInfo(17-82)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)apps/web/lib/storage.ts (2)
isNotHostedImage(257-259)storage(251-251)
apps/web/scripts/migrations/backfill-banned-partner-links.ts (3)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/lib/actions/partners/ban-partner.ts (1)
apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/lib/api/links/create-link.ts (4)
apps/web/lib/planetscale/get-partner-enrollment-info.ts (1)
getPartnerEnrollmentInfo(17-82)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)apps/web/lib/storage.ts (2)
isNotHostedImage(257-259)storage(251-251)packages/prisma/index.ts (1)
prisma(3-9)
apps/web/lib/actions/partners/unban-partner.ts (2)
apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
⏰ 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 (10)
apps/web/lib/api/links/update-link.ts (3)
1-1: Good optimization with clear documentation.Importing
getPartnerEnrollmentInfoand using a raw SQL query instead of Prisma'sincludeProgramEnrollmentis more efficient. The comment at line 166 clearly explains this decision.Also applies to: 166-166
172-176: Well-structured async IIFE pattern.Wrapping the side effects in an async IIFE to fetch enrollment info before dispatching parallel operations is the right approach, since both cache and Tinybird recording need this data.
192-225: Remaining side effects are correctly structured.The conditional operations for cache invalidation, image management, webhook propagation, and AB test scheduling all have appropriate guards and are correctly parallelized.
apps/web/lib/api/links/create-link.ts (4)
2-2: LGTM: Import updated to use new enrollment info function.The import correctly reflects the function rename from
getPartnerAndDiscounttogetPartnerEnrollmentInfo, which now provides group data in addition to partner and discount information.
134-134: LGTM: Clear explanation of data fetching strategy.The comment appropriately explains why
includeProgramEnrollmentis omitted from the initial query, since enrollment data is retrieved viagetPartnerEnrollmentInfoin the subsequentwaitUntilblock.
143-147: LGTM: Enrollment info fetched before side effects.The refactor correctly wraps side effects in an async IIFE, allowing
getPartnerEnrollmentInfoto be awaited before downstream operations. This ensures enrollment data (partner, discount, group) is available for both caching and analytics recording.
158-161: LGTM: Tinybird payload correctly includes group data.The
recordLinkcall properly includesprogramEnrollment.groupIdwhengroupexists, enabling group-level analytics filtering as intended by this PR.apps/web/lib/actions/partners/ban-partner.ts (2)
9-9: LGTM!The import is correctly added and used later in the file to record link deletions in Tinybird.
54-54: LGTM!Setting both
disabledAtandexpiresAtis appropriate for ban operations. The fields serve complementary purposes:disabledAtindicates administrative disabling, whileexpiresAtensures time-based expiration mechanisms also recognize the link as inactive.apps/web/lib/actions/partners/unban-partner.ts (1)
117-131: Great to sync Tinybird after unban.Hooking
recordLink(links)into the post-unban side-effects keeps partner-group metadata in Tinybird aligned with the re-enabled links, preventing stale analytics.
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
♻️ Duplicate comments (3)
apps/web/scripts/migrations/backfill-link-partner-group-ids.ts (2)
10-74: Add error handling and progress tracking for resilience.The migration still lacks error handling and progress tracking, as previously flagged. Any failure will crash the script and lose all progress.
76-76: Handle promise rejection from main().The
main()function is still invoked without error handling, as previously flagged.apps/web/lib/actions/partners/ban-partner.ts (1)
37-39: AddincludeProgramEnrollmentto load groupId for Tinybird recording.The links include is missing
includeProgramEnrollment, which is essential for this PR's groupId propagation feature. WhenrecordLinkis called at line 132, it invokestransformLinkTBwhich accesseslink.programEnrollment?.groupIdto populate thepartner_group_idfield in Tinybird. Without loading this relation,programEnrollmentwill beundefinedandpartner_group_idwill be recorded as an empty string, breaking the group filtering analytics.Apply this diff to include the program enrollment data:
links: { - include: includeTags, + include: { ...includeTags, ...includeProgramEnrollment }, },Note: This assumes
includeProgramEnrollmentis exported from the appropriate module. If not already imported, add:import { includeProgramEnrollment } from "@/lib/api/links/include-program-enrollment";
🧹 Nitpick comments (1)
apps/web/scripts/migrations/backfill-link-partner-group-ids.ts (1)
74-74: Add Prisma cleanup on script completion.The script should explicitly disconnect the Prisma client to prevent connection leaks.
Add cleanup after the main function completes (when implementing the error handling suggested above):
main() .catch((error) => { console.error("Migration failed:", error); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/lib/actions/partners/ban-partner.ts(4 hunks)apps/web/scripts/migrations/backfill-banned-partner-links.ts(1 hunks)apps/web/scripts/migrations/backfill-link-partner-group-ids.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/scripts/migrations/backfill-banned-partner-links.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:254-254
Timestamp: 2025-05-29T04:49:42.842Z
Learning: In the Dub codebase, it's acceptable to keep `partners.dub.co` hardcoded rather than making it configurable for different environments.
📚 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/actions/partners/ban-partner.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/actions/partners/ban-partner.tsapps/web/scripts/migrations/backfill-link-partner-group-ids.ts
📚 Learning: 2025-10-06T15:48:14.205Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.205Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.
Applied to files:
apps/web/lib/actions/partners/ban-partner.ts
📚 Learning: 2025-10-28T19:17:44.390Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2985
File: apps/web/app/(ee)/api/network/programs/[programSlug]/route.ts:32-37
Timestamp: 2025-10-28T19:17:44.390Z
Learning: In Prisma queries, the `include` clause is only used for relationships (one-to-one, one-to-many, many-to-many). Regular scalar fields, JSON fields, and other non-relational columns are automatically included in the query result and do not need to be specified in the `include` object.
Applied to files:
apps/web/lib/actions/partners/ban-partner.ts
🧬 Code graph analysis (2)
apps/web/lib/actions/partners/ban-partner.ts (1)
apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
apps/web/scripts/migrations/backfill-link-partner-group-ids.ts (3)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/api/links/include-program-enrollment.ts (1)
includeProgramEnrollment(3-9)apps/web/lib/tinybird/record-link.ts (1)
recordLink(81-98)
⏰ 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 (3)
apps/web/lib/actions/partners/ban-partner.ts (3)
6-6: LGTM: Imports added for new functionality.The imports for
includeTagsandrecordLinkare correctly added to support the link enrichment and Tinybird recording features introduced in this PR.Also applies to: 10-10
57-57: LGTM: Explicitly marking links as disabled on ban.Setting
disabledAtin addition toexpiresAtprovides clearer intent when a partner is banned and improves consistency for queries that filter on disabled links.
128-132: LGTM: Cache expiry and Tinybird recording improve data consistency.Good additions:
linkCache.expireMany(links)ensures banned partner links are removed from cacherecordLink(links, { deleted: true })maintains analytics consistency by recording link deletionsBoth operations are properly parallelized with
Promise.allSettled. However, the effectiveness of the Tinybird recording for group analytics depends on fixing the missingincludeProgramEnrollmentin the query (see comment on lines 37-39).
Cherry-picked upstream commits: - 45a227d: Remove accessLevel from top_folders analytics schema - 6d60f1b: Partner group filters (dubinc#3071) - adds top_groups groupBy - 6ef200f: Hide group cards dynamically, transformLink import fix Changes include: - Add groupId filter and top_groups analytics endpoint - Add includeProgramEnrollment for link queries with group context - Update partner UI to hide group info for banned/deactivated partners - Fix transformLink import path in get-events.ts Conflicts resolved: - Kept existing storage.upload() positional API (upstream uses object syntax) - Preserved sortRewardsByEventOrder in partner-info-cards.tsx
Summary by CodeRabbit
New Features
Improvements