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/23387-two-master-names-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@objectstack/spec": patch
---

fix(spec): the two-required-masters `controlled_by_parent` refusal names its release ("since @objectstack/spec 18"), as the no-master refusal does. Message text only: the refused shapes and the prescribed fix are unchanged.
4 changes: 2 additions & 2 deletions packages/spec/src/data/controlled-by-parent-master.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ export function controlledByParentMasterRefusal(
requiredLookups,
message:
`Object '${name}' declares sharingModel 'controlled_by_parent' with ${masters.length} required `
+ `\`master_detail\` fields (${masters.map((m) => `\`${m}\``).join(', ')}). ADR-0055 §1: a `
+ 'controlled-by-parent object derives its master from exactly one required `master_detail` field, so '
+ `\`master_detail\` fields (${masters.map((m) => `\`${m}\``).join(', ')}). ADR-0055 §1: since `
+ '@objectstack/spec 18, a controlled-by-parent object derives its master from exactly one required `master_detail` field, so '
+ 'with two the declaration does not say which master governs its rows, and the platform derives none '
+ '(every read is denied, every write refused). Keep `master_detail` on the one master, and declare '
+ 'every other parent as a `lookup` field.',
Expand Down
6 changes: 5 additions & 1 deletion packages/spec/src/data/object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1632,13 +1632,17 @@ describe('ObjectSchema refuses a controlled_by_parent object without exactly one
});

it('REFUSES no relation at all, and two required master_details, at the two lint-shared paths', () => {
expect(issuesOf(cbp({ title: { type: 'text' } })).map((i) => i.path)).toEqual(['sharingModel']);
const none = issuesOf(cbp({ title: { type: 'text' } }));
expect(none.map((i) => i.path)).toEqual(['sharingModel']);
const two = issuesOf(cbp({
a: { type: 'master_detail', reference: 'master_a', required: true },
b: { type: 'master_detail', reference: 'master_b', required: true },
}));
expect(two.map((i) => i.path)).toEqual(['fields']);
expect(two[0].message).toMatch(/^Object 'cbp_line' declares sharingModel 'controlled_by_parent' with 2 required `master_detail` fields \(`a`, `b`\)\. ADR-0055 §1:/);
// Both refusals date themselves: 17.7.0 accepts either shape, so each names the release that refuses it.
expect(two[0].message).toContain('since @objectstack/spec 18, a controlled-by-parent object derives');
expect(none[0].message).toContain('since @objectstack/spec 18, a controlled-by-parent object derives');
});

it('judges controlled_by_parent only — every other sharing model keeps its relations as authored', () => {
Expand Down
Loading