Skip to content

Commit d10c4dc

Browse files
os-zhuangclaude
andauthored
fix(platform-objects): allow import/export on sys_business_unit_member (#3025 / #3391 P0) (#3394)
Completes the #3025 point-fix. #3392 added import/export to sys_business_unit but left the sibling membership table on a CRUD-only whitelist, so the HRIS org-tree sync scenario (units + memberships imported together) still 405'd on the membership import/export path. #3391's P0 checklist pairs both tables; this is the half #3392 missed. - sys_business_unit_member enable.apiMethods gains 'import','export'. - Reconcile-safe: import/export are not in MANAGED_WRITE_VERB_AFFORDANCE, so reconcileManagedApiMethods (managedBy:'platform') never strips them — the declared whitelist reaches apiAccessDenialFromEnable intact. - Regression test locks import/export + CRUD, proven red-before-green. Transitional per #3391 P2 (derived import/export mapping reclaims both objects). Refs #3025, #3391. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5bab2c5 commit d10c4dc

3 files changed

Lines changed: 62 additions & 1 deletion

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
"@objectstack/platform-objects": patch
3+
---
4+
5+
fix(platform-objects): allow import/export on sys_business_unit_member (#3025 / #3391 P0)
6+
7+
Completes the #3025 point-fix. #3392 unblocked `sys_business_unit`'s Import/Export
8+
buttons (405 `OBJECT_API_METHOD_NOT_ALLOWED`) by adding `'import'`/`'export'` to its
9+
`enable.apiMethods` whitelist, but the HRIS org-tree sync scenario imports **two
10+
tables together** — the units *and* their memberships — and the sibling
11+
`sys_business_unit_member` was left on the CRUD-only whitelist, so the membership
12+
Import/Export path still 405'd. #3391's P0 checklist pairs both tables; this is the
13+
half #3392 missed.
14+
15+
- `packages/platform-objects/src/identity/sys-business-unit-member.object.ts`:
16+
`apiMethods` gains `'import'`, `'export'`. Import reuses the object's
17+
already-granted create/update affordances; export is a bulk read.
18+
- Reconcile-safe: the object is `managedBy:'platform'`, but
19+
`reconcileManagedApiMethods` only strips generic write verbs
20+
(`create/update/upsert/delete/purge``MANAGED_WRITE_VERB_AFFORDANCE`). It never
21+
touches `import`/`export`, so the declared whitelist reaches the REST gate intact
22+
(no false-green: the static whitelist the regression test asserts IS what
23+
`apiAccessDenialFromEnable` enforces at runtime).
24+
- Regression test (`platform-objects.test.ts`) locks `import`/`export` presence and
25+
CRUD retention. Proven red-before-green: reverting the object edit fails with
26+
`expected [...] to include 'import'`.
27+
28+
Transitional: #3391 P2 replaces per-object `import`/`export` declarations with a
29+
single derived mapping (import ⊆ create/update, export ⊆ list) and reclaims the
30+
explicit entries on both business-unit objects together.
31+
32+
Refs #3025, #3391.

packages/platform-objects/src/identity/sys-business-unit-member.object.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,14 @@ export const SysBusinessUnitMember = ObjectSchema.create({
101101
trackHistory: true,
102102
searchable: true,
103103
apiEnabled: true,
104-
apiMethods: ['get', 'list', 'create', 'update', 'delete'],
104+
// import/export complete the HRIS org-tree sync scenario: the units
105+
// (sys_business_unit, #3025/#3392) and their memberships are imported
106+
// together as one bulk operation. Import reuses the already-granted
107+
// create/update affordances; export is a bulk read. Transitional — #3391
108+
// P2 derives these from create/update|list and reclaims the explicit
109+
// entries. Reconcile-safe: import/export are not generic write verbs, so
110+
// reconcileManagedApiMethods (managedBy:'platform') never strips them.
111+
apiMethods: ['get', 'list', 'create', 'update', 'delete', 'import', 'export'],
105112
trash: true,
106113
mru: false,
107114
},

packages/platform-objects/src/platform-objects.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
SysAccount,
66
SysApiKey,
77
SysBusinessUnit,
8+
SysBusinessUnitMember,
89
SysDeviceCode,
910
SysInvitation,
1011
SysJwks,
@@ -246,6 +247,27 @@ describe('@objectstack/platform-objects', () => {
246247
expect(SysBusinessUnit.enable?.apiMethods).toContain(verb);
247248
}
248249
});
250+
251+
it('sys_business_unit_member allows import/export and keeps CRUD (#3391 P0)', () => {
252+
// HRIS org-tree sync imports TWO tables together — the units (above) AND
253+
// their memberships. The sibling membership table carries the same kind
254+
// of restrictive whitelist, so it needs import/export too or the
255+
// membership import path 405s (OBJECT_API_METHOD_NOT_ALLOWED). #3391's P0
256+
// checklist pairs both tables; this is the half #3392 did not cover.
257+
const methods = SysBusinessUnitMember.enable?.apiMethods ?? [];
258+
expect(methods).toContain('import');
259+
expect(methods).toContain('export');
260+
// CRUD must remain — import writes reuse create/update; the membership
261+
// grid depends on the rest.
262+
for (const verb of ['get', 'list', 'create', 'update', 'delete'] as const) {
263+
expect(methods).toContain(verb);
264+
}
265+
// Transitional: #3391 P2 derives import/export (import ⊆ create/update,
266+
// export ⊆ list) and reclaims both objects' explicit entries together.
267+
// Reconcile-safe: import/export are not generic write verbs, so
268+
// reconcileManagedApiMethods (managedBy:'platform') never strips them —
269+
// this static whitelist IS what apiAccessDenialFromEnable enforces.
270+
});
249271
});
250272

251273
describe('SETUP_APP (ADR-0029 D7 shell)', () => {

0 commit comments

Comments
 (0)