Skip to content

Commit 212b66a

Browse files
os-zhuangclaude
andauthored
fix(platform-objects): allow import/export on sys_business_unit (#3025) (#3392)
The Business Units list surfaces Import/Export buttons, but sys_business_unit declared a restrictive `enable.apiMethods` whitelist of only the five CRUD verbs. The REST data plane gates import/export on that whitelist (ADR-0049), so both buttons returned 405 OBJECT_API_METHOD_NOT_ALLOWED. This was an unintentional gap, not a deliberate restriction: the object's fields (external_ref, effective_from/to) are designed for HRIS batch sync and the org tree is expected to support bulk import. Add 'import'/'export' to the whitelist; import reuses the create/update affordances already granted, and the managed-object reconciliation backstop leaves import/export untouched. Adds a regression test asserting the whitelist includes import/export. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b54f62c commit 212b66a

3 files changed

Lines changed: 39 additions & 1 deletion

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"@objectstack/platform-objects": patch
3+
---
4+
5+
fix(platform-objects): allow import/export on sys_business_unit (#3025)
6+
7+
The Business Units list (Setup → Business Units) surfaces Import/Export buttons,
8+
but `sys_business_unit` declared a restrictive `enable.apiMethods` whitelist of
9+
only the five CRUD verbs. The REST data plane gates import/export on that
10+
whitelist (ADR-0049), so both buttons returned `405 OBJECT_API_METHOD_NOT_ALLOWED`.
11+
12+
This was an unintentional gap, not a deliberate restriction: the object's fields
13+
(`external_ref`, `effective_from/to`) are designed for HRIS batch sync, and the
14+
org tree is expected to support bulk import. Added `'import'` and `'export'` to
15+
the whitelist. Import reuses the `create`/`update` affordances the object already
16+
grants, and the managed-object reconciliation backstop leaves import/export
17+
untouched (it only strips write verbs). Regression test added.

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ export const SysBusinessUnit = ObjectSchema.create({
216216
trackHistory: true,
217217
searchable: true,
218218
apiEnabled: true,
219-
apiMethods: ['get', 'list', 'create', 'update', 'delete'],
219+
// Data portability is expected for the org tree: fields like `external_ref`
220+
// and `effective_from/to` are designed for HRIS batch sync (#3025). Import
221+
// reuses the create/update affordances this object already grants.
222+
apiMethods: ['get', 'list', 'create', 'update', 'delete', 'import', 'export'],
220223
trash: true,
221224
mru: false,
222225
},

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { describe, expect, it } from 'vitest';
44
import {
55
SysAccount,
66
SysApiKey,
7+
SysBusinessUnit,
78
SysDeviceCode,
89
SysInvitation,
910
SysJwks,
@@ -230,6 +231,23 @@ describe('@objectstack/platform-objects', () => {
230231
});
231232
});
232233

234+
describe('data portability whitelist (#3025)', () => {
235+
it('sys_business_unit allows import/export so the org tree can be batch-synced', () => {
236+
// The Business Units list exposes Import/Export buttons and the object's
237+
// schema (external_ref, effective_from/to) is designed for HRIS batch
238+
// sync. The REST data plane gates these routes on `enable.apiMethods`
239+
// (ADR-0049), so both verbs must be present or the buttons 405 with
240+
// OBJECT_API_METHOD_NOT_ALLOWED. Regression guard for #3025.
241+
expect(SysBusinessUnit.enable?.apiMethods).toContain('import');
242+
expect(SysBusinessUnit.enable?.apiMethods).toContain('export');
243+
// The five CRUD verbs it already granted must remain — import writes
244+
// reuse the create/update affordances.
245+
for (const verb of ['get', 'list', 'create', 'update', 'delete'] as const) {
246+
expect(SysBusinessUnit.enable?.apiMethods).toContain(verb);
247+
}
248+
});
249+
});
250+
233251
describe('SETUP_APP (ADR-0029 D7 shell)', () => {
234252
it('parses cleanly through AppSchema', () => {
235253
expect(() => AppSchema.parse(SETUP_APP)).not.toThrow();

0 commit comments

Comments
 (0)