feat(api): add updated_at filter and sort to GET /device#2719
Conversation
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 23 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe device API now supports validated incremental filtering, sorting, and limits across Cloudflare and Supabase queries. Tests cover these behaviors and invalid inputs. Builtin app versions explicitly set ChangesDevice listing enhancements
Builtin version metadata
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant GET_device
participant readDevices
participant Cloudflare
participant Supabase
Client->>GET_device: Send updated_at, order, and limit
GET_device->>GET_device: Validate and normalize parameters
GET_device->>readDevices: Pass updated_at_gt, order, and limit
readDevices->>Cloudflare: Apply updated_at > toDateTime(updated_at_gt)
readDevices->>Supabase: Apply gt(updated_at, updated_at_gt)
Cloudflare-->>readDevices: Return filtered devices
Supabase-->>readDevices: Return filtered devices
readDevices-->>GET_device: Return device results
GET_device-->>Client: Return response
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
Merging this PR will improve performance by 68.42%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | /updates manifest response with metadata |
148 µs | 87.9 µs | +68.42% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing feat/device-updated-at-filter (075570d) with main (239b5a9)2
Footnotes
-
2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
-
No successful run was found on
main(242f735) during the generation of this report, so 239b5a9 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
Allow public device listing to filter by updated_at greater than an ISO timestamp and sort by updated_at asc/desc, avoiding full client-side paging.
Keep createBuiltinChannelVersion aligned with the app_versions row type after the schema sync added the API-key creator column.
36cb1da to
e0c9225
Compare
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_cd555ce7-83f4-4b42-b212-f20dba2f4faa) |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/functions/_backend/public/device/get.ts`:
- Around line 51-60: Update parseUpdatedAtFilter to validate updatedAt against a
strict ISO timestamp format before constructing the Date, rejecting non-ISO and
rollover dates such as 2024-02-31 with the existing invalid_updated_at error.
Preserve normalization to parsed.toISOString() for valid inputs, and add tests
covering rollover and non-ISO timestamps.
- Around line 80-83: The device page-limit validation around limit must reject
fractional values so Supabase and Cloudflare behave consistently. Update the
existing finite/positive check to require Number.isInteger(limit), or normalize
the value before passing it to readDevices; preserve the current invalid_limit
error handling.
In `@tests/device.test.ts`:
- Line 105: Remove the duplicate const data declarations in the response parsing
section of the device test, keeping a single declaration with the existing
response.json type and preserving all subsequent uses of data.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3af791f6-a4f8-414d-8b58-700ecb22302e
📒 Files selected for processing (7)
src/services/versions.tssupabase/functions/_backend/public/device/get.tssupabase/functions/_backend/utils/cloudflare.tssupabase/functions/_backend/utils/supabase.tssupabase/functions/_backend/utils/types.tstests/cloudflare-device-pagination.unit.test.tstests/device.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
Reject non-calendar ISO timestamps and non-integer limits on GET /device so filter/pagination inputs cannot be silently normalized.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_064211a6-fa5b-4025-a375-90fdc98e8cfd) |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
supabase/functions/_backend/public/device/get.ts (1)
52-57: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReject an explicitly empty
updated_at.
?updated_at=becomes an empty string and returnsundefinedhere, silently disabling filtering instead of returninginvalid_updated_at. Only omit validation when the value isundefined; let empty strings fail the regex. Add coverage for an empty query value.Proposed fix
- if (!updatedAt) + if (updatedAt === undefined) return undefined🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@supabase/functions/_backend/public/device/get.ts` around lines 52 - 57, Update the updatedAt guard in the request parsing flow to skip validation only when updatedAt is undefined, allowing an explicitly empty string to reach the ISO-date regex and raise invalid_updated_at. Add coverage for a query containing an empty updated_at value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/functions/_backend/public/device/get.ts`:
- Around line 95-100: Move the updatedAtGt, order, and limit computations and
limit validation out of the shared setup and into the list-request branch,
leaving device_id requests to call readDevices with limit: 1 without validating
those parameters. Add a regression test confirming a single-device request with
an otherwise invalid limit such as 0 retains its existing success behavior.
---
Duplicate comments:
In `@supabase/functions/_backend/public/device/get.ts`:
- Around line 52-57: Update the updatedAt guard in the request parsing flow to
skip validation only when updatedAt is undefined, allowing an explicitly empty
string to reach the ISO-date regex and raise invalid_updated_at. Add coverage
for a query containing an empty updated_at value.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e5254643-19de-4040-889d-68ecb4924396
📒 Files selected for processing (2)
supabase/functions/_backend/public/device/get.tstests/device.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
Keep single-device GET backward compatible by ignoring updated_at, order, and limit unless the caller is listing devices.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_014fe853-6925-4375-b650-6da942fe638c) |
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot skipped (usage limit reached) and did not complete automated review. This public GET /device API change exceeds the low-risk approval threshold; Dalanir and WcaleNieWolny are already assigned.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Review completed against the latest diff
Confidence score: 5/5
- Safe to merge after the addressed issues were fixed.
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
Normalize public device timestamps to ISO UTC, accept Cloudflare's space-separated UTC format for filter round-trips, push updated_at_gt into the Analytics Engine inner query, and avoid mutating shared seed devices.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_cb5c56ac-4e89-4c42-88c7-c1732374b88a) |
|
There was a problem hiding this comment.
2 issues found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="supabase/functions/_backend/public/device/get.ts">
<violation number="1" location="supabase/functions/_backend/public/device/get.ts:66">
P2: High-precision cutoffs lose fractional digits beyond milliseconds, so `updated_at_gt` can return devices at or before the requested timestamp. Preserving the validated `normalized` timestamp for the backend comparison would retain the advertised strict greater-than semantics.</violation>
</file>
<file name="supabase/functions/_backend/utils/cloudflare.ts">
<violation number="1" location="supabase/functions/_backend/utils/cloudflare.ts:1012">
P2: Filtered reads that include country data can now return `country_code: null` for a recently updated device whose latest country-bearing row predates the cutoff. The timestamp qualification should identify updated devices without removing older rows needed by the non-empty `country_code` aggregation.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| throw simpleError('invalid_updated_at', 'updated_at must be a valid ISO date', { updated_at: updatedAt }) | ||
| } | ||
|
|
||
| const parsed = new Date(normalized) |
There was a problem hiding this comment.
P2: High-precision cutoffs lose fractional digits beyond milliseconds, so updated_at_gt can return devices at or before the requested timestamp. Preserving the validated normalized timestamp for the backend comparison would retain the advertised strict greater-than semantics.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/functions/_backend/public/device/get.ts, line 66:
<comment>High-precision cutoffs lose fractional digits beyond milliseconds, so `updated_at_gt` can return devices at or before the requested timestamp. Preserving the validated `normalized` timestamp for the backend comparison would retain the advertised strict greater-than semantics.</comment>
<file context>
@@ -41,23 +41,29 @@ interface publicDevice {
}
- const parsed = new Date(updatedAt)
+ const parsed = new Date(normalized)
if (Number.isNaN(parsed.getTime()))
throw simpleError('invalid_updated_at', 'updated_at must be a valid ISO date', { updated_at: updatedAt })
</file context>
|
|
||
| if (params.updated_at_gt) { | ||
| const safeUpdatedAtGt = escapeSqlString(formatDateCF(params.updated_at_gt)) | ||
| conditions.push(`timestamp > toDateTime('${safeUpdatedAtGt}')`) |
There was a problem hiding this comment.
P2: Filtered reads that include country data can now return country_code: null for a recently updated device whose latest country-bearing row predates the cutoff. The timestamp qualification should identify updated devices without removing older rows needed by the non-empty country_code aggregation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/functions/_backend/utils/cloudflare.ts, line 1012:
<comment>Filtered reads that include country data can now return `country_code: null` for a recently updated device whose latest country-bearing row predates the cutoff. The timestamp qualification should identify updated devices without removing older rows needed by the non-empty `country_code` aggregation.</comment>
<file context>
@@ -1007,6 +1007,11 @@ export function buildReadDevicesCFQuery(params: ReadDevicesParams, customIdMode:
+ if (params.updated_at_gt) {
+ const safeUpdatedAtGt = escapeSqlString(formatDateCF(params.updated_at_gt))
+ conditions.push(`timestamp > toDateTime('${safeUpdatedAtGt}')`)
+ }
+
</file context>





Summary (AI generated)
updated_atquery param on publicGET /deviceto return only devices withupdated_atgreater than an ISO timestamporder=asc|descquery param to sort device listings byupdated_atCloses #2714
Motivation (AI generated)
The public devices list API previously had no way to filter or sort by
updated_at, so callers had to page through every device and filter client-side. That is inefficient for apps with many devices.Business Impact (AI generated)
Makes the public devices API usable for incremental sync and monitoring workflows, reducing API traffic and improving integration efficiency for customers building on Capgo.
Test Plan (AI generated)
updated_at_gtfilter andupdated_atorderingGET /device?updated_at=...andGET /device?order=descupdated_atandordervalues with 400Usage examples
Generated with AI
Made with Cursor
Summary by CodeRabbit
New Features
updated_at_gtfilter.order=asc|desc) with consistent results.Bug Fixes
updated_at,order, andlimit, returning clear HTTP 400 errors for invalid values.Tests
updated_at_gtfiltering, ordering, tie-breakers, and malformed input handling.