Applications API endpoints - #3787
Conversation
- Implemented POST /api/partners/approve to approve pending partner applications. - Implemented POST /api/partners/reject to reject pending partner applications. - Updated partner approval and rejection logic to include workspace context and user permissions. - Enhanced OpenAPI documentation for both endpoints. - Refactored partner approval and rejection actions to streamline database interactions and email notifications.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR refactors partner application approval and rejection workflows by extracting inline server action logic into dedicated API layer functions ( Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Route as API Route
participant Action as Server Action
participant API as API Function
participant DB as Database
participant Async as Async Tasks
Client->>Route: POST /partners/applications/approve
Route->>Route: Validate input (approvePartnerSchema)
Route->>Route: Derive workspace & user context
Route->>Action: Call approvePartnerAction()
Action->>API: Call approvePartner()
API->>DB: Fetch programEnrollment
API->>DB: Validate enrollment status = pending
API->>DB: Start transaction
API->>DB: Check trial enrollment limits
API->>DB: Update enrollment to approved
API->>DB: Update programApplication metadata
API->>DB: Commit transaction
API->>Async: Schedule activity log & partner-approved workflow
API-->>Action: Return partnerId
Action-->>Route: Return approval result
Route-->>Client: 200 JSON {partnerId}
Async->>Async: Record activity & trigger workflow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 4
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/approve-partner-enrollment.ts (1)
40-60:⚠️ Potential issue | 🟠 MajorGuard approval to pending enrollments only.
Line 41 updates by
(partnerId, programId)without checking status, so the new approval endpoint can turn rejected, deactivated, or banned enrollments back to approved while logging the old status as"pending". Make the transition conditional onstatus: "pending"and fail when no pending row is updated.🛡️ Proposed fix
- const enrollment = await tx.programEnrollment.update({ - where: { - partnerId_programId: { - partnerId, - programId, - }, - }, + const { count } = await tx.programEnrollment.updateMany({ + where: { + partnerId, + programId, + status: "pending", + }, data: { status: "approved", createdAt: new Date(), groupId: group.id, clickRewardId: group.clickRewardId, leadRewardId: group.leadRewardId, saleRewardId: group.saleRewardId, discountId: group.discountId, }, + }); + + if (count === 0) { + throw new Error("Partner enrollment must be pending to approve."); + } + + const enrollment = await tx.programEnrollment.findUniqueOrThrow({ + where: { + partnerId_programId: { + partnerId, + programId, + }, + }, include: { partner: true, }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/partners/approve-partner-enrollment.ts` around lines 40 - 60, The update currently uses tx.programEnrollment.update(...) with the unique key and no status guard, allowing non-pending enrollments to be re-approved; change this to a conditional update that only affects rows with status: "pending" by using tx.programEnrollment.updateMany({ where: { partnerId, programId, status: "pending" }, data: { ... } }) inside the transaction, check the returned count and throw an error (or return a failure) if count === 0, and then fetch the updated enrollment (e.g., tx.programEnrollment.findUnique or findFirst) to get the partner include; replace references to tx.programEnrollment.update with this two-step updateMany + fetch flow so the transition is guarded to pending-only.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/lib/api/partners/reject-partner.ts`:
- Around line 71-80: The deletion of the pending enrollment
(tx.programEnrollment.deleteMany for programEnrollment.id with status "pending")
is not verified, so a concurrent change could leave the row undeleted; update
the block guarded by allowImmediateReapply to capture the result of
tx.programEnrollment.deleteMany, check result.count (or count field) and if it
is 0 throw an error or handle it as a failed deletion (e.g., throw new
Error("Failed to delete pending enrollment")) so the caller knows the deletion
did not occur; keep this check inside the same transaction and only return after
confirming count > 0.
In `@apps/web/lib/openapi/partners/approve-partner.ts`:
- Around line 12-18: The OpenAPI operation in approve-partner.ts currently
defines requestBody with content but omits requestBody.required; update the
requestBody object for this operation to include required: true so the
requestBody (schema referenced by approvePartnerSchema) is explicitly mandatory
for clients and generated SDKs. Locate the requestBody definition in the
approve-partner.ts OpenAPI object and add required: true alongside the existing
content entry referencing approvePartnerSchema.
In `@apps/web/lib/openapi/partners/reject-partner.ts`:
- Around line 12-18: The OpenAPI operation in reject-partner.ts currently omits
requestBody.required (so the body is treated as optional); update the
operation's requestBody object to include required: true so that the generated
SDK enforces the body and partnerId presence—i.e., add required: true beside the
existing content block that references rejectPartnerSchema in the requestBody
for this endpoint.
In `@apps/web/lib/zod/schemas/partners.ts`:
- Around line 862-871: The schema description for approvePartnerSchema (field
groupId) is inconsistent with the approvePartnerEnrollment implementation which
uses groupId || program.defaultGroupId; either update approvePartnerEnrollment
to implement the intended precedence (e.g., use current enrollment.groupId >
passed groupId > program.defaultGroupId) or change the approvePartnerSchema
description to unambiguously match current behavior, and if you can’t change
behavior now, add a tracked follow-up (issue/PR) and simplify the schema comment
to state the actual runtime precedence (passed groupId or
program.defaultGroupId) so the public API contract is not misleading.
---
Outside diff comments:
In `@apps/web/lib/partners/approve-partner-enrollment.ts`:
- Around line 40-60: The update currently uses tx.programEnrollment.update(...)
with the unique key and no status guard, allowing non-pending enrollments to be
re-approved; change this to a conditional update that only affects rows with
status: "pending" by using tx.programEnrollment.updateMany({ where: { partnerId,
programId, status: "pending" }, data: { ... } }) inside the transaction, check
the returned count and throw an error (or return a failure) if count === 0, and
then fetch the updated enrollment (e.g., tx.programEnrollment.findUnique or
findFirst) to get the partner include; replace references to
tx.programEnrollment.update with this two-step updateMany + fetch flow so the
transition is guarded to pending-only.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f6bd368a-c585-4af1-8ae8-3685dc742844
📒 Files selected for processing (10)
apps/web/app/(ee)/api/partners/approve/route.tsapps/web/app/(ee)/api/partners/reject/route.tsapps/web/lib/actions/partners/approve-partner.tsapps/web/lib/actions/partners/reject-partner-application.tsapps/web/lib/api/partners/reject-partner.tsapps/web/lib/openapi/partners/approve-partner.tsapps/web/lib/openapi/partners/index.tsapps/web/lib/openapi/partners/reject-partner.tsapps/web/lib/partners/approve-partner-enrollment.tsapps/web/lib/zod/schemas/partners.ts
- Improved error handling in the approvePartner function to provide clearer feedback on enrollment status. - Updated the logic to determine the final group ID for partner assignments. - Enhanced comments and descriptions in the API and OpenAPI documentation for clarity. - Removed unnecessary code and streamlined database interactions.
- Updated error messages in the rejectPartner function for clarity on enrollment status. - Added validation to ensure that only pending enrollments can be deleted, with appropriate error feedback. - Marked requestBody as required in the OpenAPI documentation for both reject and approve partner endpoints.
|
@coderabbitai full review please |
|
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/lib/api/partners/reject-partner.ts`:
- Around line 117-167: The Promise.allSettled call inside waitUntil discards
rejected results so failed post-rejection side effects (trackActivityLog,
resolveFraudGroups, sendEmail) are never surfaced; update the waitUntil block to
inspect the Promise.allSettled results and log any failures (include the
operation identity and error) so failures in trackActivityLog,
resolveFraudGroups, or sendEmail are visible for investigation — e.g., run
Promise.allSettled([...]) and iterate the settled results, calling
processLogger.error or your logger with the task name
(trackActivityLog/resolveFraudGroups/sendEmail), the rejection reason, and
contextual IDs (workspaceId, programId, partnerId, userId).
- Around line 100-112: The non-immediate-reapply branch currently uses
tx.programEnrollment.update which can throw if the enrollment status was changed
concurrently; replace that call with tx.programEnrollment.updateMany filtering
on id and status: "pending", then validate the returned count is 1 (otherwise
throw or return the same controlled error path), mirroring the
immediate-reapply/deleteMany pattern; update references:
tx.programEnrollment.update -> tx.programEnrollment.updateMany and ensure you
set status to ProgramEnrollmentStatus.rejected and null out
clickRewardId/leadRewardId/saleRewardId/discountId, then check the count to
handle concurrent status changes consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1c17b7da-9e33-4d73-9b50-c401848d0e0b
📒 Files selected for processing (2)
apps/web/lib/api-logs/constants.tsapps/web/lib/api/partners/reject-partner.ts
There was a problem hiding this comment.
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/approve-partner.ts (1)
77-125:⚠️ Potential issue | 🟠 MajorActivity log records stale status — variable shadowing makes
newalways equal"pending".Inside the transaction callback,
const programEnrollment(Line 78) shadows the outerprogramEnrollmentloaded at Line 21. Once the transaction returns, the reference at Line 123 (new: programEnrollment.status) resolves to the outer object, whosestatusis the pre-update value ("pending", as enforced by the check at Lines 51–57). The activity log will always emitstatus: { old: "pending", new: "pending" }, silently breaking audit fidelity for partner approvals.Either hoist the updated enrollment out of the transaction, or hard-code the new status (since it's always
"approved"on success). Renaming the inner binding also avoids the shadowing.🛠️ Proposed fix
- await prisma.$transaction(async (tx) => { - const programEnrollment = await tx.programEnrollment.update({ + const updatedEnrollment = await prisma.$transaction(async (tx) => { + const updated = await tx.programEnrollment.update({ where: { partnerId_programId: { partnerId, programId, }, }, data: { status: "approved", createdAt: new Date(), groupId: group.id, clickRewardId: group.clickRewardId, leadRewardId: group.leadRewardId, saleRewardId: group.saleRewardId, discountId: group.discountId, }, }); - if (programEnrollment.applicationId) { + if (updated.applicationId) { await tx.programApplication.update({ where: { - id: programEnrollment.applicationId, + id: updated.applicationId, }, data: { reviewedAt: new Date(), rejectionReason: null, rejectionNote: null, userId, }, }); } + + return updated; }); waitUntil( Promise.allSettled([ trackActivityLog({ workspaceId: program.workspace.id, programId, resourceType: "partner", resourceId: partnerId, userId, action: "partner.approved", changeSet: { status: { old: "pending", - new: programEnrollment.status, + new: updatedEnrollment.status, }, }, }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/partners/approve-partner.ts` around lines 77 - 125, The activity log uses the outer programEnrollment (pre-update) because the inner const programEnrollment inside the prisma.$transaction shadows it; fix by returning the updated enrollment from the transaction and assigning it to a new variable (e.g., updatedEnrollment) or by renaming the inner binding (e.g., txProgramEnrollment) and then use updatedEnrollment.status (or hard-code "approved") when calling trackActivityLog so the changeSet new value reflects the post-update status; update the prisma.$transaction callback (and the Promise.allSettled call site using trackActivityLog) to reference the non-shadowed variable (updatedEnrollment or the hard-coded "approved") instead of the outer programEnrollment.
♻️ Duplicate comments (2)
apps/web/lib/api/partners/reject-partner.ts (2)
100-112:⚠️ Potential issue | 🟠 MajorUse
updateManywith count validation for the non-immediate rejection path.This branch still uses
updatewith a pending-status guard. If the enrollment changes after Line 56 but before this transaction runs, Prisma can throw instead of returning the controlledDubApiErrorused by the delete branch. Mirror the Line 81 count-check pattern here.Proposed fix
- await tx.programEnrollment.update({ + const { count } = await tx.programEnrollment.updateMany({ where: { id: programEnrollment.id, status: "pending", }, data: { status: ProgramEnrollmentStatus.rejected, clickRewardId: null, leadRewardId: null, saleRewardId: null, discountId: null, }, }); + + if (count !== 1) { + throw new DubApiError({ + code: "bad_request", + message: + "This enrollment cannot be rejected because it is no longer pending.", + }); + }Optional verification: confirm whether global API error handling already maps Prisma
P2025into a client-safe 400; if not, this fix prevents a race from surfacing as a 500.#!/bin/bash # Description: Inspect whether Prisma not-found/update misses are globally translated # into controlled client errors. Expect: explicit handling of P2025 if keeping update(). rg -n -C3 'P2025|PrismaClientKnownRequestError|isRecordNotFound|Record to update not found' --type ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/api/partners/reject-partner.ts` around lines 100 - 112, The current non-immediate rejection uses tx.programEnrollment.update with a pending-status guard (tx.programEnrollment.update) which can throw if the record changed before the transaction; change this to tx.programEnrollment.updateMany and then check the returned count (as done for the delete branch) to ensure exactly one row was updated, and throw the same controlled DubApiError when count !== 1; keep the data payload (status: ProgramEnrollmentStatus.rejected, clickRewardId: null, leadRewardId: null, saleRewardId: null, discountId: null) and the same where filter (id: programEnrollment.id, status: "pending") but perform a count validation after updateMany to mirror the Line 81 pattern.
117-167:⚠️ Potential issue | 🟡 MinorLog rejected side effects instead of discarding
allSettledfailures.
allSettledprevents side-effect failures from blocking the response, which is good, but the rejected results are currently dropped. That makes activity-log, fraud-resolution, or rejection-email failures invisible.Proposed fix
waitUntil( Promise.allSettled([ trackActivityLog({ workspaceId: workspace.id, programId, resourceType: "partner", resourceId: partnerId, userId, action: "partner_application.rejected", changeSet: { status: { old: "pending", new: "rejected", }, }, }), resolveFraudGroups({ where: { programId, partnerId, }, userId, resolutionReason: "Resolved automatically because the partner application was rejected.", }), partner.email && sendEmail({ to: partner.email, subject: `Your application to ${program.name} was not approved`, variant: "notifications", replyTo: program.supportEmail || "noreply", react: PartnerApplicationRejected({ partner: { name: partner.name ?? "there", email: partner.email, }, program: { name: program.name, slug: program.slug, supportEmail: program.supportEmail, }, additionalNotes: rejectionNote, rejectionReason: getProgramApplicationRejectionReasonLabel(rejectionReason), canReapplyImmediately: allowImmediateReapply, }), }), - ]), + ]).then((results) => { + results.forEach((result, index) => { + if (result.status === "rejected") { + console.error("Failed to run partner rejection side effect", { + task: ["trackActivityLog", "resolveFraudGroups", "sendEmail"][index], + workspaceId: workspace.id, + programId, + partnerId, + userId, + error: result.reason, + }); + } + }); + }), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/api/partners/reject-partner.ts` around lines 117 - 167, The Promise.allSettled call inside waitUntil swallows failures; change it to capture the settled results (e.g., const results = await Promise.allSettled([...]) inside the same async context used by waitUntil) and then iterate over results to find entries with status === "rejected" and log their reason (use the project's logger or processLogger) along with context (workspaceId, programId, partnerId) so failures from trackActivityLog, resolveFraudGroups, or sendEmail (PartnerApplicationRejected) are recorded instead of discarded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@apps/web/lib/partners/approve-partner.ts`:
- Around line 77-125: The activity log uses the outer programEnrollment
(pre-update) because the inner const programEnrollment inside the
prisma.$transaction shadows it; fix by returning the updated enrollment from the
transaction and assigning it to a new variable (e.g., updatedEnrollment) or by
renaming the inner binding (e.g., txProgramEnrollment) and then use
updatedEnrollment.status (or hard-code "approved") when calling trackActivityLog
so the changeSet new value reflects the post-update status; update the
prisma.$transaction callback (and the Promise.allSettled call site using
trackActivityLog) to reference the non-shadowed variable (updatedEnrollment or
the hard-coded "approved") instead of the outer programEnrollment.
---
Duplicate comments:
In `@apps/web/lib/api/partners/reject-partner.ts`:
- Around line 100-112: The current non-immediate rejection uses
tx.programEnrollment.update with a pending-status guard
(tx.programEnrollment.update) which can throw if the record changed before the
transaction; change this to tx.programEnrollment.updateMany and then check the
returned count (as done for the delete branch) to ensure exactly one row was
updated, and throw the same controlled DubApiError when count !== 1; keep the
data payload (status: ProgramEnrollmentStatus.rejected, clickRewardId: null,
leadRewardId: null, saleRewardId: null, discountId: null) and the same where
filter (id: programEnrollment.id, status: "pending") but perform a count
validation after updateMany to mirror the Line 81 pattern.
- Around line 117-167: The Promise.allSettled call inside waitUntil swallows
failures; change it to capture the settled results (e.g., const results = await
Promise.allSettled([...]) inside the same async context used by waitUntil) and
then iterate over results to find entries with status === "rejected" and log
their reason (use the project's logger or processLogger) along with context
(workspaceId, programId, partnerId) so failures from trackActivityLog,
resolveFraudGroups, or sendEmail (PartnerApplicationRejected) are recorded
instead of discarded.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c424996c-1be7-4e33-95b5-7cea4108241c
📒 Files selected for processing (12)
apps/web/app/(ee)/api/cron/partners/auto-approve/route.tsapps/web/app/(ee)/api/partners/approve/route.tsapps/web/app/(ee)/api/partners/reject/route.tsapps/web/lib/actions/partners/approve-partner.tsapps/web/lib/actions/partners/reject-partner-application.tsapps/web/lib/api-logs/constants.tsapps/web/lib/api/partners/reject-partner.tsapps/web/lib/openapi/partners/approve-partner.tsapps/web/lib/openapi/partners/index.tsapps/web/lib/openapi/partners/reject-partner.tsapps/web/lib/partners/approve-partner.tsapps/web/lib/zod/schemas/partners.ts
POST /api/partners/[approve, reject]
POST /api/partners/[approve, reject]POST /api/partners/approve and POST /api/partners/reject
…Partner Applications"
POST /api/partners/approve and POST /api/partners/reject|
@coderabbitai full review, any breaking changes? |
🧩 Analysis chain🏁 Script executed: Length of output: 47223
✅ Breaking ChangesThere are no external breaking changes. Here's the breakdown:
🔍 Issues Found1. Semantic bug: activity log records wrong status when
|
| Change | Breaking? | Notes |
|---|---|---|
approvePartnerEnrollment → approvePartner rename |
✅ No | Zero remaining references to the old name — cron route, server actions, and new API routes all updated consistently. |
workspaceId removed from approvePartnerSchema / rejectPartnerSchema |
✅ No | workspaceId was never exposed in the public API contract. Server actions re-add it locally via .extend({ workspaceId: z.string() }). |
New POST /api/partners/approve & POST /api/partners/reject |
✅ No | Purely additive endpoints. |
rejectPartner extracted from inline server action |
✅ No | Server action now delegates; behavior is preserved. |
🔍 Issues Found
1. 🐛 Bug: allowImmediateReapply=true logs wrong status
In apps/web/lib/api/partners/reject-partner.ts, when allowImmediateReapply is true, the enrollment row is deleted — but waitUntil still fires with:
changeSet: {
status: {
old: "pending",
new: "rejected", // ❌ enrollment was deleted, not transitioned to "rejected"
},
},The activity log action and changeSet should reflect the deletion, or a separate conditional branch should be used for the allowImmediateReapply path.
2. 🐛 Bug: approvePartner activity log captures stale "pending" status
In apps/web/lib/partners/approve-partner.ts, the waitUntil block uses:
changeSet: {
status: {
old: "pending",
new: programEnrollment.status, // ❌ this is the pre-transaction snapshot = "pending"
},
},programEnrollment.status was fetched before the transaction that updates it to "approved". The new value should be hardcoded to "approved".
3. ⚠️ High-impact inconsistency: auto-reject cron bypasses rejectPartner()
apps/web/app/(ee)/api/cron/partners/auto-reject/route.ts has its own inline rejection logic (Prisma transaction, resolveFraudGroups, email sending) and does not call the new rejectPartner() function. This means rejection behavior can diverge between:
POST /api/partners/reject+ server action → usesrejectPartner()- Auto-reject cron → uses its own inline path
For example, if rejectPartner() is updated in the future, the cron will silently fall out of sync. The cron should be refactored to call rejectPartner() (or a shared internal helper) for consistency.
4. Activity log action naming inconsistency
approvePartner uses action: "partner.approved" (dot separator), while rejectPartner uses action: "partner_application.rejected" (underscore separator). These should follow one convention.
5. No tests
No test files exist for approvePartner, rejectPartner, or the new API routes. Given the transaction branching complexity in rejectPartner (especially the allowImmediateReapply path), unit/integration tests are strongly recommended.
✅ Actions performed
Review triggered.
Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/web/tests/partners/applications/approve-reject-partner-application.test.ts (1)
8-91: Good E2E coverage — consider asserting activity log entries too.The sequential flow exercises reject → duplicate reject (400) → reset → approve → GET correctly. One gap: the test only inspects
partner.status, so it wouldn't catch the stalechangeSet.status.newbug flagged inapprove-partner.ts(line 131) or the changeSet mismatch in the immediate-reapply rejection path. A lightweight DB assertion againstactivityLogfor the approve/reject records would make this suite a stronger regression guard.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/tests/partners/applications/approve-reject-partner-application.test.ts` around lines 8 - 91, Add assertions that query the activityLog after the reject and approve requests to ensure corresponding entries exist and contain correct payloads; specifically after the reject POST check activityLog for a rejection entry tied to partnerId with rejectionReason/rejectionNote and correct changeSet (e.g., changeSet.status.old/new reflect the transition), and after the approve POST check activityLog for an approval entry tied to partnerId with changeSet.status.new === "approved" and any groupId present; locate these checks around the uses of the endpoints "/partners/applications/reject" and "/partners/applications/approve" in the test "reject then approve the same partner after E2E pending reset" and fail the test if expected activityLog records are missing or have mismatched changeSet/status values.apps/web/lib/api/partners/applications/approve-partner.ts (1)
127-127: Consider aligning activity log action naming with the reject flow.This uses
"partner.approved"whilereject-partner.ts(line 125) uses"partner_application.rejected". Pick one convention (e.g., both under apartner_application.*namespace) so downstream activity consumers don't have to special-case each side of the same workflow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/api/partners/applications/approve-partner.ts` at line 127, The activity log action in apps/web/lib/api/partners/applications/approve-partner.ts currently uses "partner.approved" which is inconsistent with the reject flow that uses "partner_application.rejected"; update the action string to the same namespace (e.g., change "partner.approved" to "partner_application.approved") where the action property is set so both approve and reject flows share the "partner_application.*" convention and downstream consumers don't need special-casing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/lib/api/partners/applications/approve-partner.ts`:
- Around line 119-134: The activity log is using the outer-scoped
programEnrollment (the pre-update findUnique) so changeSet.status.new is stuck
as "pending"; inside the transaction callback rename the updated record (e.g.,
to updatedEnrollment) and ensure you capture that updatedEnrollment in a
variable accessible when calling waitUntil/trackActivityLog (or hoist a
reference to the updated status after the transaction) so trackActivityLog uses
the post-transaction status; update references to programEnrollment in the
trackActivityLog call to use the new updatedEnrollment (or its status) instead
of the outer programEnrollment to avoid shadowing.
In `@apps/web/lib/api/partners/applications/reject-partner.ts`:
- Around line 117-132: The activity log always records changeSet.status.new =
"rejected" in the trackActivityLog call even when allowImmediateReapply === true
and the enrollment row is deleted; update the logic in the reject partner flow
to branch the changeSet based on allowImmediateReapply (use
allowImmediateReapply flag in the code path that calls trackActivityLog) so that
when allowImmediateReapply is true you record a different outcome (e.g.,
changeSet.status.new = "deleted" or set a distinct action like
"partner_application.deleted") rather than "rejected"; locate the
trackActivityLog invocation in reject-partner.ts and alter the payload
construction to conditionally set changeSet/status or action using
allowImmediateReapply so logs accurately reflect deletion vs rejection.
---
Nitpick comments:
In `@apps/web/lib/api/partners/applications/approve-partner.ts`:
- Line 127: The activity log action in
apps/web/lib/api/partners/applications/approve-partner.ts currently uses
"partner.approved" which is inconsistent with the reject flow that uses
"partner_application.rejected"; update the action string to the same namespace
(e.g., change "partner.approved" to "partner_application.approved") where the
action property is set so both approve and reject flows share the
"partner_application.*" convention and downstream consumers don't need
special-casing.
In
`@apps/web/tests/partners/applications/approve-reject-partner-application.test.ts`:
- Around line 8-91: Add assertions that query the activityLog after the reject
and approve requests to ensure corresponding entries exist and contain correct
payloads; specifically after the reject POST check activityLog for a rejection
entry tied to partnerId with rejectionReason/rejectionNote and correct changeSet
(e.g., changeSet.status.old/new reflect the transition), and after the approve
POST check activityLog for an approval entry tied to partnerId with
changeSet.status.new === "approved" and any groupId present; locate these checks
around the uses of the endpoints "/partners/applications/reject" and
"/partners/applications/approve" in the test "reject then approve the same
partner after E2E pending reset" and fail the test if expected activityLog
records are missing or have mismatched changeSet/status values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 551eeb61-5f48-444e-83e4-a0b4a17cf9fb
📒 Files selected for processing (23)
apps/web/app/(ee)/api/cron/partners/auto-approve/route.tsapps/web/app/(ee)/api/e2e/partners/pending-program-application/route.tsapps/web/app/(ee)/api/partners/applications/approve/route.tsapps/web/app/(ee)/api/partners/applications/reject/route.tsapps/web/lib/actions/partners/approve-partner.tsapps/web/lib/actions/partners/reject-partner-application.tsapps/web/lib/api-logs/constants.tsapps/web/lib/api/partners/applications/approve-partner.tsapps/web/lib/api/partners/applications/reject-partner.tsapps/web/lib/openapi/commissions/list-commissions.tsapps/web/lib/openapi/customers/get-customer.tsapps/web/lib/openapi/customers/get-customers.tsapps/web/lib/openapi/domains/list-domains.tsapps/web/lib/openapi/events/index.tsapps/web/lib/openapi/folders/list-folders.tsapps/web/lib/openapi/links/get-links.tsapps/web/lib/openapi/partners/approve-partner.tsapps/web/lib/openapi/partners/index.tsapps/web/lib/openapi/partners/list-partner-applications.tsapps/web/lib/openapi/partners/reject-partner.tsapps/web/lib/openapi/payouts/list-payouts.tsapps/web/lib/openapi/tags/get-tags.tsapps/web/tests/partners/applications/approve-reject-partner-application.test.ts
✅ Files skipped from review due to trivial changes (11)
- apps/web/lib/openapi/payouts/list-payouts.ts
- apps/web/lib/openapi/events/index.ts
- apps/web/lib/openapi/tags/get-tags.ts
- apps/web/lib/openapi/domains/list-domains.ts
- apps/web/lib/openapi/links/get-links.ts
- apps/web/lib/openapi/folders/list-folders.ts
- apps/web/lib/openapi/customers/get-customers.ts
- apps/web/lib/api-logs/constants.ts
- apps/web/lib/openapi/partners/list-partner-applications.ts
- apps/web/lib/openapi/customers/get-customer.ts
- apps/web/lib/openapi/commissions/list-commissions.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/web/lib/openapi/partners/index.ts
- apps/web/app/(ee)/api/cron/partners/auto-approve/route.ts
- apps/web/lib/openapi/partners/approve-partner.ts
- apps/web/lib/actions/partners/reject-partner-application.ts
- apps/web/lib/actions/partners/approve-partner.ts
Summary by CodeRabbit
Release Notes
New Features
Documentation