Skip to content

Commit 501dca7

Browse files
committed
fix(rest): the template's create refusal answers in the shared error envelope (#20896)
check:route-envelope ratchets the flat sibling-code dialect down and refused the new 403 body; it is now built through sendError from @objectstack/types. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 86cb4bb commit 501dca7

2 files changed

Lines changed: 12 additions & 8 deletions

File tree

‎packages/rest/src/import-template-route.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ describe('?template=true — the IMPORT door\'s gates decide, not the export\'s
286286
const builder = vi.spyOn(rest as any, 'answerImportTemplate');
287287
const out = await get({ template: 'true' });
288288
expect(out.status()).toBe(403);
289-
expect(out.json()).toMatchObject({ code: 'PERMISSION_DENIED', object: 'deal' });
289+
expect(out.json()).toMatchObject({ success: false, error: { code: 'PERMISSION_DENIED', details: { object: 'deal' } } });
290290
expect(out.body().length).toBe(0);
291291
expect(builder).not.toHaveBeenCalled();
292292
expect(getWritableFields).not.toHaveBeenCalled();
@@ -299,7 +299,7 @@ describe('?template=true — the IMPORT door\'s gates decide, not the export\'s
299299
const builder = vi.spyOn(rest as any, 'answerImportTemplate');
300300
const out = await get({ template: 'true' });
301301
expect(out.status()).toBe(403);
302-
expect(out.json()).toMatchObject({ code: 'PERMISSION_DENIED', object: 'deal' });
302+
expect(out.json()).toMatchObject({ success: false, error: { code: 'PERMISSION_DENIED', details: { object: 'deal' } } });
303303
expect(out.body().length).toBe(0);
304304
expect(builder).not.toHaveBeenCalled();
305305
expect(getWritableFields).not.toHaveBeenCalled();
@@ -312,7 +312,7 @@ describe('?template=true — the IMPORT door\'s gates decide, not the export\'s
312312
});
313313
const out = await get({ template: 'true' });
314314
expect(out.status()).toBe(403);
315-
expect(out.json()).toMatchObject({ code: 'PERMISSION_DENIED' });
315+
expect(out.json()?.error?.code).toBe('PERMISSION_DENIED');
316316
expect(out.body().length).toBe(0);
317317
});
318318

‎packages/rest/src/rest-server.ts‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,12 +2572,16 @@ export class RestServer {
25722572
allowed = false; // access-narrowing answer → a throw is a denial
25732573
}
25742574
if (allowed) return false;
2575-
res.status(403).json({
2576-
code: 'PERMISSION_DENIED',
2577-
error: `Creating records on object '${objectName}' is not permitted for this user, `
2575+
// Built through the SHARED envelope (`{ success: false, error: { code,
2576+
// message, details } }`), not in the flat sibling-`code` dialect the
2577+
// export gate above still answers in — `check:route-envelope` ratchets
2578+
// that dialect down and refuses a new body in it.
2579+
sendEnvelopeError(
2580+
res, 403, 'PERMISSION_DENIED',
2581+
`Creating records on object '${objectName}' is not permitted for this user, `
25782582
+ 'so its import template is not served',
2579-
object: objectName,
2580-
});
2583+
{ details: { object: objectName } },
2584+
);
25812585
return true;
25822586
}
25832587

0 commit comments

Comments
 (0)