| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- import {Service} from '@e22m4u/js-service';
- import {cloneDeep} from '../../utils/index.js';
- import {isPlainObject} from '../../utils/index.js';
- import {transformPromise} from '../../utils/index.js';
- import {EmptyValuesService} from '@e22m4u/js-empty-values';
- import {InvalidArgumentError} from '../../errors/index.js';
- import {ModelDefinitionUtils} from './model-definition-utils.js';
- import {PropertyTransformerRegistry} from './properties/index.js';
- /**
- * Model data transformer.
- */
- export class ModelDataTransformer extends Service {
- /**
- * Transform.
- *
- * @param {string} modelName
- * @param {object} modelData
- * @param {boolean} isPartial
- * @returns {object|Promise<object>}
- */
- transform(modelName, modelData, isPartial = false) {
- if (!isPlainObject(modelData))
- throw new InvalidArgumentError(
- 'The data of the model %v should be an Object, but %v was given.',
- modelName,
- modelData,
- );
- const emptyValuesService = this.getService(EmptyValuesService);
- const modelDefinitionUtils = this.getService(ModelDefinitionUtils);
- const propDefs =
- modelDefinitionUtils.getPropertiesDefinitionInBaseModelHierarchy(
- modelName,
- );
- const propNames = Object.keys(isPartial ? modelData : propDefs);
- const transformedData = cloneDeep(modelData);
- return propNames.reduce((transformedDataOrPromise, propName) => {
- const propDef = propDefs[propName];
- if (!propDef) return transformedDataOrPromise;
- const propType =
- modelDefinitionUtils.getDataTypeFromPropertyDefinition(propDef);
- const propValue = modelData[propName];
- const isEmpty = emptyValuesService.isEmptyByType(propType, propValue);
- if (isEmpty) return transformedDataOrPromise;
- const newPropValueOrPromise = this._transformPropertyValue(
- modelName,
- propName,
- propDef,
- propValue,
- );
- return transformPromise(newPropValueOrPromise, newPropValue => {
- return transformPromise(transformedDataOrPromise, resolvedData => {
- if (newPropValue !== propValue) resolvedData[propName] = newPropValue;
- return resolvedData;
- });
- });
- }, transformedData);
- }
- /**
- * Transform property value.
- *
- * @param {string} modelName
- * @param {string} propName
- * @param {string|object} propDef
- * @param {*} propValue
- * @returns {*|Promise<*>}
- */
- _transformPropertyValue(modelName, propName, propDef, propValue) {
- if (typeof propDef === 'string' || propDef.transform == null)
- return propValue;
- const transformDef = propDef.transform;
- const transformerRegistry = this.getService(PropertyTransformerRegistry);
- const transformFn = (
- value,
- transformerOrName,
- transformerOptions = undefined,
- ) => {
- let transformerName, transformerFn;
- // если второй аргумент является строкой, то строка
- // воспринимается как название зарегистрированного
- // трансформера
- if (typeof transformerOrName === 'string') {
- transformerName = transformerOrName;
- transformerFn = transformerRegistry.getTransformer(transformerName);
- }
- // если второй аргумент является функцией,
- // то функция воспринимается как трансформер
- else if (typeof transformerOrName === 'function') {
- transformerName =
- transformerOrName.name && transformerOrName.name !== 'transform'
- ? transformerOrName.name
- : undefined;
- transformerFn = transformerOrName;
- }
- // если второй аргумент не является строкой
- // и функцией, то выбрасывается ошибка
- else {
- throw new InvalidArgumentError(
- 'Transformer must be a non-empty String or ' +
- 'a Function, but %v was given.',
- transformerOrName,
- );
- }
- const context = {transformerName, modelName, propName};
- return transformerFn(value, transformerOptions, context);
- };
- // если значением опции "transform" является строка,
- // то строка воспринимается как название трансформера
- if (transformDef && typeof transformDef === 'string') {
- return transformFn(propValue, transformDef);
- }
- // если значением опции "transform" является функция,
- // то функция воспринимается как трансформер
- else if (transformDef && typeof transformDef === 'function') {
- return transformFn(propValue, transformDef);
- }
- // если значение опции "transform" является массив, то каждый
- // элемент массива воспринимается как название трансформера
- // или функция-валидатор
- else if (Array.isArray(transformDef)) {
- return transformDef.reduce((valueOrPromise, transformerOrName) => {
- if (
- !transformerOrName ||
- (typeof transformerOrName !== 'string' &&
- typeof transformerOrName !== 'function')
- ) {
- throw new InvalidArgumentError(
- 'The provided option "transform" for the property %v ' +
- 'in the model %v has an Array value that should contain ' +
- 'transformer names or transformer functions, but %v was given.',
- propName,
- modelName,
- transformerOrName,
- );
- }
- return transformPromise(valueOrPromise, value => {
- return transformFn(value, transformerOrName);
- });
- }, propValue);
- }
- // если значение опции "transform" является объектом,
- // то ключи объекта воспринимаются как названия трансформеров,
- // а их значения аргументами
- else if (transformDef !== null && typeof transformDef === 'object') {
- return Object.keys(transformDef).reduce(
- (valueOrPromise, transformerName) => {
- const transformerOptions = transformDef[transformerName];
- return transformPromise(valueOrPromise, value => {
- return transformFn(value, transformerName, transformerOptions);
- });
- },
- propValue,
- );
- }
- // если значение опции "transform" не является строкой,
- // функцией и массивом, то выбрасывается ошибка
- else {
- throw new InvalidArgumentError(
- 'The provided option "transform" for the property %v in the model %v ' +
- 'should be either a transformer name, a transformer function, an array ' +
- 'of transformer names or functions, or an object mapping transformer ' +
- 'names to their arguments, but %v was given.',
- propName,
- modelName,
- transformDef,
- );
- }
- }
- }
|