Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds optional email-based filtering to GET /partners with precedence rules relative to tenantId and search. Updates Zod schema to include email and tenantId filters with clarified precedence and an optional includeExpandedFields flag. Adds an end-to-end test for email filtering and updates test resources with a partner email. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant C as Client
participant API as GET /partners
participant S as Zod Schema
participant DB as Database
C->>API: Request with query { tenantId?, email?, search?, includeExpandedFields? }
API->>S: Validate query (email, tenantId, search, includeExpandedFields)
S-->>API: Parsed filters
alt tenantId provided
API->>DB: SELECT ... WHERE tenantId = :tenantId [ignores email/search precedence]
else email provided
API->>DB: SELECT ... WHERE email = :email
else search provided
API->>DB: SELECT ... WHERE matches(search)
else
API->>DB: SELECT ... (no tenant/email/search filter)
end
DB-->>API: Rows
API-->>C: 200 OK with partners[]
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 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
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: 0
🧹 Nitpick comments (8)
apps/web/tests/utils/resource.ts (1)
86-88: Avoid hard‑coding a personal email in E2E fixturesEnsure this email exists in all envs used by CI; otherwise tests can flake. Prefer a test‑only alias via env with a safe default.
Proposed tweak:
export const E2E_PARTNER = { id: "pn_H4TB2V5hDIjpqB7PwrxESoY3", - email: "[email protected]", + email: process.env.E2E_PARTNER_EMAIL ?? "[email protected]", tenantId: "4149092f-7265-4002-98d9-da9f8e67e1fb", };apps/web/lib/zod/schemas/partners.ts (3)
137-144: Tighten email filter validationSince the backend does exact equality on email, validate shape to reduce garbage inputs and align with createPartner constraints.
- email: z - .string() - .optional() + email: z + .string() + .trim() + .max(190) + .email() + .optional() .describe( "Filter the partner list based on the partner's `email`. The value must be a string. Takes precedence over `search`.", ) .openapi({ example: "[email protected]" }),
144-151: Precedence semantics: clarify and align with implementationDocs say tenantId takes precedence over email and search. Backend currently still applies
searchalongsidetenantId/
- update description to “combined with other filters,” or
- gate
searchwhentenantIdor
158-163: Name consistency in descriptionUse the actual field name
totalCommissionsto avoid confusion.- "Whether to include stats fields on the partner (`clicks`, `leads`, `conversions`, `sales`, `saleAmount`, `commissions`, `netRevenue`). If false, those fields will be returned as 0.", + "Whether to include stats fields on the partner (`clicks`, `leads`, `conversions`, `sales`, `saleAmount`, `totalCommissions`, `netRevenue`). If false, those fields will be returned as 0.",apps/web/tests/partners/list-partners.test.ts (1)
84-101: Add precedence and negative‑case testsTo lock semantics:
- When both tenantId and email are provided, tenantId should win.
- Non‑existent email should return 200 with an empty array.
test("filters partners by email", async () => { const { data, status } = await http.get<EnrolledPartnerProps[]>({ path: "/partners", query: { email: E2E_PARTNER.email, }, }); expect(status).toEqual(200); expect(Array.isArray(data)).toBe(true); data.forEach((partner) => { const parsed = EnrolledPartnerSchema.parse(partner); expect(parsed.email).toBe(E2E_PARTNER.email); }); }); + + test("tenantId takes precedence over email when both are provided", async () => { + const { data, status } = await http.get<EnrolledPartnerProps[]>({ + path: "/partners", + query: { + tenantId: E2E_PARTNER.tenantId, + email: "[email protected]", + }, + }); + expect(status).toEqual(200); + expect(Array.isArray(data)).toBe(true); + data.forEach((partner) => { + const parsed = EnrolledPartnerSchema.parse(partner); + expect(parsed.tenantId).toBe(E2E_PARTNER.tenantId); + }); + }); + + test("returns empty list for non-existent email", async () => { + const { data, status } = await http.get<EnrolledPartnerProps[]>({ + path: "/partners", + query: { email: "nope-" + Date.now() + "@invalid.tld" }, + }); + expect(status).toEqual(200); + expect(Array.isArray(data)).toBe(true); + expect(data.length).toBe(0); + });apps/web/lib/api/partners/get-partners.ts (3)
137-137: Case‑insensitive email match (optional)If
Partner.emailisn’t stored/lower‑cased or using a CI collation, consider case‑insensitive equality to avoid misses.- ${tenantId ? Prisma.sql`AND pe.tenantId = ${tenantId}` : email ? Prisma.sql`AND p.email = ${email}` : Prisma.sql``} + ${tenantId + ? Prisma.sql`AND pe.tenantId = ${tenantId}` + : email + ? Prisma.sql`AND LOWER(p.email) = LOWER(${email})` + : Prisma.sql``}Note: Using functions can bypass indexes. If
140-152: Enforce precedence by gating search when email/tenantId is presentThis matches the schema’s “takes precedence” wording and avoids accidental over‑filtering.
- ${ - search - ? Prisma.sql`AND ( + ${ + !tenantId && !email && search + ? Prisma.sql`AND ( LOWER(p.name) LIKE LOWER(${`%${search}%`}) OR LOWER(p.email) LIKE LOWER(${`%${search}%`}) OR EXISTS ( SELECT 1 FROM Link searchLink WHERE searchLink.programId = ${programId} AND searchLink.partnerId = p.id AND searchLink.shortLink LIKE LOWER(${`%${search}%`}) ) )` : Prisma.sql`` }
50-158: Indexing note for new filterEnsure
Partner.emailhas an index (and unique constraint, per schema doc) to keep the new equality filter fast at scale.Suggested migration (MySQL):
ALTER TABLE Partner ADD UNIQUE KEY `uniq_partner_email` (`email`);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/lib/api/partners/get-partners.ts(2 hunks)apps/web/lib/zod/schemas/partners.ts(1 hunks)apps/web/tests/partners/list-partners.test.ts(1 hunks)apps/web/tests/utils/resource.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/tests/partners/list-partners.test.ts (3)
apps/web/lib/types.ts (1)
EnrolledPartnerProps(434-434)apps/web/tests/utils/resource.ts (1)
E2E_PARTNER(84-88)apps/web/lib/zod/schemas/partners.ts (1)
EnrolledPartnerSchema(306-369)
apps/web/lib/zod/schemas/partners.ts (1)
apps/web/lib/zod/schemas/misc.ts (1)
booleanQuerySchema(23-28)
🔇 Additional comments (1)
apps/web/tests/partners/list-partners.test.ts (1)
84-101: Good addition: positive path for email filterCovers the new query param and validates response shape.
Summary by CodeRabbit
New Features
Documentation