import type { UniqueID } from "@repo/rdx-ddd"; import { AggregateRoot } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { ResourceCatalogError } from "./resource-definition"; import { ResourceRevision } from "./resource-revision.entity"; import type { ResourceSpecification } from "./value-objects/resource-values.vo"; import { ResourceValues } from "./value-objects/resource-values.vo"; type ResourceProps = Readonly<{ companyId: UniqueID; specification: ResourceSpecification; values: ResourceValues; history: readonly ResourceRevision[]; }>; export class Resource extends AggregateRoot { private constructor(props: ResourceProps, id?: UniqueID) { super({ ...props, history: Object.freeze([...props.history]) }, id); } static create( companyId: UniqueID, specification: ResourceSpecification, values: ResourceValues, actorId: UniqueID, recordedAt: string ) { return Result.ok( new Resource({ companyId, specification, values, history: [ResourceRevision.create({ values, specification, actorId, recordedAt })], }) ); } static rehydrate(props: ResourceProps, id: UniqueID) { return new Resource(props, id); } get companyId() { return this.props.companyId; } get specification() { return this.props.specification; } get values() { return this.props.values; } get history() { return this.props.history; } synchronizeSpecification( specification: ResourceSpecification, actorId: UniqueID, recordedAt: string ) { const allowed = new Set(specification.toPrimitive().fields.map((field) => field.fieldId)); const values = ResourceValues.reconstruct( { ...this.values.toPrimitive(), characteristics: Object.fromEntries( Object.entries(this.values.toPrimitive().characteristics).filter(([id]) => allowed.has(id) ) ), }, specification ); if (!values.isSuccess) { return values; } if (this.specification.equals(specification) && this.values.equals(values.data)) { return Result.ok(this); } return Result.ok( new Resource( { ...this.props, specification, values: values.data, history: [ ...this.history, ResourceRevision.create({ specification, values: values.data, actorId, recordedAt }), ], }, this.id ) ); } update( values: ResourceValues, actorId: UniqueID, recordedAt: string ): Result { if (values.toPrimitive().templateId !== this.values.toPrimitive().templateId) { return Result.fail(new ResourceCatalogError("TEMPLATE_FIXED")); } const previous = this.values.toPrimitive().integration; const next = values.toPrimitive().integration; if ( previous?.kind !== next?.kind || (previous?.kind === "mailbox" && next?.kind === "mailbox" && (previous.mailboxKind !== next.mailboxKind || previous.addressFieldId !== next.addressFieldId || previous.contextFieldId !== next.contextFieldId)) || (previous?.kind === "access" && next?.kind === "access" && (previous.referenceFieldId !== next.referenceFieldId || previous.resourceTypeId !== next.resourceTypeId || previous.systemId !== next.systemId || previous.environmentId !== next.environmentId)) ) { return Result.fail(new ResourceCatalogError("CONTEXT_FIXED")); } return Result.ok( this.values.equals(values) ? this : new Resource( { ...this.props, values, history: [ ...this.history, ResourceRevision.create({ values, specification: this.specification, actorId, recordedAt, }), ], }, this.id ) ); } }