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

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Oct 31, 2025

Summary by CodeRabbit

  • New Features

    • Improved partner account transfer: bounty submissions are moved individually and total commissions are synchronized per program.
  • Bug Fixes

    • Wrapped user and partner deletion steps in error handlers to prevent interruptions and log failures.
    • Ensured related links, cache expiration, and notifications are updated after transfers.
  • Refactor

    • Added a utility to log promise results with success/failure counts for clearer operation visibility.

@vercel
Copy link
Contributor

vercel bot commented Oct 31, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Oct 31, 2025 6:29pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Performed per-submission transfer of bounty submissions using Promise.allSettled, added logPromiseResults utility, invoked syncTotalCommissions per transferred program for the target partner, and wrapped user/partner deletions in try/catch with error logging.

Changes

Cohort / File(s) Change Summary
Partner account merge endpoint
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts
Replaced bulk bountySubmission.updateMany with fetch + per-submission updates via Promise.allSettled; added dedicated bounty transfer flow; call syncTotalCommissions for target partner per transferred program; wrapped user and partner deletion blocks in try/catch and log errors; preserved tinybird link/cache update steps.
Promise utilities
packages/utils/src/functions/promises.ts
Added exported logPromiseResults<T>(results, options?) to summarize Promise.allSettled outcomes, log per-item messages with optional label/item ids, and return { successCount, failureCount }.
Manifest / metadata
manifest_file, package.json
Metadata files present in diff (no exported/public API signature changes).

Sequence Diagram(s)

sequenceDiagram
    participant Route as MergeAccounts Cron Route
    participant DB as Database
    participant Tiny as Tinybird/Cache
    participant Comms as syncTotalCommissions
    participant Logger as Logger
    participant PromAll as Promise.allSettled

    rect rgb(235,243,255)
    Note over Route: Update links / notifications / cache
    Route->>DB: updateMany (links/notifications)
    Route->>Tiny: update links / expire caches
    end

    rect rgb(220,255,230)
    Note over Route: Bounty submission transfer (per-item)
    Route->>DB: fetch bountySubmissions by sourcePartnerId & programIdsToTransfer
    Route->>PromAll: update each submission -> set partnerId = targetPartnerId
    PromAll->>DB: update each bountySubmission
    PromAll->>Logger: return settled results
    Logger->>Route: logPromiseResults(results)
    Route->>Comms: syncTotalCommissions(targetPartnerId, programId)  -- per program
    Comms-->>Route: ack
    end

    rect rgb(255,240,235)
    Note over Route: Deletions (non-fatal)
    Route->>Logger: try delete user if workspaceCount === 0
    alt error
        Logger->>Logger: log error (continue)
    end
    Route->>Logger: try delete partner
    alt error
        Logger->>Logger: log error (continue)
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Attention points:
    • Verify per-item updates preserve the same data invariants previously ensured by bulk updateMany (filters, race conditions).
    • Confirm correct program list and call frequency passed to syncTotalCommissions.
    • Ensure logPromiseResults formatting & indexing align with callers.
    • Review deletion error handling to confirm non-fatal behavior is acceptable.

Possibly related PRs

Poem

🐰
I hopped through promises, one by one,
Settled each tale till the transfer was done,
Commissions counted, programs in line,
Try-catch cradled deletions just fine,
I nibble a carrot — the logs look fine.

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 "Fix merge partner accounts" directly relates to the primary changes in this pull request, which focus on fixing and improving the merge partner accounts route. The changeset includes error handling improvements (try/catch blocks for user and partner deletion), enhanced bounty submission transfer logic, and commission synchronization. The title clearly indicates this is a fix to the merge partner accounts functionality without being vague or misleading, and it appropriately focuses on the main area of impact rather than attempting to enumerate every detail.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e600fef and e4af43e.

📒 Files selected for processing (1)
  • apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (3 hunks)

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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6784533 and eb2d679.

📒 Files selected for processing (2)
  • apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (2 hunks)
  • packages/utils/src/functions/promises.ts (1 hunks)
🧰 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/app/(ee)/api/cron/merge-partner-accounts/route.ts
📚 Learning: 2025-08-14T05:17:51.825Z
Learnt from: devkiran
Repo: dubinc/dub PR: 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/app/(ee)/api/cron/merge-partner-accounts/route.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/app/(ee)/api/cron/merge-partner-accounts/route.ts
🧬 Code graph analysis (1)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (2)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/lib/storage.ts (1)
  • storage (251-251)
⏰ 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

Comment on lines +177 to +202
// Bounty submissions to transfer to the target partner
const bountySubmissions = await prisma.bountySubmission.findMany({
where: {
programId: {
in: programIdsToTransfer,
},
partnerId: sourcePartnerId,
},
});

// Attempting to update all source submissions to the target partnerId fails
// if the target already has submissions for the same bounties.
if (bountySubmissions.length > 0) {
await Promise.allSettled(
bountySubmissions.map((submission) =>
prisma.bountySubmission.update({
where: {
id: submission.id,
},
data: {
partnerId: targetPartnerId,
},
}),
),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle Promise.allSettled failures instead of ignoring them

Line 177: Switching to Promise.allSettled avoids hard failures, but we now discard the results entirely. Any rejected updates (for example, when the target already holds a submission for the same bounty) will be silently skipped while the route still reports a successful merge, leaving the source submission unmoved. Previously the single updateMany call failed atomically—now we can exit with inconsistent partner assignments and zero visibility.

Please inspect the settled results, log failures, and decide whether to retry or surface an error (e.g., by using the new logPromiseResults helper and acting on a non-zero failureCount). Without this, we can’t trust that bounty submissions actually moved.

🤖 Prompt for AI Agents
In apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts around lines 177
to 202, the Promise.allSettled results for updating bountySubmissions are
currently discarded which hides rejected updates; inspect the settled results,
call the existing logPromiseResults helper with a clear context to log errors,
then check the returned failureCount (or count rejected entries) and if > 0
either surface an error (throw/return a 5xx response) or implement a
retry/backoff for conflict cases — ensure the route does not report success when
any update failed.

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: 1

♻️ Duplicate comments (1)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (1)

184-195: Handle rejected bounty transfers before reporting success

Promise.allSettled makes the merge resilient, but we still drop every rejection on the floor. Any submission that fails to move (e.g., the target already owns that bounty) will silently stay on the source partner while the route claims success. Please inspect the settled results and fail fast (or implement a retry) if any updates were rejected, logging the reasons so we can act on them.

-        await Promise.allSettled(
-          bountySubmissions.map((submission) =>
-            prisma.bountySubmission.update({
-              where: {
-                id: submission.id,
-              },
-              data: {
-                partnerId: targetPartnerId,
-              },
-            }),
-          ),
-        );
+        const submissionResults = await Promise.allSettled(
+          bountySubmissions.map((submission) =>
+            prisma.bountySubmission.update({
+              where: { id: submission.id },
+              data: { partnerId: targetPartnerId },
+            }),
+          ),
+        );
+
+        const failedSubmissions = submissionResults.filter(
+          (result): result is PromiseRejectedResult =>
+            result.status === "rejected",
+        );
+
+        if (failedSubmissions.length > 0) {
+          failedSubmissions.forEach(({ reason }) =>
+            console.error(
+              `[merge-partner-accounts] Failed to transfer bounty submission`,
+              reason,
+            ),
+          );
+          throw new Error(
+            `Halting merge: ${failedSubmissions.length} bounty submissions were not transferred.`,
+          );
+        }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb2d679 and e600fef.

📒 Files selected for processing (1)
  • apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-info.tsx:45-56
Timestamp: 2025-08-25T17:39:38.965Z
Learning: In the bounty system, each partner can only submit to the same bounty once. This means totalSubmissions (pending + approved + rejected) equals the number of unique partners who have submitted, making UI text like "X of Y partners completed" accurate when using totalSubmissions.
Learnt from: devkiran
Repo: dubinc/dub PR: 2833
File: apps/web/lib/actions/partners/approve-bounty-submission.ts:53-61
Timestamp: 2025-09-12T17:31:10.548Z
Learning: In approve-bounty-submission.ts, the logic `bounty.rewardAmount ?? rewardAmount` is intentional. Bounties with preset reward amounts should use those fixed amounts, and the rewardAmount override parameter is only used when bounty.rewardAmount is null/undefined (for custom reward bounties). This follows the design pattern where bounties are either "flat rate" (fixed amount) or "custom" (variable amount set during approval).
📚 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.ts
📚 Learning: 2025-08-26T14:32:33.851Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/actions/partners/create-bounty-submission.ts:105-112
Timestamp: 2025-08-26T14:32:33.851Z
Learning: Non-performance bounties are required to have submissionRequirements. In create-bounty-submission.ts, it's appropriate to let the parsing fail if submissionRequirements is null for non-performance bounties, as this indicates a data integrity issue that should be caught.

Applied to files:

  • apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts
📚 Learning: 2025-09-12T17:31:10.548Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2833
File: apps/web/lib/actions/partners/approve-bounty-submission.ts:53-61
Timestamp: 2025-09-12T17:31:10.548Z
Learning: In approve-bounty-submission.ts, the logic `bounty.rewardAmount ?? rewardAmount` is intentional. Bounties with preset reward amounts should use those fixed amounts, and the rewardAmount override parameter is only used when bounty.rewardAmount is null/undefined (for custom reward bounties). This follows the design pattern where bounties are either "flat rate" (fixed amount) or "custom" (variable amount set during approval).

Applied to files:

  • apps/web/app/(ee)/api/cron/merge-partner-accounts/route.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/app/(ee)/api/cron/merge-partner-accounts/route.ts
📚 Learning: 2025-08-14T05:17:51.825Z
Learnt from: devkiran
Repo: dubinc/dub PR: 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/app/(ee)/api/cron/merge-partner-accounts/route.ts
🧬 Code graph analysis (1)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (4)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/lib/tinybird/record-link.ts (1)
  • recordLink (76-93)
apps/web/lib/api/partners/sync-total-commissions.ts (1)
  • syncTotalCommissions (5-50)
apps/web/lib/storage.ts (1)
  • storage (251-251)
⏰ 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

@steven-tey steven-tey merged commit dcf9db2 into main Oct 31, 2025
7 checks passed
@steven-tey steven-tey deleted the fix-merge-partner-accounts branch October 31, 2025 18:36
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