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
5 changes: 5 additions & 0 deletions .changeset/drafts-route-org-scope-11087.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@objectstack/rest': patch
---

`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.
Original file line numberDiff line numberDiff line change
Expand Up@@ -273,6 +273,19 @@ describe('#10340 the /meta doors decide org scope on the FOLDED type, not the ra
});
expect(requestFrom(b.listDrafts).type).toBe('translations');
});

it('threads the CALLER org into GET /meta/_drafts — read scope symmetric with the save route (#11087)', async () => {
// A draft saved by a session carrying an active org lands in that
// org's overlay scope (`saveMetaItem`'s `organizationId:
// ctx?.tenantId`). Reading with NO org sees only env-wide rows
// (`getOverlayRepo(null)` → `organization_id IS NULL`), so every
// org-scoped draft was invisible to the pending-changes surfaces —
// the write-org/read-null split behind cloud#1593. The repository's
// own `$or` contract surfaces BOTH scopes once the org is threaded.
const b = boot(AUTHORIZED);
await b.drive('GET', `${META}/_drafts`, {});
expect(requestFrom(b.listDrafts).organizationId).toBe(ORG);
});
});

describe('what the fold deliberately does NOT touch', () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/rest/src/rest-server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3950,9 +3950,24 @@ export class RestServer {
// [#6877] Both narrow the draft list to one package /
// one type; an array reached `listDrafts` untouched.
if (refuseRepeatedQueryParams(req, res, ['packageId', 'type'])) return;
// [#11087] Read in the CALLER'S org scope, symmetric with
// the save route (`saveMetaItem`'s `organizationId:
// ctx?.tenantId`, below): a draft saved by a session
// carrying an active org lands in that org's overlay
// scope, and this route used to 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. With the org threaded, the repository's
// own `$or` contract surfaces BOTH the caller's org
// overlay and env-wide drafts.
const result = await (p as any).listDrafts({
packageId: (req.query?.packageId as string | undefined) || undefined,
type: (req.query?.type as string | undefined) || undefined,
organizationId: ctx?.tenantId ?? undefined,
});
res.json(result);
} catch (error: any) {
Expand Down
Loading