Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/cross-object-batch-501-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@objectstack/rest": patch
---

fix(rest): give the bare 501 error exits a machine `code` (#4067)

Most REST error exits already carry a typed `code` (`VALIDATION_FAILED`,
`BATCH_NOT_ATOMIC`, `BATCH_TOO_LARGE`, `PERMISSION_DENIED`), and the clone /
search 501s already answer `{ error, code: 'NOT_IMPLEMENTED' }`. Four 501 exits
still returned a bare `{ error: '<string>' }` with no code, so a client could
only key on the prose:

- the cross-object transactional batch route (`POST {basePath}/batch`) when the
runtime has no `transaction()` — the last untyped exit on that route, whose
siblings (`BATCH_NOT_ATOMIC`, `VALIDATION_FAILED`, the `enforceBatchSize`
`BATCH_TOO_LARGE`) were already typed by the #3897 / #3933 / #3939 line;
- the two `saveMetaItem`-unsupported exits;
- the UI-view-resolution-unsupported exit.

Each now carries `code: 'NOT_IMPLEMENTED'`, matching the clone / search 501s.
Additive only — the `error` message is unchanged and no status changes — so
existing clients are unaffected; new ones can branch on the code.
4 changes: 3 additions & 1 deletion packages/rest/src/rest-batch-endpoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,12 @@ describe('POST {basePath}/batch — cross-object transactional batch', () => {
expect(route!.metadata?.tags).toEqual(expect.arrayContaining(['data', 'batch']));
});

it('returns 501 when the runtime has no transactional ObjectQL', async () => {
it('returns 501 with a NOT_IMPLEMENTED code when the runtime has no transactional ObjectQL', async () => {
const { route } = buildServer({}); // no ql provider
const res = await post(route, { operations: [{ object: 'account', data: {} }] });
expect(res.statusCode).toBe(501);
// Typed like every other 501 (#4067) so clients key on the code, not the prose.
expect(res.body.code).toBe('NOT_IMPLEMENTED');
});

// ── happy path ───────────────────────────────────────────────────────────
Expand Down
10 changes: 6 additions & 4 deletions packages/rest/src/rest-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3334,7 +3334,7 @@ export class RestServer {
const environmentId = isScoped ? req.params?.environmentId : undefined;
const p = await this.resolveProtocol(environmentId, req);
if (!p.saveMetaItem) {
res.status(501).json({ error: 'Save operation not supported by protocol implementation' });
res.status(501).json({ error: 'Save operation not supported by protocol implementation', code: 'NOT_IMPLEMENTED' });
return;
}

Expand Down Expand Up @@ -3717,7 +3717,7 @@ export class RestServer {
const environmentId = isScoped ? req.params?.environmentId : undefined;
const p = await this.resolveProtocol(environmentId, req);
if (!p.saveMetaItem) {
res.status(501).json({ error: 'Save operation not supported by protocol implementation' });
res.status(501).json({ error: 'Save operation not supported by protocol implementation', code: 'NOT_IMPLEMENTED' });
return;
}

Expand Down Expand Up @@ -3780,7 +3780,7 @@ export class RestServer {
} as any);
res.json(view);
} else {
res.status(501).json({ error: 'UI View resolution not supported by protocol implementation' });
res.status(501).json({ error: 'UI View resolution not supported by protocol implementation', code: 'NOT_IMPLEMENTED' });
}
} catch (error: any) {
logError("[REST] Unhandled error:", error);
Expand Down Expand Up @@ -6877,7 +6877,9 @@ export class RestServer {
if (this.enforceAuth(req, res, context)) return;
const ql = this.objectQLProvider ? await this.objectQLProvider(environmentId) : undefined;
if (!ql || typeof ql.transaction !== 'function') {
res.status(501).json({ error: 'Transactional batch not supported by this runtime' });
// Typed like every other 501 on this server (clone/search,
// #4067) so a client can key on the code, not the prose.
res.status(501).json({ error: 'Transactional batch not supported by this runtime', code: 'NOT_IMPLEMENTED' });
return;
}

Expand Down
Loading