@endora-commerce/mod-custom-fields 0.0.0-stage → 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -2
- package/dist/admin/api/custom-fields-client.d.ts +10 -0
- package/dist/admin/api/custom-fields-client.d.ts.map +1 -0
- package/dist/admin/api/custom-fields-client.js +11 -0
- package/dist/admin/api/custom-fields-client.js.map +1 -0
- package/dist/admin/index.d.ts +44 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +51 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CustomFieldsPage.d.ts +10 -0
- package/dist/admin/pages/CustomFieldsPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomFieldsPage.js +118 -0
- package/dist/admin/pages/CustomFieldsPage.js.map +1 -0
- package/dist/backend/commands/definition-commands.d.ts +22 -0
- package/dist/backend/commands/definition-commands.d.ts.map +1 -0
- package/dist/backend/commands/definition-commands.js +115 -0
- package/dist/backend/commands/definition-commands.js.map +1 -0
- package/dist/backend/entities/custom-field-definition.entity.d.ts +37 -0
- package/dist/backend/entities/custom-field-definition.entity.d.ts.map +1 -0
- package/dist/backend/entities/custom-field-definition.entity.js +98 -0
- package/dist/backend/entities/custom-field-definition.entity.js.map +1 -0
- package/dist/backend/entities/custom-field-option.entity.d.ts +25 -0
- package/dist/backend/entities/custom-field-option.entity.d.ts.map +1 -0
- package/dist/backend/entities/custom-field-option.entity.js +79 -0
- package/dist/backend/entities/custom-field-option.entity.js.map +1 -0
- package/dist/backend/index.d.ts +79 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +94 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +14 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +179 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/services/custom-field-definition-apply.d.ts +63 -0
- package/dist/backend/services/custom-field-definition-apply.d.ts.map +1 -0
- package/dist/backend/services/custom-field-definition-apply.js +221 -0
- package/dist/backend/services/custom-field-definition-apply.js.map +1 -0
- package/dist/backend/services/custom-field-definition.service.d.ts +68 -0
- package/dist/backend/services/custom-field-definition.service.d.ts.map +1 -0
- package/dist/backend/services/custom-field-definition.service.js +205 -0
- package/dist/backend/services/custom-field-definition.service.js.map +1 -0
- package/dist/backend/services/custom-field-definitions-cache.d.ts +46 -0
- package/dist/backend/services/custom-field-definitions-cache.d.ts.map +1 -0
- package/dist/backend/services/custom-field-definitions-cache.js +82 -0
- package/dist/backend/services/custom-field-definitions-cache.js.map +1 -0
- package/dist/backend/services/custom-field-read-port.d.ts +61 -0
- package/dist/backend/services/custom-field-read-port.d.ts.map +1 -0
- package/dist/backend/services/custom-field-read-port.js +82 -0
- package/dist/backend/services/custom-field-read-port.js.map +1 -0
- package/dist/backend/services/custom-field-registry.d.ts +41 -0
- package/dist/backend/services/custom-field-registry.d.ts.map +1 -0
- package/dist/backend/services/custom-field-registry.js +24 -0
- package/dist/backend/services/custom-field-registry.js.map +1 -0
- package/dist/backend/services/custom-field-value.service.d.ts +56 -0
- package/dist/backend/services/custom-field-value.service.d.ts.map +1 -0
- package/dist/backend/services/custom-field-value.service.js +175 -0
- package/dist/backend/services/custom-field-value.service.js.map +1 -0
- package/dist/manifest.d.ts +177 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +130 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260718T200338_custom_fields_init.d.ts +20 -0
- package/dist/migrations/20260718T200338_custom_fields_init.d.ts.map +1 -0
- package/dist/migrations/20260718T200338_custom_fields_init.js +60 -0
- package/dist/migrations/20260718T200338_custom_fields_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +123 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/i18n/en.json +15 -0
- package/i18n/pl.json +15 -0
- package/package.json +99 -3
- package/tailwind.css +14 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { type CreateCustomFieldDefinitionRequest, type CustomFieldOptionDto, type SupportedEntityType, type UpdateCustomFieldDefinitionRequest } from '@endora-commerce/contracts';
|
|
3
|
+
import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
|
|
4
|
+
import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* Transactional apply functions for custom-field definition/option writes
|
|
7
|
+
* (feature 061, contracts/custom-fields-product-host.md §3).
|
|
8
|
+
*
|
|
9
|
+
* Every function runs entirely on the CALLER-provided EntityManager and
|
|
10
|
+
* enforces the same invariants as the public CRUD (duplicate key, options
|
|
11
|
+
* rule, entity type known, value-type lock via the probe binding,
|
|
12
|
+
* option-in-use). They dispatch NO command, write NO audit entry, and publish
|
|
13
|
+
* NO cache invalidation — a host command audits its composite operation and
|
|
14
|
+
* calls `publishInvalidate` post-commit; the module's own definition commands
|
|
15
|
+
* share these bodies so there is a single write path (Principle XIII).
|
|
16
|
+
*/
|
|
17
|
+
/** Typed failures the routes map to HTTP status codes. */
|
|
18
|
+
export declare class CustomFieldDefinitionError extends Error {
|
|
19
|
+
readonly code: 'not_found' | 'duplicate_key' | 'options_required' | 'options_forbidden' | 'entity_type_unknown' | 'value_type_locked' | 'option_in_use' | 'key_changed' | 'invalid_key';
|
|
20
|
+
constructor(code: 'not_found' | 'duplicate_key' | 'options_required' | 'options_forbidden' | 'entity_type_unknown' | 'value_type_locked' | 'option_in_use' | 'key_changed' | 'invalid_key', message: string);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Change-guard probes over the host's value storage (structurally satisfied by
|
|
24
|
+
* `CustomFieldValueService`). Optional: when absent, the corresponding guard
|
|
25
|
+
* is the caller's responsibility (e.g. a host running its own authoritative
|
|
26
|
+
* in-use checks first — research §R9).
|
|
27
|
+
*/
|
|
28
|
+
export interface DefinitionChangeProbes {
|
|
29
|
+
hasStoredValues(em: EntityManager, entityType: SupportedEntityType, key: string): Promise<boolean>;
|
|
30
|
+
isOptionInUse(em: EntityManager, entityType: SupportedEntityType, key: string, optionValue: string): Promise<boolean>;
|
|
31
|
+
}
|
|
32
|
+
/** Select fields require ≥ 1 option; non-select fields must not carry any. */
|
|
33
|
+
export declare function assertOptionsRule(valueType: string, options: CustomFieldOptionDto[]): void;
|
|
34
|
+
/** Create a definition (+ its options for select types) on the caller's EM. */
|
|
35
|
+
export declare function applyCreateDefinition(em: EntityManager, input: CreateCustomFieldDefinitionRequest): Promise<CustomFieldDefinition>;
|
|
36
|
+
/** Patch a definition's scalar/label/config fields on the caller's EM. */
|
|
37
|
+
export declare function applyUpdateDefinition(em: EntityManager, id: string, patch: UpdateCustomFieldDefinitionRequest, probes?: DefinitionChangeProbes): Promise<CustomFieldDefinition>;
|
|
38
|
+
/**
|
|
39
|
+
* Rename a definition's key on the caller's EM — the one path by which a key
|
|
40
|
+
* changes after create (`specs/134-paid-module-extraction/research.md` D12).
|
|
41
|
+
*
|
|
42
|
+
* Not an edit: `UpdateCustomFieldDefinitionRequest` omits `key` on purpose,
|
|
43
|
+
* because every host stores its values **under** the key and an ordinary rename
|
|
44
|
+
* would orphan them. The caller of this function is therefore one that renames
|
|
45
|
+
* the host's values in the same transaction — today a connector repairing keys
|
|
46
|
+
* it derived itself, with `catalog` moving the product values beside it.
|
|
47
|
+
*
|
|
48
|
+
* `expectedKey` is the key the caller planned from. A definition whose key has
|
|
49
|
+
* moved since is refused with `key_changed` rather than renamed from a state the
|
|
50
|
+
* caller never saw. Flushes before it returns, so a caller sequencing a
|
|
51
|
+
* two-phase rename (park, then move) gets its statements in the order it asked
|
|
52
|
+
* for them and a unique-index violation surfaces at the call that caused it.
|
|
53
|
+
*/
|
|
54
|
+
export declare function applyRenameDefinitionKey(em: EntityManager, id: string, expectedKey: string, newKey: string): Promise<CustomFieldDefinition>;
|
|
55
|
+
/** Delete a definition on the caller's EM. Options cascade; host values stay dormant (FR-010). */
|
|
56
|
+
export declare function applyDeleteDefinition(em: EntityManager, id: string): Promise<CustomFieldDefinition>;
|
|
57
|
+
/** Add one option to a select/multiselect definition on the caller's EM. */
|
|
58
|
+
export declare function applyCreateOption(em: EntityManager, definitionId: string, input: CustomFieldOptionDto): Promise<CustomFieldOption>;
|
|
59
|
+
/** Patch one option's label/default/ordering on the caller's EM. */
|
|
60
|
+
export declare function applyUpdateOption(em: EntityManager, definitionId: string, optionId: string, patch: Partial<Pick<CustomFieldOptionDto, 'label' | 'labelDefault' | 'isDefault' | 'sortOrder'>>): Promise<CustomFieldOption>;
|
|
61
|
+
/** Delete one option on the caller's EM (in-use guard via the probe binding). */
|
|
62
|
+
export declare function applyDeleteOption(em: EntityManager, definitionId: string, optionId: string, probes?: DefinitionChangeProbes): Promise<CustomFieldOption>;
|
|
63
|
+
//# sourceMappingURL=custom-field-definition-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-field-definition-apply.d.ts","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition-apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,kCAAkC,EACvC,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kCAAkC,EACxC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAG9E;;;;;;;;;;;GAWG;AAEH,0DAA0D;AAC1D,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,QAAQ,CAAC,IAAI,EACT,WAAW,GACX,eAAe,GACf,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,mBAAmB,GACnB,eAAe,GACf,aAAa,GACb,aAAa;gBATR,IAAI,EACT,WAAW,GACX,eAAe,GACf,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,mBAAmB,GACnB,eAAe,GACf,aAAa,GACb,aAAa,EACjB,OAAO,EAAE,MAAM;CAKlB;AAID;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,eAAe,CAAC,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnG,aAAa,CACX,EAAE,EAAE,aAAa,EACjB,UAAU,EAAE,mBAAmB,EAC/B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,IAAI,CAQ1F;AAED,+EAA+E;AAC/E,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE,kCAAkC,GACxC,OAAO,CAAC,qBAAqB,CAAC,CAoChC;AAED,0EAA0E;AAC1E,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,aAAa,EACjB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,kCAAkC,EACzC,MAAM,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAoBhC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,aAAa,EACjB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,qBAAqB,CAAC,CA6BhC;AAED,kGAAkG;AAClG,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAWzG;AAED,4EAA4E;AAC5E,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,aAAa,EACjB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAO5B;AAED,oEAAoE;AACpE,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,aAAa,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,cAAc,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC,GAC/F,OAAO,CAAC,iBAAiB,CAAC,CAU5B;AAED,iFAAiF;AACjF,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,aAAa,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CAuB5B"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { customFieldDefinitionSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
|
|
3
|
+
import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
|
|
4
|
+
import { isSupportedEntityType } from './custom-field-registry.js';
|
|
5
|
+
/**
|
|
6
|
+
* Transactional apply functions for custom-field definition/option writes
|
|
7
|
+
* (feature 061, contracts/custom-fields-product-host.md §3).
|
|
8
|
+
*
|
|
9
|
+
* Every function runs entirely on the CALLER-provided EntityManager and
|
|
10
|
+
* enforces the same invariants as the public CRUD (duplicate key, options
|
|
11
|
+
* rule, entity type known, value-type lock via the probe binding,
|
|
12
|
+
* option-in-use). They dispatch NO command, write NO audit entry, and publish
|
|
13
|
+
* NO cache invalidation — a host command audits its composite operation and
|
|
14
|
+
* calls `publishInvalidate` post-commit; the module's own definition commands
|
|
15
|
+
* share these bodies so there is a single write path (Principle XIII).
|
|
16
|
+
*/
|
|
17
|
+
/** Typed failures the routes map to HTTP status codes. */
|
|
18
|
+
export class CustomFieldDefinitionError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
constructor(code, message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.name = 'CustomFieldDefinitionError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const SELECT_TYPES = new Set(['select', 'multiselect']);
|
|
27
|
+
/** Select fields require ≥ 1 option; non-select fields must not carry any. */
|
|
28
|
+
export function assertOptionsRule(valueType, options) {
|
|
29
|
+
const isSelect = SELECT_TYPES.has(valueType);
|
|
30
|
+
if (isSelect && options.length === 0) {
|
|
31
|
+
throw new CustomFieldDefinitionError('options_required', 'Select fields require at least one option.');
|
|
32
|
+
}
|
|
33
|
+
if (!isSelect && options.length > 0) {
|
|
34
|
+
throw new CustomFieldDefinitionError('options_forbidden', 'Only select fields may carry options.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Create a definition (+ its options for select types) on the caller's EM. */
|
|
38
|
+
export async function applyCreateDefinition(em, input) {
|
|
39
|
+
// command-coverage-ignore: apply-seam primitive — mutates on the invoking Command's
|
|
40
|
+
// transactional EM; the caller (custom_fields definition command or a host command,
|
|
41
|
+
// e.g. catalog.attribute.*) records the single audit row co-transactionally.
|
|
42
|
+
// Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
|
|
43
|
+
if (!isSupportedEntityType(input.entityType)) {
|
|
44
|
+
throw new CustomFieldDefinitionError('entity_type_unknown', `Unknown entity type "${input.entityType}".`);
|
|
45
|
+
}
|
|
46
|
+
assertOptionsRule(input.valueType, input.options);
|
|
47
|
+
const existing = await em.findOne(CustomFieldDefinition, {
|
|
48
|
+
entityType: input.entityType,
|
|
49
|
+
key: input.key,
|
|
50
|
+
});
|
|
51
|
+
if (existing) {
|
|
52
|
+
throw new CustomFieldDefinitionError('duplicate_key', `A field "${input.key}" already exists on ${input.entityType}.`);
|
|
53
|
+
}
|
|
54
|
+
const def = em.create(CustomFieldDefinition, {
|
|
55
|
+
entityType: input.entityType,
|
|
56
|
+
key: input.key,
|
|
57
|
+
label: input.label,
|
|
58
|
+
labelDefault: input.labelDefault,
|
|
59
|
+
valueType: input.valueType,
|
|
60
|
+
required: input.required,
|
|
61
|
+
sortOrder: input.sortOrder,
|
|
62
|
+
config: input.config,
|
|
63
|
+
});
|
|
64
|
+
if (SELECT_TYPES.has(input.valueType)) {
|
|
65
|
+
// Flush the definition first: options reference it by id (no ORM relation),
|
|
66
|
+
// so MikroORM cannot infer the insert order and would violate the FK.
|
|
67
|
+
await em.flush();
|
|
68
|
+
for (const opt of input.options)
|
|
69
|
+
createOptionEntity(em, def.id, opt);
|
|
70
|
+
}
|
|
71
|
+
return def;
|
|
72
|
+
}
|
|
73
|
+
/** Patch a definition's scalar/label/config fields on the caller's EM. */
|
|
74
|
+
export async function applyUpdateDefinition(em, id, patch, probes) {
|
|
75
|
+
const def = await em.findOne(CustomFieldDefinition, { id });
|
|
76
|
+
if (!def)
|
|
77
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
|
|
78
|
+
// FR-010: changing valueType while values exist is rejected (probe binding).
|
|
79
|
+
if (patch.valueType !== undefined && patch.valueType !== def.valueType && probes) {
|
|
80
|
+
const inUse = await probes.hasStoredValues(em, def.entityType, def.key);
|
|
81
|
+
if (inUse) {
|
|
82
|
+
throw new CustomFieldDefinitionError('value_type_locked', `Cannot change the value type of "${def.key}" while records hold values. Clear them first.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (patch.label !== undefined)
|
|
86
|
+
def.label = patch.label;
|
|
87
|
+
if (patch.labelDefault !== undefined)
|
|
88
|
+
def.labelDefault = patch.labelDefault;
|
|
89
|
+
if (patch.valueType !== undefined)
|
|
90
|
+
def.valueType = patch.valueType;
|
|
91
|
+
if (patch.required !== undefined)
|
|
92
|
+
def.required = patch.required;
|
|
93
|
+
if (patch.sortOrder !== undefined)
|
|
94
|
+
def.sortOrder = patch.sortOrder;
|
|
95
|
+
if (patch.config !== undefined)
|
|
96
|
+
def.config = patch.config;
|
|
97
|
+
return def;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Rename a definition's key on the caller's EM — the one path by which a key
|
|
101
|
+
* changes after create (`specs/134-paid-module-extraction/research.md` D12).
|
|
102
|
+
*
|
|
103
|
+
* Not an edit: `UpdateCustomFieldDefinitionRequest` omits `key` on purpose,
|
|
104
|
+
* because every host stores its values **under** the key and an ordinary rename
|
|
105
|
+
* would orphan them. The caller of this function is therefore one that renames
|
|
106
|
+
* the host's values in the same transaction — today a connector repairing keys
|
|
107
|
+
* it derived itself, with `catalog` moving the product values beside it.
|
|
108
|
+
*
|
|
109
|
+
* `expectedKey` is the key the caller planned from. A definition whose key has
|
|
110
|
+
* moved since is refused with `key_changed` rather than renamed from a state the
|
|
111
|
+
* caller never saw. Flushes before it returns, so a caller sequencing a
|
|
112
|
+
* two-phase rename (park, then move) gets its statements in the order it asked
|
|
113
|
+
* for them and a unique-index violation surfaces at the call that caused it.
|
|
114
|
+
*/
|
|
115
|
+
export async function applyRenameDefinitionKey(em, id, expectedKey, newKey) {
|
|
116
|
+
// command-coverage-ignore: apply-seam primitive — mutates on the caller's
|
|
117
|
+
// transactional EM. Its one caller is a system-invariant repair with no
|
|
118
|
+
// operator and no actor (a connector re-keying the attributes it derived,
|
|
119
|
+
// committed atomically with the host's value rename and the connector's own
|
|
120
|
+
// repair checkpoint); an audit entry for it would have nobody to attribute.
|
|
121
|
+
// Contract: specs/134-paid-module-extraction/research.md D12.
|
|
122
|
+
const def = await em.findOne(CustomFieldDefinition, { id });
|
|
123
|
+
if (!def)
|
|
124
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
|
|
125
|
+
if (def.key !== expectedKey) {
|
|
126
|
+
throw new CustomFieldDefinitionError('key_changed', `Custom field ${id} is keyed "${def.key}", not "${expectedKey}"; nothing was renamed.`);
|
|
127
|
+
}
|
|
128
|
+
if (!customFieldDefinitionSchema.shape.key.safeParse(newKey).success) {
|
|
129
|
+
throw new CustomFieldDefinitionError('invalid_key', `"${newKey}" is not a valid field key.`);
|
|
130
|
+
}
|
|
131
|
+
if (newKey === def.key)
|
|
132
|
+
return def;
|
|
133
|
+
const holder = await em.findOne(CustomFieldDefinition, { entityType: def.entityType, key: newKey });
|
|
134
|
+
if (holder) {
|
|
135
|
+
throw new CustomFieldDefinitionError('duplicate_key', `A field "${newKey}" already exists on ${def.entityType}.`);
|
|
136
|
+
}
|
|
137
|
+
def.key = newKey;
|
|
138
|
+
await em.flush();
|
|
139
|
+
return def;
|
|
140
|
+
}
|
|
141
|
+
/** Delete a definition on the caller's EM. Options cascade; host values stay dormant (FR-010). */
|
|
142
|
+
export async function applyDeleteDefinition(em, id) {
|
|
143
|
+
// command-coverage-ignore: apply-seam primitive — mutates on the invoking Command's
|
|
144
|
+
// transactional EM; the caller (custom_fields definition command or a host command,
|
|
145
|
+
// e.g. catalog.attribute.*) records the single audit row co-transactionally.
|
|
146
|
+
// Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
|
|
147
|
+
const def = await em.findOne(CustomFieldDefinition, { id });
|
|
148
|
+
if (!def)
|
|
149
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
|
|
150
|
+
const options = await em.find(CustomFieldOption, { definitionId: id });
|
|
151
|
+
for (const o of options)
|
|
152
|
+
em.remove(o);
|
|
153
|
+
em.remove(def);
|
|
154
|
+
return def;
|
|
155
|
+
}
|
|
156
|
+
/** Add one option to a select/multiselect definition on the caller's EM. */
|
|
157
|
+
export async function applyCreateOption(em, definitionId, input) {
|
|
158
|
+
const def = await em.findOne(CustomFieldDefinition, { id: definitionId });
|
|
159
|
+
if (!def)
|
|
160
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
|
|
161
|
+
if (!SELECT_TYPES.has(def.valueType)) {
|
|
162
|
+
throw new CustomFieldDefinitionError('options_forbidden', `Field "${def.key}" does not take options.`);
|
|
163
|
+
}
|
|
164
|
+
return createOptionEntity(em, definitionId, input);
|
|
165
|
+
}
|
|
166
|
+
/** Patch one option's label/default/ordering on the caller's EM. */
|
|
167
|
+
export async function applyUpdateOption(em, definitionId, optionId, patch) {
|
|
168
|
+
const option = await em.findOne(CustomFieldOption, { id: optionId });
|
|
169
|
+
if (!option || option.definitionId !== definitionId) {
|
|
170
|
+
throw new CustomFieldDefinitionError('not_found', `Option ${optionId} not found.`);
|
|
171
|
+
}
|
|
172
|
+
if (patch.label !== undefined)
|
|
173
|
+
option.label = patch.label;
|
|
174
|
+
if (patch.labelDefault !== undefined)
|
|
175
|
+
option.labelDefault = patch.labelDefault;
|
|
176
|
+
if (patch.isDefault !== undefined)
|
|
177
|
+
option.isDefault = patch.isDefault;
|
|
178
|
+
if (patch.sortOrder !== undefined)
|
|
179
|
+
option.sortOrder = patch.sortOrder;
|
|
180
|
+
return option;
|
|
181
|
+
}
|
|
182
|
+
/** Delete one option on the caller's EM (in-use guard via the probe binding). */
|
|
183
|
+
export async function applyDeleteOption(em, definitionId, optionId, probes) {
|
|
184
|
+
// command-coverage-ignore: apply-seam primitive — mutates on the invoking Command's
|
|
185
|
+
// transactional EM; the caller (custom_fields definition command or a host command,
|
|
186
|
+
// e.g. catalog.attribute.*) records the single audit row co-transactionally.
|
|
187
|
+
// Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
|
|
188
|
+
const def = await em.findOne(CustomFieldDefinition, { id: definitionId });
|
|
189
|
+
if (!def)
|
|
190
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
|
|
191
|
+
const option = await em.findOne(CustomFieldOption, { id: optionId });
|
|
192
|
+
if (!option || option.definitionId !== definitionId) {
|
|
193
|
+
throw new CustomFieldDefinitionError('not_found', `Option ${optionId} not found.`);
|
|
194
|
+
}
|
|
195
|
+
// FR-010: an in-use option cannot be removed while records reference it.
|
|
196
|
+
if (probes) {
|
|
197
|
+
const inUse = await probes.isOptionInUse(em, def.entityType, def.key, option.value);
|
|
198
|
+
if (inUse) {
|
|
199
|
+
throw new CustomFieldDefinitionError('option_in_use', `Option "${option.value}" is in use and cannot be removed.`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
em.remove(option);
|
|
203
|
+
return option;
|
|
204
|
+
}
|
|
205
|
+
function createOptionEntity(em, definitionId, input) {
|
|
206
|
+
// command-coverage-ignore: apply-seam primitive, exactly like the two exported
|
|
207
|
+
// functions that call it — the `em.create` queues the row on the invoking
|
|
208
|
+
// Command's transactional EM and is flushed by that Command, which records the
|
|
209
|
+
// single audit row co-transactionally. The marker belongs here rather than only
|
|
210
|
+
// on the callers because this is the function that writes (D-89).
|
|
211
|
+
// Contract: specs/061-attributes-on-custom-fields/contracts/custom-fields-product-host.md.
|
|
212
|
+
return em.create(CustomFieldOption, {
|
|
213
|
+
definitionId,
|
|
214
|
+
value: input.value,
|
|
215
|
+
label: input.label,
|
|
216
|
+
labelDefault: input.labelDefault,
|
|
217
|
+
isDefault: input.isDefault,
|
|
218
|
+
sortOrder: input.sortOrder,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=custom-field-definition-apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-field-definition-apply.js","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CreateCustomFieldDefinitionRequest, CustomFieldDefinitionRecord, CustomFieldOptionDto, CustomFieldOptionRecord, SupportedEntityType, UpdateCustomFieldDefinitionRequest } from '@endora-commerce/contracts';
|
|
3
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
4
|
+
import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
|
|
5
|
+
import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
|
|
6
|
+
import type { CustomFieldDefinitionsCache, CachedDefinition } from './custom-field-definitions-cache.js';
|
|
7
|
+
import { CustomFieldDefinitionError } from './custom-field-definition-apply.js';
|
|
8
|
+
import type { CustomFieldValueService } from './custom-field-value.service.js';
|
|
9
|
+
import type { DefinitionSource } from './custom-field-value.service.js';
|
|
10
|
+
import type { CustomFieldDefinitionApplyApi } from '../../ports/index.js';
|
|
11
|
+
export { CustomFieldDefinitionError };
|
|
12
|
+
/**
|
|
13
|
+
* CustomFieldDefinitionService (feature 055) — CRUD over definitions and options.
|
|
14
|
+
*
|
|
15
|
+
* All mutations run through the Command Bus (Principle XIII); the bus writes the
|
|
16
|
+
* audit entry, so this service never calls the audit writer by hand. Reads are
|
|
17
|
+
* served from the per-`entityType` cache and every committed mutation publishes a
|
|
18
|
+
* cross-process invalidation. Implements {@link DefinitionSource} so the value
|
|
19
|
+
* service can validate host writes against the live definitions, and
|
|
20
|
+
* {@link CustomFieldDefinitionApplyApi} so host modules can mutate definitions
|
|
21
|
+
* co-transactionally with their own data (feature 061).
|
|
22
|
+
*/
|
|
23
|
+
export declare class CustomFieldDefinitionService implements DefinitionSource, CustomFieldDefinitionApplyApi {
|
|
24
|
+
private readonly emFactory;
|
|
25
|
+
private readonly cache;
|
|
26
|
+
private readonly commandBus;
|
|
27
|
+
/** Set post-construction to break the definition⇄value service cycle (used by change guards). */
|
|
28
|
+
private valueService?;
|
|
29
|
+
constructor(emFactory: () => EntityManager, cache: CustomFieldDefinitionsCache, commandBus: CommandBus);
|
|
30
|
+
setValueService(v: CustomFieldValueService): void;
|
|
31
|
+
/** Live definitions (with options) for one entity type, cached per type. */
|
|
32
|
+
listForEntity(entityType: SupportedEntityType): Promise<CachedDefinition[]>;
|
|
33
|
+
/**
|
|
34
|
+
* The same list, past the cache, and every other process's cache with it
|
|
35
|
+
* (D-97.1).
|
|
36
|
+
*
|
|
37
|
+
* A consumer that composes its own rows against these definitions is the only
|
|
38
|
+
* party that can *detect* a stale entry — it knows what its rows say — and
|
|
39
|
+
* this module is the only one that may *resolve* it, because the cache is its
|
|
40
|
+
* own. The invalidation is the same fan-out a committed definition Command
|
|
41
|
+
* publishes, so the reload converges every process rather than only this one.
|
|
42
|
+
*
|
|
43
|
+
* This is what `catalog` used to do by calling `publishInvalidate` through an
|
|
44
|
+
* optional widening of the read port, which the `lazyPort` proxy made
|
|
45
|
+
* always-truthy and therefore always wrong when the real provider lacked it.
|
|
46
|
+
*/
|
|
47
|
+
listForEntityFresh(entityType: SupportedEntityType): Promise<CachedDefinition[]>;
|
|
48
|
+
private loadForEntity;
|
|
49
|
+
/** All definitions across every (or one) entity type, options embedded. */
|
|
50
|
+
listAll(entityType?: SupportedEntityType): Promise<CachedDefinition[]>;
|
|
51
|
+
getById(id: string): Promise<CachedDefinition | null>;
|
|
52
|
+
create(input: CreateCustomFieldDefinitionRequest): Promise<CustomFieldDefinition>;
|
|
53
|
+
update(id: string, patch: UpdateCustomFieldDefinitionRequest): Promise<CustomFieldDefinition>;
|
|
54
|
+
delete(id: string): Promise<void>;
|
|
55
|
+
createOption(definitionId: string, input: CustomFieldOptionDto): Promise<CustomFieldOption>;
|
|
56
|
+
updateOption(definitionId: string, optionId: string, patch: Partial<Pick<CustomFieldOptionDto, 'label' | 'labelDefault' | 'isDefault' | 'sortOrder'>>): Promise<CustomFieldOption>;
|
|
57
|
+
deleteOption(definitionId: string, optionId: string): Promise<void>;
|
|
58
|
+
applyCreate(em: EntityManager, input: CreateCustomFieldDefinitionRequest): Promise<CustomFieldDefinitionRecord>;
|
|
59
|
+
applyUpdate(em: EntityManager, id: string, patch: UpdateCustomFieldDefinitionRequest): Promise<CustomFieldDefinitionRecord>;
|
|
60
|
+
applyRenameKey(em: EntityManager, id: string, expectedKey: string, newKey: string): Promise<CustomFieldDefinitionRecord>;
|
|
61
|
+
applyDelete(em: EntityManager, id: string): Promise<void>;
|
|
62
|
+
applyCreateOption(em: EntityManager, definitionId: string, input: CustomFieldOptionDto): Promise<CustomFieldOptionRecord>;
|
|
63
|
+
applyUpdateOption(em: EntityManager, definitionId: string, optionId: string, patch: Partial<Pick<CustomFieldOptionDto, 'label' | 'labelDefault' | 'isDefault' | 'sortOrder'>>): Promise<CustomFieldOptionRecord>;
|
|
64
|
+
applyDeleteOption(em: EntityManager, definitionId: string, optionId: string): Promise<void>;
|
|
65
|
+
/** Post-commit cache fan-out for apply-seam callers (also used internally after every command). */
|
|
66
|
+
publishInvalidate(entityType: SupportedEntityType): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=custom-field-definition.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-field-definition.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
|
|
2
|
+
import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
|
|
3
|
+
import { isSupportedEntityType } from './custom-field-registry.js';
|
|
4
|
+
import { createDefinitionCommand, createOptionCommand, deleteDefinitionCommand, deleteOptionCommand, updateDefinitionCommand, updateOptionCommand, } from '../commands/definition-commands.js';
|
|
5
|
+
import { applyCreateDefinition, applyCreateOption, applyDeleteDefinition, applyDeleteOption, applyRenameDefinitionKey, applyUpdateDefinition, applyUpdateOption, assertOptionsRule, CustomFieldDefinitionError, } from './custom-field-definition-apply.js';
|
|
6
|
+
import { toCustomFieldDefinitionRecord, toCustomFieldOptionRecord, } from './custom-field-read-port.js';
|
|
7
|
+
// Feature 061 — the error class moved next to the shared apply functions;
|
|
8
|
+
// re-exported here so existing imports keep working.
|
|
9
|
+
export { CustomFieldDefinitionError };
|
|
10
|
+
const SELECT_TYPES = new Set(['select', 'multiselect']);
|
|
11
|
+
/**
|
|
12
|
+
* CustomFieldDefinitionService (feature 055) — CRUD over definitions and options.
|
|
13
|
+
*
|
|
14
|
+
* All mutations run through the Command Bus (Principle XIII); the bus writes the
|
|
15
|
+
* audit entry, so this service never calls the audit writer by hand. Reads are
|
|
16
|
+
* served from the per-`entityType` cache and every committed mutation publishes a
|
|
17
|
+
* cross-process invalidation. Implements {@link DefinitionSource} so the value
|
|
18
|
+
* service can validate host writes against the live definitions, and
|
|
19
|
+
* {@link CustomFieldDefinitionApplyApi} so host modules can mutate definitions
|
|
20
|
+
* co-transactionally with their own data (feature 061).
|
|
21
|
+
*/
|
|
22
|
+
export class CustomFieldDefinitionService {
|
|
23
|
+
emFactory;
|
|
24
|
+
cache;
|
|
25
|
+
commandBus;
|
|
26
|
+
/** Set post-construction to break the definition⇄value service cycle (used by change guards). */
|
|
27
|
+
valueService;
|
|
28
|
+
constructor(emFactory, cache, commandBus) {
|
|
29
|
+
this.emFactory = emFactory;
|
|
30
|
+
this.cache = cache;
|
|
31
|
+
this.commandBus = commandBus;
|
|
32
|
+
}
|
|
33
|
+
setValueService(v) {
|
|
34
|
+
this.valueService = v;
|
|
35
|
+
}
|
|
36
|
+
// ---- Reads --------------------------------------------------------------
|
|
37
|
+
/** Live definitions (with options) for one entity type, cached per type. */
|
|
38
|
+
async listForEntity(entityType) {
|
|
39
|
+
if (!isSupportedEntityType(entityType))
|
|
40
|
+
return [];
|
|
41
|
+
return this.cache.getForEntity(entityType, () => this.loadForEntity(entityType));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The same list, past the cache, and every other process's cache with it
|
|
45
|
+
* (D-97.1).
|
|
46
|
+
*
|
|
47
|
+
* A consumer that composes its own rows against these definitions is the only
|
|
48
|
+
* party that can *detect* a stale entry — it knows what its rows say — and
|
|
49
|
+
* this module is the only one that may *resolve* it, because the cache is its
|
|
50
|
+
* own. The invalidation is the same fan-out a committed definition Command
|
|
51
|
+
* publishes, so the reload converges every process rather than only this one.
|
|
52
|
+
*
|
|
53
|
+
* This is what `catalog` used to do by calling `publishInvalidate` through an
|
|
54
|
+
* optional widening of the read port, which the `lazyPort` proxy made
|
|
55
|
+
* always-truthy and therefore always wrong when the real provider lacked it.
|
|
56
|
+
*/
|
|
57
|
+
async listForEntityFresh(entityType) {
|
|
58
|
+
if (!isSupportedEntityType(entityType))
|
|
59
|
+
return [];
|
|
60
|
+
await this.cache.publishInvalidate(entityType);
|
|
61
|
+
return this.listForEntity(entityType);
|
|
62
|
+
}
|
|
63
|
+
async loadForEntity(entityType) {
|
|
64
|
+
const em = this.emFactory();
|
|
65
|
+
const definitions = await em.find(CustomFieldDefinition, { entityType }, { orderBy: { sortOrder: 'asc', key: 'asc' } });
|
|
66
|
+
if (definitions.length === 0)
|
|
67
|
+
return [];
|
|
68
|
+
const options = await em.find(CustomFieldOption, { definitionId: { $in: definitions.map((d) => d.id) } }, { orderBy: { sortOrder: 'asc' } });
|
|
69
|
+
const byDef = new Map();
|
|
70
|
+
for (const o of options) {
|
|
71
|
+
const list = byDef.get(o.definitionId) ?? [];
|
|
72
|
+
list.push(o);
|
|
73
|
+
byDef.set(o.definitionId, list);
|
|
74
|
+
}
|
|
75
|
+
return definitions.map((definition) => ({
|
|
76
|
+
definition,
|
|
77
|
+
options: byDef.get(definition.id) ?? [],
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
/** All definitions across every (or one) entity type, options embedded. */
|
|
81
|
+
async listAll(entityType) {
|
|
82
|
+
if (entityType)
|
|
83
|
+
return this.listForEntity(entityType);
|
|
84
|
+
const em = this.emFactory();
|
|
85
|
+
const definitions = await em.find(CustomFieldDefinition, {}, { orderBy: { entityType: 'asc', sortOrder: 'asc', key: 'asc' } });
|
|
86
|
+
const options = await em.find(CustomFieldOption, {}, { orderBy: { sortOrder: 'asc' } });
|
|
87
|
+
const byDef = new Map();
|
|
88
|
+
for (const o of options) {
|
|
89
|
+
const list = byDef.get(o.definitionId) ?? [];
|
|
90
|
+
list.push(o);
|
|
91
|
+
byDef.set(o.definitionId, list);
|
|
92
|
+
}
|
|
93
|
+
return definitions.map((definition) => ({ definition, options: byDef.get(definition.id) ?? [] }));
|
|
94
|
+
}
|
|
95
|
+
async getById(id) {
|
|
96
|
+
const em = this.emFactory();
|
|
97
|
+
const definition = await em.findOne(CustomFieldDefinition, { id });
|
|
98
|
+
if (!definition)
|
|
99
|
+
return null;
|
|
100
|
+
const options = await em.find(CustomFieldOption, { definitionId: id }, { orderBy: { sortOrder: 'asc' } });
|
|
101
|
+
return { definition, options };
|
|
102
|
+
}
|
|
103
|
+
// ---- Mutations (via Command Bus) ----------------------------------------
|
|
104
|
+
async create(input) {
|
|
105
|
+
if (!isSupportedEntityType(input.entityType)) {
|
|
106
|
+
throw new CustomFieldDefinitionError('entity_type_unknown', `Unknown entity type "${input.entityType}".`);
|
|
107
|
+
}
|
|
108
|
+
assertOptionsRule(input.valueType, input.options);
|
|
109
|
+
const existing = await this.emFactory().findOne(CustomFieldDefinition, {
|
|
110
|
+
entityType: input.entityType,
|
|
111
|
+
key: input.key,
|
|
112
|
+
});
|
|
113
|
+
if (existing) {
|
|
114
|
+
throw new CustomFieldDefinitionError('duplicate_key', `A field "${input.key}" already exists on ${input.entityType}.`);
|
|
115
|
+
}
|
|
116
|
+
const def = await this.commandBus.run(createDefinitionCommand(input));
|
|
117
|
+
await this.cache.publishInvalidate(input.entityType);
|
|
118
|
+
return def;
|
|
119
|
+
}
|
|
120
|
+
async update(id, patch) {
|
|
121
|
+
const current = await this.emFactory().findOne(CustomFieldDefinition, { id });
|
|
122
|
+
if (!current)
|
|
123
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
|
|
124
|
+
// FR-010: changing valueType while values exist is rejected.
|
|
125
|
+
if (patch.valueType !== undefined && patch.valueType !== current.valueType && this.valueService) {
|
|
126
|
+
const inUse = await this.valueService.hasStoredValues(this.emFactory(), current.entityType, current.key);
|
|
127
|
+
if (inUse) {
|
|
128
|
+
throw new CustomFieldDefinitionError('value_type_locked', `Cannot change the value type of "${current.key}" while records hold values. Clear them first.`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const def = await this.commandBus.run(updateDefinitionCommand(id, patch, this.valueService));
|
|
132
|
+
await this.cache.publishInvalidate(current.entityType);
|
|
133
|
+
return def;
|
|
134
|
+
}
|
|
135
|
+
async delete(id) {
|
|
136
|
+
const current = await this.emFactory().findOne(CustomFieldDefinition, { id });
|
|
137
|
+
if (!current)
|
|
138
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${id} not found.`);
|
|
139
|
+
await this.commandBus.run(deleteDefinitionCommand(id));
|
|
140
|
+
await this.cache.publishInvalidate(current.entityType);
|
|
141
|
+
}
|
|
142
|
+
async createOption(definitionId, input) {
|
|
143
|
+
const def = await this.emFactory().findOne(CustomFieldDefinition, { id: definitionId });
|
|
144
|
+
if (!def)
|
|
145
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
|
|
146
|
+
if (!SELECT_TYPES.has(def.valueType)) {
|
|
147
|
+
throw new CustomFieldDefinitionError('options_forbidden', `Field "${def.key}" does not take options.`);
|
|
148
|
+
}
|
|
149
|
+
const opt = await this.commandBus.run(createOptionCommand(definitionId, input));
|
|
150
|
+
await this.cache.publishInvalidate(def.entityType);
|
|
151
|
+
return opt;
|
|
152
|
+
}
|
|
153
|
+
async updateOption(definitionId, optionId, patch) {
|
|
154
|
+
const def = await this.emFactory().findOne(CustomFieldDefinition, { id: definitionId });
|
|
155
|
+
if (!def)
|
|
156
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
|
|
157
|
+
const opt = await this.commandBus.run(updateOptionCommand(definitionId, optionId, patch));
|
|
158
|
+
await this.cache.publishInvalidate(def.entityType);
|
|
159
|
+
return opt;
|
|
160
|
+
}
|
|
161
|
+
async deleteOption(definitionId, optionId) {
|
|
162
|
+
const def = await this.emFactory().findOne(CustomFieldDefinition, { id: definitionId });
|
|
163
|
+
if (!def)
|
|
164
|
+
throw new CustomFieldDefinitionError('not_found', `Custom field ${definitionId} not found.`);
|
|
165
|
+
const option = await this.emFactory().findOne(CustomFieldOption, { id: optionId });
|
|
166
|
+
if (!option)
|
|
167
|
+
throw new CustomFieldDefinitionError('not_found', `Option ${optionId} not found.`);
|
|
168
|
+
// FR-010: an in-use option cannot be removed while records reference it.
|
|
169
|
+
if (this.valueService) {
|
|
170
|
+
const inUse = await this.valueService.isOptionInUse(this.emFactory(), def.entityType, def.key, option.value);
|
|
171
|
+
if (inUse) {
|
|
172
|
+
throw new CustomFieldDefinitionError('option_in_use', `Option "${option.value}" is in use and cannot be removed.`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
await this.commandBus.run(deleteOptionCommand(definitionId, optionId, this.valueService));
|
|
176
|
+
await this.cache.publishInvalidate(def.entityType);
|
|
177
|
+
}
|
|
178
|
+
// ---- Transactional apply seam (feature 061, CustomFieldDefinitionApplyApi)
|
|
179
|
+
async applyCreate(em, input) {
|
|
180
|
+
return toCustomFieldDefinitionRecord(await applyCreateDefinition(em, input));
|
|
181
|
+
}
|
|
182
|
+
async applyUpdate(em, id, patch) {
|
|
183
|
+
return toCustomFieldDefinitionRecord(await applyUpdateDefinition(em, id, patch, this.valueService));
|
|
184
|
+
}
|
|
185
|
+
async applyRenameKey(em, id, expectedKey, newKey) {
|
|
186
|
+
return toCustomFieldDefinitionRecord(await applyRenameDefinitionKey(em, id, expectedKey, newKey));
|
|
187
|
+
}
|
|
188
|
+
async applyDelete(em, id) {
|
|
189
|
+
await applyDeleteDefinition(em, id);
|
|
190
|
+
}
|
|
191
|
+
async applyCreateOption(em, definitionId, input) {
|
|
192
|
+
return toCustomFieldOptionRecord(await applyCreateOption(em, definitionId, input));
|
|
193
|
+
}
|
|
194
|
+
async applyUpdateOption(em, definitionId, optionId, patch) {
|
|
195
|
+
return toCustomFieldOptionRecord(await applyUpdateOption(em, definitionId, optionId, patch));
|
|
196
|
+
}
|
|
197
|
+
async applyDeleteOption(em, definitionId, optionId) {
|
|
198
|
+
await applyDeleteOption(em, definitionId, optionId, this.valueService);
|
|
199
|
+
}
|
|
200
|
+
/** Post-commit cache fan-out for apply-seam callers (also used internally after every command). */
|
|
201
|
+
async publishInvalidate(entityType) {
|
|
202
|
+
await this.cache.publishInvalidate(entityType);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=custom-field-definition.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-field-definition.service.js","sourceRoot":"","sources":["../../../src/backend/services/custom-field-definition.service.ts"],"names":[],"mappings":"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"}
|