UNPKG

@hiki9/rich-domain

Version:

Rich Domain is a library that provides a set of tools to help you build complex business logic in NodeJS using Domain Driven Design principles.

279 lines 12.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EntityMetaHistory = void 0; const lodash_1 = __importDefault(require("lodash")); const validator_1 = __importDefault(require("../../utils/validator")); const errors_1 = require("../errors"); const history_snapshot_1 = require("./history-snapshot"); class EntityMetaHistory { constructor(props, callbacks) { this.onChange = []; this.initialProps = lodash_1.default.cloneDeep(props); this.snapshots = []; this.callbacks = callbacks; } addSnapshot(data) { const snapshot = new history_snapshot_1.Snapshot({ props: null, //lodash.cloneDeep(data.props), trace: { updatedAt: data.trace.updatedAt, update: data.trace.update, fieldKey: data.trace.fieldKey, instanceKey: data.trace.instanceKey, instanceId: data.trace.instanceId, }, }); if (!data.trace.action) { snapshot.trace.from = data.trace.from; snapshot.trace.to = data.trace.to; } else { snapshot.trace.action = data.trace.action; } if (typeof data.trace.position !== 'undefined') { snapshot.trace.position = data.trace.position; } this.snapshots.push(snapshot); if (this.callbacks?.onAddedSnapshot) { this.callbacks.onAddedSnapshot(snapshot); } if (this.onChange.length) { this.onChange.forEach((callback) => { callback(snapshot); }); } } deepWatch(rootEntity, onChangeCallback, childrenEntity) { const entityTarget = childrenEntity ?? rootEntity; const currentProps = entityTarget?.['props']; Object.entries(currentProps).forEach(([key, _value]) => { const value = _value; if (Array.isArray(value)) { value.forEach((currentValue) => { if (currentValue?.isEntity && currentValue?.history) { currentValue.history.onChange.push((snapshot) => { const clonedSnapshot = lodash_1.default.cloneDeep(snapshot); clonedSnapshot.fromDeepWatch = true; clonedSnapshot.deepWatchPath = key; onChangeCallback(rootEntity, clonedSnapshot); }); entityTarget.history?.deepWatch(rootEntity, onChangeCallback, currentValue); } }); } else if (value?.isEntity && value?.history) { value.history.onChange.push((snapshot) => { const clonedSnapshot = lodash_1.default.cloneDeep(snapshot); clonedSnapshot.fromDeepWatch = true; clonedSnapshot.deepWatchPath = key; onChangeCallback(rootEntity, clonedSnapshot); }); entityTarget.history?.deepWatch(rootEntity, onChangeCallback, value); } }); } getSnapshotFromUpdatedKey(key) { return this.snapshots.filter((snapshot) => { if (snapshot.trace.update === key) { return true; } const splitted = snapshot.trace.update.split('.'); return splitted.includes(key); }); } hasChange(key) { return this.snapshots.some((snapshot) => { if (snapshot.trace.update === key) { return true; } const splitted = snapshot.trace.update.split('.'); return splitted.includes(key); }); } subs(entity, onChange, parents = [], result = []) { const history = entity.history; if (!(entity.isEntity)) { throw new errors_1.DomainError('Entity is not an entity', entity); } if (!history) { throw new errors_1.DomainError('History is not enabled for this entity', entity); } const currentResult = history.snapshots.map((snapshot) => onChange(snapshot, ...parents)); result.push(...currentResult); Object.values(entity['props']).forEach((value) => { if (Array.isArray(value)) { value.forEach((possibleEntity) => { if (possibleEntity?.isEntity) { const partial = possibleEntity.history.subs(possibleEntity, onChange, [...parents, entity]); result.push(...partial); } }); } else if (value?.isEntity) { const partial = value.history.subs(value, onChange, [...parents, entity]); result.push(...partial); } }); return result; } subscribe(entity, subscribeProps, initialProps) { if (!entity) { throw new errors_1.DomainError('History is not enabled for this entity', entity); } if (typeof subscribeProps !== 'object') { throw new errors_1.ApplicationLevelError('Subscribe props must be an object', subscribeProps); } const onChange = subscribeProps['onChange']; if (typeof onChange === 'function') { if (Array.isArray(entity)) { const { toCreate, toDelete, toUpdate } = this.resolve(initialProps ?? [], entity); const trace = entity.map(e => e.history.snapshots.map((snapshot) => snapshot.trace)).flat(); onChange({ entity, toCreate, toUpdate, toDelete }, trace); } else { const trace = entity.history.snapshots.map((snapshot) => snapshot.trace); onChange({ entity }, trace); } } Object.entries(subscribeProps).forEach((entries) => { const [key, value] = entries; if (key === 'onChange') return; if (typeof value !== 'object') return; if (!Array.isArray(entity)) { const nextEntity = entity['props'][key]; const nextInitialProps = entity.history.initialProps[key]; if (nextEntity?.isEntity) { return this.subscribe(nextEntity, value, nextInitialProps); } if (nextEntity?.isValueObject) { const trace = entity.history.snapshots.map((snapshot) => snapshot.trace); const onChange = value?.['onChange']; if (trace.length && typeof onChange === 'function') { onChange({ entity: nextEntity }, trace); } return; } if (Array.isArray(nextEntity)) { const everyPropIsEntity = nextEntity.every((prop) => prop?.isEntity); if (everyPropIsEntity) { return this.subscribe(nextEntity, value, nextInitialProps); } const everyPropIsValueObject = nextEntity.every((prop) => prop?.isValueObject); if (everyPropIsValueObject) { const { toCreate, toDelete, toUpdate } = this.resolve(nextInitialProps, nextEntity); const trace = entity.history.snapshots.map((snapshot) => snapshot.trace); const onChange = value?.['onChange']; if (trace.length && typeof onChange === 'function') { onChange({ entity: nextEntity, toCreate, toUpdate, toDelete }, trace); } } } } else { const nextEntity = entity.flatMap((entity) => entity['props'][key]); const nextInitialProps = entity.flatMap((entity) => entity.history.initialProps[key]); const isEntity = nextEntity.every((entity) => entity?.isEntity); if (isEntity) { return this.subscribe(nextEntity, value, nextInitialProps); } const isValueObject = nextEntity.every((entity) => entity?.isValueObject); if (isValueObject) { const { toCreate, toDelete, toUpdate } = this.resolve(nextInitialProps, nextEntity); const trace = entity.map(e => e.history.snapshots.map((snapshot) => snapshot.trace)).flat(); const onChange = value?.['onChange']; if (trace.length && typeof onChange === 'function') { onChange({ entity: nextEntity, toCreate, toUpdate, toDelete }, trace); return; } } } }); } deepSearchSnapshots(entity, key) { const propertyOfKey = entity?.['props']?.[key]; if (!propertyOfKey) { return []; } if (Array.isArray(propertyOfKey)) { const everyPropIsEntity = propertyOfKey.every((prop) => prop?.isEntity); if (!everyPropIsEntity) { return this.getSnapshotFromUpdatedKey(key); } return propertyOfKey.map((prop) => prop.history.snapshots).flat(); } if (!propertyOfKey?.isEntity) { return this.getSnapshotFromUpdatedKey(key); } const history = propertyOfKey.history; if (!(history instanceof EntityMetaHistory)) { throw new errors_1.DomainError('History is not enabled for this entity ->' + key?.toString()); } return history.snapshots; } resolve(initialValues, currentValues) { const { toCreate, toUpdate } = this.resolveEachPropsToUpsert(initialValues, currentValues); return { toCreate, toUpdate, toDelete: this.resolveEachPropsToDelete(initialValues, currentValues), }; } resolveEachPropsToDelete(initialValues, currentValues) { return initialValues.reduce((acc, initialValue) => { const found = currentValues.find((value) => { if (validator_1.default.isValueObject(value)) { return value.isEqual(initialValue); } else if (validator_1.default.isEntity(value) || validator_1.default.isAggregate(value)) { return value.isEqual(initialValue) || value.id.isEqual(initialValue?.id); } else { return value === initialValue; } }); if (!found) { acc.push(initialValue); } return acc; }, []); } resolveEachPropsToUpsert(initialValues, currentValues) { return currentValues.reduce((acc, currentValue) => { let shouldUpdate = false; const found = initialValues.find((initialValue) => { if (validator_1.default.isValueObject(initialValue)) { return initialValue.isEqual(currentValue); } else if (validator_1.default.isEntity(initialValue) || validator_1.default.isAggregate(initialValue)) { const sameID = initialValue.id.isEqual(currentValue.id); const sameProps = initialValue.isEqual(currentValue); if (sameID && !sameProps) { shouldUpdate = true; } return sameID; } else { return initialValue === currentValue; } }); if (found && shouldUpdate) { acc.toUpdate.push(currentValue); } if (!found) { acc.toCreate.push(currentValue); } return acc; }, { toCreate: [], toUpdate: [] }); } } exports.EntityMetaHistory = EntityMetaHistory; //# sourceMappingURL=history.js.map