metis-desk/modules/assets/src/api/domain/asset-definition.entity.ts

140 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-10-08 14:43:34 +00:00
import type { UniqueID } from "@repo/rdx-ddd";
import { DomainEntity } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import { AssetCatalogError } from "./asset-definition";
import type { AssetDefinitionValues } from "./value-objects/asset-definition-values";
export class TemplateField extends DomainEntity<{ fieldId: UniqueID; required: boolean }> {
static create(fieldId: UniqueID, required: boolean, id?: UniqueID) {
return new TemplateField({ fieldId, required }, id);
}
static rehydrate(fieldId: UniqueID, required: boolean, id: UniqueID) {
return new TemplateField({ fieldId, required }, id);
}
get fieldId() {
return this.props.fieldId;
}
get required() {
return this.props.required;
}
}
type DefinitionProps = {
companyId: Maybe<UniqueID>;
stockKey: Maybe<string>;
values: AssetDefinitionValues;
active: boolean;
bindings: readonly TemplateField[];
};
export class AssetDefinition extends DomainEntity<DefinitionProps> {
private constructor(props: DefinitionProps, id?: UniqueID) {
super({ ...props, bindings: Object.freeze([...props.bindings]) }, id);
}
static create(
companyId: Maybe<UniqueID>,
values: AssetDefinitionValues,
active: boolean,
stockKey = Maybe.none<string>()
) {
const bindings = values.fieldIds.map((id, index) =>
TemplateField.create(
id,
values.data.kind === "template" && (values.data.fields[index]?.required ?? false)
)
);
return new AssetDefinition({ companyId, stockKey, values, active, bindings });
}
static rehydrate(props: DefinitionProps, id: UniqueID) {
return new AssetDefinition(props, id);
}
get companyId() {
return this.props.companyId;
}
get stockKey() {
return this.props.stockKey;
}
get values() {
return this.props.values;
}
get active() {
return this.props.active;
}
get bindings() {
return this.props.bindings;
}
update(values: AssetDefinitionValues): Result<AssetDefinition, AssetCatalogError> {
if (this.stockKey.getOrUndefined()) {
return Result.fail(new AssetCatalogError("BUILTIN_PROTECTED"));
}
const previous = this.values.data;
const next = values.data;
if (
previous.kind !== next.kind ||
(previous.kind === "field" &&
next.kind === "field" &&
(previous.type !== next.type ||
previous.choices.some((choice) => !next.choices.includes(choice))))
) {
return Result.fail(new AssetCatalogError("FIELD_TYPE_FIXED"));
}
2026-10-04 19:04:14 +00:00
if (
previous.kind === "relation" &&
next.kind === "relation" &&
previous.symmetric !== next.symmetric
) {
return Result.fail(new AssetCatalogError("RELATION_DIRECTION_FIXED"));
}
if (this.values.equals(values)) {
return Result.ok(this);
}
const bindings = values.fieldIds.map((fieldId, index) =>
TemplateField.create(
fieldId,
next.kind === "template" && (next.fields[index]?.required ?? false),
this.bindings.find((field) => field.fieldId.equals(fieldId))?.id
)
);
return Result.ok(new AssetDefinition({ ...this.props, values, bindings }, this.id));
}
setActive(active: boolean) {
return active === this.active ? this : new AssetDefinition({ ...this.props, active }, this.id);
}
}
export class CatalogAvailability extends DomainEntity<{
companyId: UniqueID;
definitionId: UniqueID;
active: Maybe<boolean>;
}> {
static create(
companyId: UniqueID,
definitionId: UniqueID,
active: Maybe<boolean>,
id?: UniqueID
) {
return new CatalogAvailability({ companyId, definitionId, active }, id);
}
static rehydrate(
companyId: UniqueID,
definitionId: UniqueID,
active: Maybe<boolean>,
id: UniqueID
) {
return new CatalogAvailability({ companyId, definitionId, active }, id);
}
get companyId() {
return this.props.companyId;
}
get definitionId() {
return this.props.definitionId;
}
get active() {
return this.props.active;
}
update(active: Maybe<boolean>) {
return active.getOrUndefined() === this.active.getOrUndefined()
? this
: CatalogAvailability.create(this.companyId, this.definitionId, active, this.id);
}
}