Skip to content

Commit ec79b11

Browse files
os-zhuangclaude
andauthored
fix(rest): GET /meta/_drafts reads in the caller's org scope (#11087) (#11160)
A draft saved by a session carrying an active org lands in that org's overlay scope (saveMetaItem's organizationId: ctx?.tenantId). The drafts route read with NO org — getOverlayRepo(null) sees only env-wide (organization_id IS NULL) rows — so every org-scoped draft was invisible to the pending-changes surfaces while single reads (which thread the ctx) and the publisher (which resolves each draft's own scope) saw it fine: the write-org/read-null split behind cloud#1593, measured live on a staging tenant (view draft saved 'org=org_mt42…' → _drafts answered only the older env-wide row). With the org threaded, SysMetadataRepository.listDrafts' own $or contract surfaces BOTH the caller's org overlay and env-wide drafts. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 927ccbb commit ec79b11

3 files changed

Lines changed: 33 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@objectstack/rest': patch
3+
---
4+
5+
`GET /meta/_drafts` threads the caller's org into `listDrafts` (#11087) — read scope symmetric with the save route, so org-scoped drafts (saved by sessions carrying an active organization) appear in the pending-changes list alongside env-wide ones instead of being invisible to every package/pending surface.

packages/rest/src/rest-server-meta-org-scope-url-spelling.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,19 @@ describe('#10340 the /meta doors decide org scope on the FOLDED type, not the ra
273273
});
274274
expect(requestFrom(b.listDrafts).type).toBe('translations');
275275
});
276+
277+
it('threads the CALLER org into GET /meta/_drafts — read scope symmetric with the save route (#11087)', async () => {
278+
// A draft saved by a session carrying an active org lands in that
279+
// org's overlay scope (`saveMetaItem`'s `organizationId:
280+
// ctx?.tenantId`). Reading with NO org sees only env-wide rows
281+
// (`getOverlayRepo(null)` → `organization_id IS NULL`), so every
282+
// org-scoped draft was invisible to the pending-changes surfaces —
283+
// the write-org/read-null split behind cloud#1593. The repository's
284+
// own `$or` contract surfaces BOTH scopes once the org is threaded.
285+
const b = boot(AUTHORIZED);
286+
await b.drive('GET', `${META}/_drafts`, {});
287+
expect(requestFrom(b.listDrafts).organizationId).toBe(ORG);
288+
});
276289
});
277290

278291
describe('what the fold deliberately does NOT touch', () => {

packages/rest/src/rest-server.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3950,9 +3950,24 @@ export class RestServer {
39503950
// [#6877] Both narrow the draft list to one package /
39513951
// one type; an array reached `listDrafts` untouched.
39523952
if (refuseRepeatedQueryParams(req, res, ['packageId', 'type'])) return;
3953+
// [#11087] Read in the CALLER'S org scope, symmetric with
3954+
// the save route (`saveMetaItem`'s `organizationId:
3955+
// ctx?.tenantId`, below): a draft saved by a session
3956+
// carrying an active org lands in that org's overlay
3957+
// scope, and this route used to read with NO org —
3958+
// `getOverlayRepo(null)` sees only env-wide
3959+
// (`organization_id IS NULL`) rows, so every org-scoped
3960+
// draft was invisible to the pending-changes surfaces
3961+
// while single reads (which thread the ctx) and the
3962+
// publisher (which resolves each draft's own scope)
3963+
// saw it fine — the write-org/read-null split behind
3964+
// cloud#1593. With the org threaded, the repository's
3965+
// own `$or` contract surfaces BOTH the caller's org
3966+
// overlay and env-wide drafts.
39533967
const result = await (p as any).listDrafts({
39543968
packageId: (req.query?.packageId as string | undefined) || undefined,
39553969
type: (req.query?.type as string | undefined) || undefined,
3970+
organizationId: ctx?.tenantId ?? undefined,
39563971
});
39573972
res.json(result);
39583973
} catch (error: any) {

0 commit comments

Comments
 (0)