Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Sep 24, 2025

Summary by CodeRabbit

  • Improvements

    • Imported partner links now correctly associate with their group’s default link across FirstPromoter, PartnerStack, and Rewardful.
    • The first imported link is auto-tied to the group’s default link; additional links are created without that association.
    • More reliable grouping and attribution for analytics and routing during imports, with safer handling when group data is missing.
  • Bug Fixes

    • Resolved inconsistencies where imported links could be misattributed to default links.

@steven-tey steven-tey requested a review from devkiran September 24, 2025 01:08
@vercel
Copy link
Contributor

vercel bot commented Sep 24, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Sep 24, 2025 6:31pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

Walkthrough

Adds partnerGroupDefaultLink awareness to partner import flows (FirstPromoter, PartnerStack, Rewardful): program/group queries now include partnerGroupDefaultLinks; link creation propagates partnerGroupDefaultLinkId, typically assigning the first default link’s id to the first generated partner link and null to subsequent links. One function signature updated to include this field.

Changes

Cohort / File(s) Summary
FirstPromoter import
apps/web/lib/firstpromoter/import-partners.ts
Include partnerGroupDefaultLinks in program group fetch; widen group type; during link creation, set partnerGroupDefaultLinkId for the first campaign link (by index) and null for others.
PartnerStack import
apps/web/lib/partnerstack/import-links.ts
Load partnerGroupDefaultLinks with groups; map groups by id; include groupId in enrollment selection; when creating links per partner, pass partnerGroupDefaultLinkId by matching index to group default links; update createPartnerLink to accept and persist this field.
Rewardful import
apps/web/lib/rewardful/import-partners.ts
Fetch partnerGroupDefaultLinks for default group; extend defaultGroupAttributes with partnerGroupDefaultLinkId; during link creation, assign the first link this id and null thereafter; updated createPartnerAndLinks signature accordingly.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Src as Import Source (FirstPromoter/PartnerStack/Rewardful)
  participant Svc as Import Service
  participant DB as DB (Program, Groups, DefaultLinks, Partners, Links)

  Src->>Svc: Start partner import
  Svc->>DB: Fetch Program incl. groups.partnerGroupDefaultLinks
  DB-->>Svc: Program + Groups + DefaultLinks

  loop For each partner enrollment
    Svc->>Svc: Resolve partnerGroup (via groupId when available)
    alt Partner exists?
      Svc->>DB: Upsert/Create Partner
    else
      Svc->>DB: Create Partner
    end

    Note over Svc: Iterate external links with index i
    Svc->>DB: Create PartnerLink<br/>payload includes partnerGroupDefaultLinkId =<br/> i==0 ? defaultLinks[0]?.id : null
    DB-->>Svc: PartnerLink created
  end

  Svc-->>Src: Import complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • devkiran

Poem

A hop, a link, a carrot-think,
I map the first to default sync.
Groups aligned, IDs in tow,
The partner paths now neatly flow.
One nibble, two, the imports sing—
Default links crowned for the very first string. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly indicates that the pull request addresses an issue with importer handling of partnerGroupDefaultLinkId across relevant modules. It is concise and directly related to the main change described in the summary. There is no extraneous information or ambiguous phrasing.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-importer-pgdl

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

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/firstpromoter/import-partners.ts (1)

168-180: Guard against missing default link (runtime crash risk)

Indexing [0].id will throw when no default links exist.

Minimal fix:

-    partnerGroupDefaultLinkId:
-      idx === 0 ? group.partnerGroupDefaultLinks[0].id : null,
+    partnerGroupDefaultLinkId:
+      idx === 0 ? (group.partnerGroupDefaultLinks[0]?.id ?? null) : null,

Optional consistency (match PartnerStack behavior by indexing):

-    partnerGroupDefaultLinkId:
-      idx === 0 ? group.partnerGroupDefaultLinks[0].id : null,
+    partnerGroupDefaultLinkId:
+      group.partnerGroupDefaultLinks[idx]?.id ?? null,
🧹 Nitpick comments (5)
apps/web/lib/rewardful/import-partners.ts (2)

32-34: Limit relation payload: select only id

Fetch only the ids to reduce payload and GC pressure.

-        include: {
-          partnerGroupDefaultLinks: true,
-        },
+        include: {
+          partnerGroupDefaultLinks: { select: { id: true } },
+        },

188-201: Null‑safety is fine; consider consistency with PartnerStack mapping

You set PGDL only for the first link. PartnerStack maps by index (idx) across available default links. Consider aligning for consistency and future multi-default-link support.

apps/web/lib/firstpromoter/import-partners.ts (1)

23-27: Limit relation payload: select only id

Fetch only ids for partnerGroupDefaultLinks.

-      groups: {
-        include: {
-          partnerGroupDefaultLinks: true,
-        },
-      },
+      groups: {
+        include: {
+          partnerGroupDefaultLinks: { select: { id: true } },
+        },
+      },
apps/web/lib/partnerstack/import-links.ts (2)

19-23: Limit relation payload: select only id

Fetch only ids for partnerGroupDefaultLinks.

-      groups: {
-        include: {
-          partnerGroupDefaultLinks: true,
-        },
-      },
+      groups: {
+        include: {
+          partnerGroupDefaultLinks: { select: { id: true } },
+        },
+      },

88-89: Avoid non‑null assertion on groupId

groupId can be null; the assertion doesn’t help at runtime. Keep it explicit.

-      const partnerGroup = groupsById.get(groupId!);
+      const partnerGroup = groupId ? groupsById.get(groupId) : undefined;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fcd7d99 and eb9f3b8.

📒 Files selected for processing (3)
  • apps/web/lib/firstpromoter/import-partners.ts (5 hunks)
  • apps/web/lib/partnerstack/import-links.ts (7 hunks)
  • apps/web/lib/rewardful/import-partners.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
PR: dubinc/dub#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/firstpromoter/import-partners.ts
  • apps/web/lib/partnerstack/import-links.ts
  • apps/web/lib/rewardful/import-partners.ts
📚 Learning: 2025-08-14T05:00:23.224Z
Learnt from: devkiran
PR: dubinc/dub#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/firstpromoter/import-partners.ts
  • apps/web/lib/rewardful/import-partners.ts
📚 Learning: 2025-08-14T05:17:51.825Z
Learnt from: devkiran
PR: dubinc/dub#2735
File: apps/web/lib/actions/partners/delete-reward.ts:33-41
Timestamp: 2025-08-14T05:17:51.825Z
Learning: In the partner groups system, a rewardId can only belong to one group, establishing a one-to-one relationship between rewards and groups. This means using Prisma's `update` method (rather than `updateMany`) is appropriate when updating groups by rewardId.

Applied to files:

  • apps/web/lib/rewardful/import-partners.ts
🧬 Code graph analysis (2)
apps/web/lib/firstpromoter/import-partners.ts (1)
apps/web/lib/types.ts (1)
  • PartnerGroupDefaultLink (547-549)
apps/web/lib/partnerstack/import-links.ts (2)
apps/web/lib/types.ts (3)
  • WorkspaceProps (186-202)
  • ProgramProps (442-442)
  • PartnerProps (428-428)
apps/web/lib/partnerstack/types.ts (1)
  • PartnerStackLink (26-26)
🔇 Additional comments (8)
apps/web/lib/firstpromoter/import-partners.ts (2)

2-6: LGTM on type imports

Extending types to include PartnerGroupDefaultLink is appropriate.


102-102: LGTM on group type expansion

Annotating group with partnerGroupDefaultLinks[] ensures type safety downstream.

apps/web/lib/partnerstack/import-links.ts (6)

36-38: LGTM

groupsById map is a clean lookup for per-enrollment processing.


48-49: LGTM

Including groupId in enrollment selection is necessary for downstream PGDL mapping.


72-72: LGTM

Destructuring groupId alongside partner simplifies usage.


91-101: Robust PGDL mapping

Index-based mapping with optional chaining is safe and consistent.


127-136: LGTM

Extending createPartnerLink to accept partnerGroupDefaultLinkId is correct.


189-190: LGTM

Forwarding partnerGroupDefaultLinkId into generatePartnerLink ensures correct association at link creation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/lib/rewardful/import-partners.ts (2)

32-34: Limit loaded fields for default links to only id

You only use the id (and array length). Reduce payload and DB transfer.

Apply:

-        include: {
-          partnerGroupDefaultLinks: true,
-        },
+        include: {
+          partnerGroupDefaultLinks: {
+            select: { id: true },
+          },
+        },

190-202: Destructure partnerGroupDefaultLinkId and simplify mapping
bulkCreateLinks already accepts partnerGroupDefaultLinkId (ProcessedLinkProps) and the Prisma schema includes the column. Extract it once:

const { partnerGroupDefaultLinkId } = defaultGroupAttributes;

links: affiliate.links.map((link, idx) => ({,
  partnerGroupDefaultLinkId: idx === 0 ? partnerGroupDefaultLinkId : null,
})),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb9f3b8 and 2b87734.

📒 Files selected for processing (1)
  • apps/web/lib/rewardful/import-partners.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: devkiran
PR: dubinc/dub#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.
📚 Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
PR: dubinc/dub#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/rewardful/import-partners.ts
📚 Learning: 2025-08-14T05:57:35.546Z
Learnt from: devkiran
PR: dubinc/dub#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/rewardful/import-partners.ts
📚 Learning: 2025-08-16T11:14:00.667Z
Learnt from: devkiran
PR: dubinc/dub#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/rewardful/import-partners.ts
📚 Learning: 2025-08-14T05:17:51.825Z
Learnt from: devkiran
PR: dubinc/dub#2735
File: apps/web/lib/actions/partners/delete-reward.ts:33-41
Timestamp: 2025-08-14T05:17:51.825Z
Learning: In the partner groups system, a rewardId can only belong to one group, establishing a one-to-one relationship between rewards and groups. This means using Prisma's `update` method (rather than `updateMany`) is appropriate when updating groups by rewardId.

Applied to files:

  • apps/web/lib/rewardful/import-partners.ts
📚 Learning: 2025-08-14T05:00:23.224Z
Learnt from: devkiran
PR: dubinc/dub#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/rewardful/import-partners.ts
⏰ 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/rewardful/import-partners.ts (3)

91-94: Safe guard for missing default link addressed

The length check avoids the earlier crash when no default links exist.


39-41: Guard when provided groupId doesn’t belong to the program

If a bad groupId is passed, groups[0] will be undefined and subsequent property access will throw. Add a defensive check or fallback to the default group.

Suggested:

 const defaultGroup = program.groups[0];
+if (!defaultGroup) {
+  throw new Error(
+    `Group ${groupId ?? DEFAULT_PARTNER_GROUP.slug} not found for program ${program.id}`,
+  );
+}

142-143: Don’t pass partnerGroupDefaultLinkId into ProgramEnrollment.create (likely unknown column)

Adding partnerGroupDefaultLinkId to defaultGroupAttributes means it’s now spread into ProgramEnrollment.create. ProgramEnrollment doesn’t have this field (per prior review); Prisma will reject the create.

Apply:

 async function createPartnerAndLinks({
@@
   defaultGroupAttributes: {
     groupId: string;
     saleRewardId: string | null;
     leadRewardId: string | null;
     clickRewardId: string | null;
     discountId: string | null;
     partnerGroupDefaultLinkId: string | null;
   };
 }) {
+  // Exclude fields not present on ProgramEnrollment from the create payload
+  const {
+    partnerGroupDefaultLinkId,
+    ...enrollmentAttrs
+  } = defaultGroupAttributes;
@@
     create: {
       id: createId({ prefix: "pge_" }),
       programId: program.id,
       partnerId: partner.id,
       status: "approved",
-      ...defaultGroupAttributes,
+      ...enrollmentAttrs,
     },

Also applies to: 169-170

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.

3 participants