fix(activity): clamp pagination params#282
Conversation
Greptile SummaryThis PR hardens the
Confidence Score: 5/5Safe to merge — the change is additive input sanitization with no side-effects on the happy path. The two helper functions correctly reject zero, negative, and NaN values and fall back to safe defaults. The Math.min(..., 50) cap is preserved, and the Supabase range call remains structurally identical. All four test scenarios confirm the clamping behaviour matches expectations. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant GET /api/activity
participant parsePositiveInt
participant parseNonNegativeInt
participant Supabase
Client->>GET /api/activity: GET ?limit=X&offset=Y
GET /api/activity->>getAuthContext: verify session
alt not authenticated
getAuthContext-->>GET /api/activity: null
GET /api/activity-->>Client: 401 Unauthorized
else authenticated
getAuthContext-->>GET /api/activity: { user, supabase }
GET /api/activity->>parsePositiveInt: parse limit (fallback 20)
parsePositiveInt-->>GET /api/activity: clamped limit (1-50)
GET /api/activity->>parseNonNegativeInt: parse offset (fallback 0)
parseNonNegativeInt-->>GET /api/activity: clamped offset (>=0)
GET /api/activity->>Supabase: .from(activities).select().eq().order().range(offset, offset+limit-1)
Supabase-->>GET /api/activity: { data, error, count }
GET /api/activity-->>Client: 200 { data, pagination: { total, limit, offset } }
end
Reviews (2): Last reviewed commit: "test(activity): cover valid pagination p..." | Re-trigger Greptile |
| function parsePositiveInt(value: string | null, fallback: number): number { | ||
| const parsed = Number.parseInt(value || "", 10); | ||
| return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; | ||
| } | ||
|
|
||
| function parseNonNegativeInt(value: string | null, fallback: number): number { | ||
| const parsed = Number.parseInt(value || "", 10); | ||
| return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback; | ||
| } |
There was a problem hiding this comment.
Number.isFinite is redundant here. Number.parseInt can only return an integer or NaN — it never produces Infinity — so the > 0 / >= 0 guards already reject NaN on their own. The check is harmless but misleading (it implies parseInt could return Infinity).
| function parsePositiveInt(value: string | null, fallback: number): number { | |
| const parsed = Number.parseInt(value || "", 10); | |
| return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; | |
| } | |
| function parseNonNegativeInt(value: string | null, fallback: number): number { | |
| const parsed = Number.parseInt(value || "", 10); | |
| return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback; | |
| } | |
| function parsePositiveInt(value: string | null, fallback: number): number { | |
| const parsed = Number.parseInt(value || "", 10); | |
| return parsed > 0 ? parsed : fallback; | |
| } | |
| function parseNonNegativeInt(value: string | null, fallback: number): number { | |
| const parsed = Number.parseInt(value || "", 10); | |
| return parsed >= 0 ? parsed : fallback; | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Follow-up pushed in b1a1d5f: added the happy-path pagination coverage Greptile suggested, verifying explicit valid limit and offset values are passed through to the Supabase range unchanged.\n\nValidation run locally:\n- corepack pnpm test -- src/app/api/activity/route.test.ts |
Summary
/api/activitypagination query params before building the Supabase rangelimitto 20 and cap large limits at 50offsetto 0Fixes #281
Payment
Test plan
npx.cmd vitest run src/app/api/activity/route.test.tsgit diff --check -- src/app/api/activity/route.ts src/app/api/activity/route.test.ts