Skip to content

Commit 8845dd2

Browse files
committed
test(rest), changeset: the measured page-window table and the patch changeset
Claude-Session: https://claude.ai/code/session_01A3FoRXqj2EgAzPQbXU3Kzh Co-authored-by: Claude <noreply@anthropic.com>
1 parent 765adb3 commit 8845dd2

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
fix(metadata-protocol)!: the Data API refuses a malformed or negative page window with a 400 naming the parameter as written, instead of a 500 or a silently ignored window
6+
7+
Clause-②: no (narrowing)
8+
Metadata-protocol: none
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, object definition or stored shape is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed, so there is nothing for `objectstack migrate meta` to rewrite. What changes is the answer the list-query normalizer gives a request whose page window is not a non-negative integer. The remedy is in the request (send a count, or leave the parameter out), never a rewrite of a package source or a stored row. -->
11+
12+
**BREAKING** accept-set narrowing on the list doors (`GET /api/v1/data/:object`, `POST /api/v1/data/:object/query`, and the runtime dispatcher's data list). It changes what a Data API caller can send, not what a metadata author can write, so it ships as `patch` with this banner.
13+
14+
- **Before.** The list-query normalizer coerced the window with a bare `Number(...)`. A negative offset (`?$skip=-1`, `{"offset": -5}`) reached the SQL driver, which threw, and the caller got `500 INTERNAL_ERROR`. A limit that was not a number or was negative (`?$top=abc`, `?$top=-1`, `{"limit": -1}`), and an offset that was not a number (`?$skip=abc`), were dropped, so the caller got every row under a `200`. A blank `?$top=` became `0` and answered an empty page. A fractional window (`{"offset": 1.5}`, `{"limit": 1.5}`) was rounded by the driver.
15+
- **Now.** `limit` / `offset` and their spellings `$top`, `top`, `$skip`, `skip` must be non-negative integers. Anything else is refused before any read with `400 INVALID_REQUEST`, and the message names the parameter the caller wrote: `Query parameter '$skip' must be a non-negative integer; got "-1".` A blank value is refused too (`?$top=`, `?$skip=`), not read as absent: an absent window means every row, so reading a blank one that way would be the silent widening this closes.
16+
- **Unchanged.** A well-formed window pages exactly as before (`?$top=5&$skip=2`, `{"limit": 5, "offset": 2}`), and `0` stays valid on both (`$top=0` asks for the total alone). A non-number in a `POST` body (`{"limit": "ten"}`) is still the body schema's `400 VALIDATION_FAILED`.
17+
18+
**The remedy.** Send the window as a whole count of zero or more, or leave the parameter out.

‎packages/rest/src/data-page-window-door.test.ts‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,21 @@
77
* `400 INVALID_REQUEST` naming the parameter the caller wrote, before any read;
88
* over a real `SqlDriver` (SQLite), five rows.
99
*
10-
* Measured on the base through these two routes (the stock showcase answered
11-
* the same, `showcase_account`, admin bearer):
10+
* Measured on the base (`345f0b154c`) over a stock showcase boot
11+
* (`showcase_account`, 14 rows, admin session), and again on the fix:
1212
*
13-
* MEASURED_TABLE
13+
* | request | base | fix |
14+
* |:--|:--|:--|
15+
* | `GET ?$skip=-5`, `?$skip=-1&$top=3` | 500 `INTERNAL_ERROR` (knex `offset` throws) | 400 naming `$skip` |
16+
* | `POST {"offset":-5}` | 500 `INTERNAL_ERROR` | 400 naming `offset` |
17+
* | `GET ?$top=abc`, `?$top=-1`, `?$skip=abc` | 200, all 14 rows | 400 naming the spelling |
18+
* | `POST {"limit":-1}` | 200, all 14 rows | 400 naming `limit` |
19+
* | `GET ?$top=` | 200, 0 rows | 400 naming `$top` |
20+
* | `GET ?$skip=` | 200, all 14 rows | 400 naming `$skip` |
21+
* | `POST {"offset":1.5}` / `{"limit":1.5}` | 200, 13 rows / 1 row | 400 naming the key |
22+
* | `POST {"limit":"ten"}` | 400 `VALIDATION_FAILED` (the body schema) | unchanged |
23+
* | `POST {"limit":0}` | 200, 0 rows | unchanged |
24+
* | `GET ?$top=5&$skip=2`, `POST {"limit":5,"offset":2}` | 200, 5 rows | unchanged |
1425
*
1526
* The refusal lives once, in the shared normalizer both doors hand their
1627
* request to (`metadata-protocol`'s `findData`); the per-spelling pin with a

0 commit comments

Comments
 (0)