|
| 1 | +--- |
| 2 | +'@objectstack/rest': patch |
| 3 | +'@objectstack/spec': minor |
| 4 | +--- |
| 5 | + |
| 6 | +fix(rest): gate the cross-object transactional batch by the same per-object API rules as single-record writes (#1604) |
| 7 | + |
| 8 | +The `POST {basePath}/batch` route (issue #1604 / ADR-0034) wraps N cross-object |
| 9 | +create/update/delete ops in one engine transaction, but it skipped the |
| 10 | +per-object API-exposure gate every single-record route applies — an |
| 11 | +authenticated caller could write to an `apiEnabled: false` object, or run an |
| 12 | +operation outside an object's `apiMethods` whitelist, straight through the batch |
| 13 | +surface (ADR-0049 / #1889 — the same "declared ≠ enforced" hole closed for the |
| 14 | +generic write path in #3220 / #3213). |
| 15 | + |
| 16 | +The route now: |
| 17 | + |
| 18 | +- validates the body against a new `CrossObjectBatchRequestSchema` |
| 19 | + (`@objectstack/spec/api`, Zod-First) — a malformed op, an unknown action, or a |
| 20 | + missing `object` is a `400` instead of a `500`; |
| 21 | +- enforces `enable.apiEnabled` / `enable.apiMethods` for **every** op (metadata |
| 22 | + fetched once, each distinct `(object, action)` checked) BEFORE opening the |
| 23 | + transaction — `404 OBJECT_API_DISABLED` / `405 OBJECT_API_METHOD_NOT_ALLOWED`; |
| 24 | +- requires an `id` for `update` / `delete` (`400`); |
| 25 | +- rejects an unresolvable `{ $ref }` with `400 BATCH_UNRESOLVED_REF` instead of |
| 26 | + silently writing a `null` FK; |
| 27 | +- rejects an explicit `atomic: false` (`400 BATCH_NOT_ATOMIC`) rather than |
| 28 | + silently applying atomically — non-atomic per-object batches stay on |
| 29 | + `POST /data/:object/batch`. |
| 30 | + |
| 31 | +`enforceApiAccess` is refactored to share the pure `apiAccessDenialFromEnable` |
| 32 | +check + a `loadObjectItems` helper with the batch route (single-record behavior |
| 33 | +unchanged). Adds `rest-batch-endpoint.test.ts` — the REST-boundary coverage |
| 34 | +ADR-0034 flagged as missing (commit, `$ref`, rollback surfacing, API-access |
| 35 | +denial, request validation). |
0 commit comments