@e22m4u/js-repository 0.1.6 → 0.1.7
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/README.md +31 -0
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/Adapter.html +1 -1
- package/docs/classes/AdapterLoader.html +1 -1
- package/docs/classes/AdapterRegistry.html +1 -1
- package/docs/classes/BelongsToResolver.html +1 -1
- package/docs/classes/DatasourceDefinitionValidator.html +1 -1
- package/docs/classes/DefinitionRegistry.html +1 -1
- package/docs/classes/FieldsClauseTool.html +1 -1
- package/docs/classes/HasManyResolver.html +1 -1
- package/docs/classes/HasOneResolver.html +1 -1
- package/docs/classes/IncludeClauseTool.html +1 -1
- package/docs/classes/InvalidArgumentError.html +1 -1
- package/docs/classes/InvalidOperatorValueError.html +1 -1
- package/docs/classes/ModelDataSanitizer.html +1 -1
- package/docs/classes/ModelDataTransformer.html +16 -0
- package/docs/classes/ModelDataValidator.html +1 -1
- package/docs/classes/ModelDefinitionUtils.html +1 -1
- package/docs/classes/ModelDefinitionValidator.html +1 -1
- package/docs/classes/NotImplementedError.html +1 -1
- package/docs/classes/OperatorClauseTool.html +1 -1
- package/docs/classes/OrderClauseTool.html +1 -1
- package/docs/classes/PrimaryKeysDefinitionValidator.html +1 -1
- package/docs/classes/PropertiesDefinitionValidator.html +1 -1
- package/docs/classes/PropertyTransformerRegistry.html +20 -0
- package/docs/classes/PropertyValidatorRegistry.html +1 -1
- package/docs/classes/ReferencesManyResolver.html +1 -1
- package/docs/classes/RelationsDefinitionValidator.html +1 -1
- package/docs/classes/Repository.html +1 -1
- package/docs/classes/RepositoryRegistry.html +1 -1
- package/docs/classes/Schema.html +1 -1
- package/docs/classes/SliceClauseTool.html +1 -1
- package/docs/classes/WhereClauseTool.html +1 -1
- package/docs/enums/DataType.html +1 -1
- package/docs/enums/DecoratorTargetType.html +1 -1
- package/docs/enums/RelationType.html +1 -1
- package/docs/functions/capitalize.html +1 -1
- package/docs/functions/cloneDeep.html +1 -1
- package/docs/functions/excludeObjectKeys.html +1 -1
- package/docs/functions/getCtorName.html +1 -1
- package/docs/functions/getDecoratorTargetType.html +1 -1
- package/docs/functions/getValueByPath.html +1 -1
- package/docs/functions/isCtor.html +1 -1
- package/docs/functions/isPureObject.html +1 -1
- package/docs/functions/selectObjectKeys.html +1 -1
- package/docs/functions/singularize.html +1 -1
- package/docs/functions/stringToRegexp.html +1 -1
- package/docs/index.html +16 -1
- package/docs/interfaces/AndClause.html +1 -1
- package/docs/interfaces/Constructor.html +1 -1
- package/docs/interfaces/OrClause.html +1 -1
- package/docs/modules.html +6 -1
- package/docs/types/AnyObject.html +1 -1
- package/docs/types/BelongsToDefinition.html +1 -1
- package/docs/types/DEFAULT_PRIMARY_KEY_PROPERTY_NAME.html +1 -1
- package/docs/types/DatasourceDefinition.html +1 -1
- package/docs/types/FieldsClause.html +1 -1
- package/docs/types/FilterClause.html +1 -1
- package/docs/types/Flatten.html +1 -1
- package/docs/types/FullPropertyDefinition.html +1 -1
- package/docs/types/HasManyDefinition.html +1 -1
- package/docs/types/HasOneDefinition.html +1 -1
- package/docs/types/Identity.html +1 -1
- package/docs/types/IncludeClause.html +1 -1
- package/docs/types/ItemFilterClause.html +1 -1
- package/docs/types/ModelData.html +1 -1
- package/docs/types/ModelDefinition.html +1 -1
- package/docs/types/ModelId.html +1 -1
- package/docs/types/NestedIncludeClause.html +1 -1
- package/docs/types/NormalizedFieldsClause.html +1 -1
- package/docs/types/NormalizedIncludeClause.html +1 -1
- package/docs/types/OperatorClause.html +1 -1
- package/docs/types/OptionalUnlessRequiredId.html +1 -1
- package/docs/types/OrderClause.html +1 -1
- package/docs/types/PartialBy.html +1 -1
- package/docs/types/PartialWithoutId.html +1 -1
- package/docs/types/PolyBelongsToDefinition.html +1 -1
- package/docs/types/PolyHasManyDefinitionWithTargetKeys.html +1 -1
- package/docs/types/PolyHasManyDefinitionWithTargetRelationName.html +1 -1
- package/docs/types/PolyHasOneDefinitionWithTargetKeys.html +1 -1
- package/docs/types/PolyHasOneDefinitionWithTargetRelationName.html +1 -1
- package/docs/types/PropertiesClause.html +1 -1
- package/docs/types/PropertyDefinition.html +1 -1
- package/docs/types/PropertyDefinitionMap.html +1 -1
- package/docs/types/PropertyTransformOptions.html +2 -0
- package/docs/types/PropertyTransformer.html +2 -0
- package/docs/types/PropertyTransformerContext.html +2 -0
- package/docs/types/PropertyValidateOptions.html +1 -1
- package/docs/types/PropertyValidator.html +1 -1
- package/docs/types/PropertyValidatorContext.html +1 -1
- package/docs/types/ReferencesManyDefinition.html +1 -1
- package/docs/types/RelationDefinition.html +1 -1
- package/docs/types/RelationDefinitionMap.html +1 -1
- package/docs/types/WhereClause.html +1 -1
- package/docs/types/WithoutId.html +1 -1
- package/package.json +3 -3
- package/src/adapter/adapter.js +2 -0
- package/src/adapter/adapter.spec.js +9 -3
- package/src/adapter/decorator/data-transformation-decorator.d.ts +14 -0
- package/src/adapter/decorator/data-transformation-decorator.js +54 -0
- package/src/adapter/decorator/data-transformation-decorator.spec.js +95 -0
- package/src/adapter/decorator/data-validation-decorator.js +6 -5
- package/src/adapter/decorator/data-validation-decorator.spec.js +49 -21
- package/src/adapter/decorator/default-values-decorator.spec.js +89 -113
- package/src/adapter/decorator/index.d.ts +1 -0
- package/src/adapter/decorator/index.js +1 -0
- package/src/definition/model/index.d.ts +1 -0
- package/src/definition/model/index.js +1 -0
- package/src/definition/model/model-data-transformer.d.ts +15 -0
- package/src/definition/model/model-data-transformer.js +96 -0
- package/src/definition/model/model-data-transformer.spec.js +534 -0
- package/src/definition/model/model-data-validator.js +10 -12
- package/src/definition/model/model-data-validator.spec.js +133 -32
- package/src/definition/model/properties/index.d.ts +1 -0
- package/src/definition/model/properties/index.js +1 -0
- package/src/definition/model/properties/properties-definition-validator.js +47 -0
- package/src/definition/model/properties/properties-definition-validator.spec.js +52 -0
- package/src/definition/model/properties/property-transformer/builtin/index.d.ts +1 -0
- package/src/definition/model/properties/property-transformer/builtin/index.js +1 -0
- package/src/definition/model/properties/property-transformer/builtin/trim-transformer.d.ts +6 -0
- package/src/definition/model/properties/property-transformer/builtin/trim-transformer.js +19 -0
- package/src/definition/model/properties/property-transformer/builtin/trim-transformer.spec.js +39 -0
- package/src/definition/model/properties/property-transformer/index.d.ts +2 -0
- package/src/definition/model/properties/property-transformer/index.js +2 -0
- package/src/definition/model/properties/property-transformer/property-transformer-registry.d.ts +29 -0
- package/src/definition/model/properties/property-transformer/property-transformer-registry.js +72 -0
- package/src/definition/model/properties/property-transformer/property-transformer-registry.spec.js +129 -0
- package/src/definition/model/properties/property-transformer/property-transformer.d.ts +25 -0
- package/src/definition/model/properties/property-transformer/property-transformer.js +1 -0
- package/src/definition/model/properties/property-validator/builtin/max-length-validator.js +1 -1
- package/src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js +10 -3
- package/src/definition/model/properties/property-validator/builtin/min-length-validator.js +1 -1
- package/src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js +10 -3
- package/src/definition/model/properties/property-validator/builtin/regexp-validator.js +1 -1
- package/src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js +10 -3
- package/src/definition/model/properties/property-validator/property-validator-registry.spec.js +51 -39
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {Service} from '@e22m4u/js-service';
|
|
2
|
+
import {cloneDeep} from '../../utils/index.js';
|
|
3
|
+
import {isPureObject} from '../../utils/index.js';
|
|
4
|
+
import {InvalidArgumentError} from '../../errors/index.js';
|
|
5
|
+
import {ModelDefinitionUtils} from './model-definition-utils.js';
|
|
6
|
+
import {PropertyTransformerRegistry} from './properties/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Model data transformer.
|
|
10
|
+
*/
|
|
11
|
+
export class ModelDataTransformer extends Service {
|
|
12
|
+
/**
|
|
13
|
+
* Transform.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} modelName
|
|
16
|
+
* @param {object} modelData
|
|
17
|
+
* @param {boolean} isPartial
|
|
18
|
+
* @returns {object}
|
|
19
|
+
*/
|
|
20
|
+
transform(modelName, modelData, isPartial = false) {
|
|
21
|
+
if (!isPureObject(modelData))
|
|
22
|
+
throw new InvalidArgumentError(
|
|
23
|
+
'The data of the model %v should be an Object, but %v given.',
|
|
24
|
+
modelName,
|
|
25
|
+
modelData,
|
|
26
|
+
);
|
|
27
|
+
const propDefs =
|
|
28
|
+
this.getService(
|
|
29
|
+
ModelDefinitionUtils,
|
|
30
|
+
).getPropertiesDefinitionInBaseModelHierarchy(modelName);
|
|
31
|
+
const transformedData = cloneDeep(modelData);
|
|
32
|
+
const propNames = Object.keys(isPartial ? modelData : propDefs);
|
|
33
|
+
propNames.forEach(propName => {
|
|
34
|
+
const propDef = propDefs[propName];
|
|
35
|
+
if (!propDef) return;
|
|
36
|
+
const oldValue = modelData[propName];
|
|
37
|
+
const newValue = this._transformPropertyValue(
|
|
38
|
+
modelName,
|
|
39
|
+
propName,
|
|
40
|
+
propDef,
|
|
41
|
+
oldValue,
|
|
42
|
+
);
|
|
43
|
+
if (oldValue !== newValue) {
|
|
44
|
+
transformedData[propName] = newValue;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return transformedData;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Transform property value.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} modelName
|
|
54
|
+
* @param {string} propName
|
|
55
|
+
* @param {string|object} propDef
|
|
56
|
+
* @param {*} propValue
|
|
57
|
+
* @returns {*}
|
|
58
|
+
*/
|
|
59
|
+
_transformPropertyValue(modelName, propName, propDef, propValue) {
|
|
60
|
+
if (typeof propDef === 'string' || propDef.transform == null)
|
|
61
|
+
return propValue;
|
|
62
|
+
const transformDef = propDef.transform;
|
|
63
|
+
const transformerRegistry = this.getService(PropertyTransformerRegistry);
|
|
64
|
+
const transformFn = (
|
|
65
|
+
value,
|
|
66
|
+
transformerName,
|
|
67
|
+
transformerOptions = undefined,
|
|
68
|
+
) => {
|
|
69
|
+
const transformer = transformerRegistry.getTransformer(transformerName);
|
|
70
|
+
const context = {transformerName, modelName, propName};
|
|
71
|
+
return transformer(value, transformerOptions, context);
|
|
72
|
+
};
|
|
73
|
+
if (transformDef && typeof transformDef === 'string') {
|
|
74
|
+
return transformFn(propValue, transformDef);
|
|
75
|
+
} else if (Array.isArray(transformDef)) {
|
|
76
|
+
return transformDef.reduce(
|
|
77
|
+
(value, transformerName) => transformFn(value, transformerName),
|
|
78
|
+
propValue,
|
|
79
|
+
);
|
|
80
|
+
} else if (transformDef !== null && typeof transformDef === 'object') {
|
|
81
|
+
return Object.keys(transformDef).reduce((value, transformerName) => {
|
|
82
|
+
const transformerOptions = transformDef[transformerName];
|
|
83
|
+
return transformFn(value, transformerName, transformerOptions);
|
|
84
|
+
}, propValue);
|
|
85
|
+
} else {
|
|
86
|
+
throw new InvalidArgumentError(
|
|
87
|
+
'The provided option "transform" of the property %v in the model %v ' +
|
|
88
|
+
'should be a non-empty String, an Array of String or an Object, ' +
|
|
89
|
+
'but %v given.',
|
|
90
|
+
propName,
|
|
91
|
+
modelName,
|
|
92
|
+
transformDef,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {Schema} from '../../schema.js';
|
|
3
|
+
import {format} from '@e22m4u/js-format';
|
|
4
|
+
import {DataType} from './properties/index.js';
|
|
5
|
+
import {DefinitionRegistry} from '../definition-registry.js';
|
|
6
|
+
import {ModelDataTransformer} from './model-data-transformer.js';
|
|
7
|
+
import {PropertyTransformerRegistry} from './properties/index.js';
|
|
8
|
+
|
|
9
|
+
describe('ModelDataTransformer', function () {
|
|
10
|
+
describe('transform', function () {
|
|
11
|
+
it('throws an error if the given model name is not defined', function () {
|
|
12
|
+
const schema = new Schema();
|
|
13
|
+
const T = schema.getService(ModelDataTransformer);
|
|
14
|
+
const throwable = () => T.transform('model', {});
|
|
15
|
+
expect(throwable).to.throw('The model "model" is not defined.');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('throws an error if the given model data is not a pure object', function () {
|
|
19
|
+
const schema = new Schema();
|
|
20
|
+
const T = schema.getService(ModelDataTransformer);
|
|
21
|
+
const throwable = v => () => T.transform('model', v);
|
|
22
|
+
const error = v =>
|
|
23
|
+
format(
|
|
24
|
+
'The data of the model "model" should be an Object, but %s given.',
|
|
25
|
+
v,
|
|
26
|
+
);
|
|
27
|
+
expect(throwable('str')).to.throw(error('"str"'));
|
|
28
|
+
expect(throwable('')).to.throw(error('""'));
|
|
29
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
30
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
31
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
32
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
33
|
+
expect(throwable(undefined)).to.throw(error('undefined'));
|
|
34
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
35
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
36
|
+
expect(throwable(new Date())).to.throw(error('Date'));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('does nothing with the given model if no transformers are set', function () {
|
|
40
|
+
const schema = new Schema();
|
|
41
|
+
schema.defineModel({
|
|
42
|
+
name: 'model',
|
|
43
|
+
properties: {
|
|
44
|
+
foo: DataType.STRING,
|
|
45
|
+
bar: {
|
|
46
|
+
type: DataType.STRING,
|
|
47
|
+
default: 'test',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const T = schema.getService(ModelDataTransformer);
|
|
52
|
+
const modelData = {baz: 'qux'};
|
|
53
|
+
const res = T.transform('model', modelData);
|
|
54
|
+
expect(res).to.be.eql(modelData);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('the option "transform" with a string value', function () {
|
|
58
|
+
it('transforms the property value by its transformer', function () {
|
|
59
|
+
const schema = new Schema();
|
|
60
|
+
const myTransformer = value => String(value);
|
|
61
|
+
schema
|
|
62
|
+
.getService(PropertyTransformerRegistry)
|
|
63
|
+
.addTransformer('myTransformer', myTransformer);
|
|
64
|
+
schema.defineModel({
|
|
65
|
+
name: 'model',
|
|
66
|
+
properties: {
|
|
67
|
+
foo: {
|
|
68
|
+
type: DataType.ANY,
|
|
69
|
+
transform: 'myTransformer',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const T = schema.getService(ModelDataTransformer);
|
|
74
|
+
const res = T.transform('model', {foo: 10});
|
|
75
|
+
expect(res).to.be.eql({foo: '10'});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('passes specific arguments to the transformer function', function () {
|
|
79
|
+
const schema = new Schema();
|
|
80
|
+
const myTransformer = (value, options, context) => {
|
|
81
|
+
expect(value).to.be.eq('input');
|
|
82
|
+
expect(options).to.be.undefined;
|
|
83
|
+
expect(context).to.be.eql({
|
|
84
|
+
transformerName: 'myTransformer',
|
|
85
|
+
modelName: 'model',
|
|
86
|
+
propName: 'foo',
|
|
87
|
+
});
|
|
88
|
+
return 'transformed';
|
|
89
|
+
};
|
|
90
|
+
schema
|
|
91
|
+
.getService(PropertyTransformerRegistry)
|
|
92
|
+
.addTransformer('myTransformer', myTransformer);
|
|
93
|
+
schema.defineModel({
|
|
94
|
+
name: 'model',
|
|
95
|
+
properties: {
|
|
96
|
+
foo: {
|
|
97
|
+
type: DataType.ANY,
|
|
98
|
+
transform: 'myTransformer',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
const T = schema.getService(ModelDataTransformer);
|
|
103
|
+
const res = T.transform('model', {foo: 'input'});
|
|
104
|
+
expect(res).to.be.eql({foo: 'transformed'});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('transforms a property value even it is not provided', function () {
|
|
108
|
+
const schema = new Schema();
|
|
109
|
+
const myTransformer = () => 'transformed';
|
|
110
|
+
schema
|
|
111
|
+
.getService(PropertyTransformerRegistry)
|
|
112
|
+
.addTransformer('myTransformer', myTransformer);
|
|
113
|
+
schema.defineModel({
|
|
114
|
+
name: 'model',
|
|
115
|
+
properties: {
|
|
116
|
+
foo: {
|
|
117
|
+
type: DataType.STRING,
|
|
118
|
+
transform: 'myTransformer',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const T = schema.getService(ModelDataTransformer);
|
|
123
|
+
const res = T.transform('model', {});
|
|
124
|
+
expect(res).to.be.eql({foo: 'transformed'});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('transforms undefined and null values', function () {
|
|
128
|
+
const schema = new Schema();
|
|
129
|
+
const myTransformer = () => 'transformed';
|
|
130
|
+
schema
|
|
131
|
+
.getService(PropertyTransformerRegistry)
|
|
132
|
+
.addTransformer('myTransformer', myTransformer);
|
|
133
|
+
schema.defineModel({
|
|
134
|
+
name: 'model',
|
|
135
|
+
properties: {
|
|
136
|
+
foo: {
|
|
137
|
+
type: DataType.STRING,
|
|
138
|
+
transform: 'myTransformer',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
const T = schema.getService(ModelDataTransformer);
|
|
143
|
+
const res1 = T.transform('model', {foo: undefined});
|
|
144
|
+
const res2 = T.transform('model', {foo: null});
|
|
145
|
+
expect(res1).to.be.eql({foo: 'transformed'});
|
|
146
|
+
expect(res2).to.be.eql({foo: 'transformed'});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('the parameter "isPartial" prevents to transform values of not provided properties', function () {
|
|
150
|
+
const schema = new Schema();
|
|
151
|
+
const myTransformer = () => 'transformed';
|
|
152
|
+
schema
|
|
153
|
+
.getService(PropertyTransformerRegistry)
|
|
154
|
+
.addTransformer('myTransformer', myTransformer);
|
|
155
|
+
schema.defineModel({
|
|
156
|
+
name: 'model',
|
|
157
|
+
properties: {
|
|
158
|
+
foo: {
|
|
159
|
+
type: DataType.STRING,
|
|
160
|
+
transform: 'myTransformer',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const T = schema.getService(ModelDataTransformer);
|
|
165
|
+
const res1 = T.transform('model', {});
|
|
166
|
+
const res2 = T.transform('model', {}, true);
|
|
167
|
+
expect(res1).to.be.eql({foo: 'transformed'});
|
|
168
|
+
expect(res2).to.be.eql({});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('the option "transform" with an array value', function () {
|
|
173
|
+
it('transforms given properties by their transformers', function () {
|
|
174
|
+
const schema = new Schema();
|
|
175
|
+
const myTransformer = value => String(value);
|
|
176
|
+
schema
|
|
177
|
+
.getService(PropertyTransformerRegistry)
|
|
178
|
+
.addTransformer('myTransformer', myTransformer);
|
|
179
|
+
schema.defineModel({
|
|
180
|
+
name: 'model',
|
|
181
|
+
properties: {
|
|
182
|
+
foo: {
|
|
183
|
+
type: DataType.ANY,
|
|
184
|
+
transform: ['myTransformer'],
|
|
185
|
+
},
|
|
186
|
+
bar: {
|
|
187
|
+
type: DataType.ANY,
|
|
188
|
+
transform: ['myTransformer'],
|
|
189
|
+
},
|
|
190
|
+
baz: {
|
|
191
|
+
type: DataType.ANY,
|
|
192
|
+
transform: ['myTransformer'],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
const T = schema.getService(ModelDataTransformer);
|
|
197
|
+
const res = T.transform('model', {foo: 1, bar: 2, baz: 3});
|
|
198
|
+
expect(res).to.be.eql({foo: '1', bar: '2', baz: '3'});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('transforms the property value by its transformers in specified order', function () {
|
|
202
|
+
const schema = new Schema();
|
|
203
|
+
const order = [];
|
|
204
|
+
const myTransformer1 = value => {
|
|
205
|
+
order.push('myTransformer1');
|
|
206
|
+
return value + '1';
|
|
207
|
+
};
|
|
208
|
+
const myTransformer2 = value => {
|
|
209
|
+
order.push('myTransformer2');
|
|
210
|
+
return value + '2';
|
|
211
|
+
};
|
|
212
|
+
schema
|
|
213
|
+
.getService(PropertyTransformerRegistry)
|
|
214
|
+
.addTransformer('myTransformer1', myTransformer1)
|
|
215
|
+
.addTransformer('myTransformer2', myTransformer2);
|
|
216
|
+
schema.defineModel({
|
|
217
|
+
name: 'model',
|
|
218
|
+
properties: {
|
|
219
|
+
foo: {
|
|
220
|
+
type: DataType.ANY,
|
|
221
|
+
transform: ['myTransformer1', 'myTransformer2'],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
const T = schema.getService(ModelDataTransformer);
|
|
226
|
+
const res = T.transform('model', {foo: 'value'});
|
|
227
|
+
expect(res).to.be.eql({foo: 'value12'});
|
|
228
|
+
expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('passes specific arguments to the transformer function', function () {
|
|
232
|
+
const schema = new Schema();
|
|
233
|
+
const myTransformer = (value, options, context) => {
|
|
234
|
+
expect(value).to.be.eq('input');
|
|
235
|
+
expect(options).to.be.undefined;
|
|
236
|
+
expect(context).to.be.eql({
|
|
237
|
+
transformerName: 'myTransformer',
|
|
238
|
+
modelName: 'model',
|
|
239
|
+
propName: 'foo',
|
|
240
|
+
});
|
|
241
|
+
return 'transformed';
|
|
242
|
+
};
|
|
243
|
+
schema
|
|
244
|
+
.getService(PropertyTransformerRegistry)
|
|
245
|
+
.addTransformer('myTransformer', myTransformer);
|
|
246
|
+
schema.defineModel({
|
|
247
|
+
name: 'model',
|
|
248
|
+
properties: {
|
|
249
|
+
foo: {
|
|
250
|
+
type: DataType.ANY,
|
|
251
|
+
transform: ['myTransformer'],
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
const T = schema.getService(ModelDataTransformer);
|
|
256
|
+
const res = T.transform('model', {foo: 'input'});
|
|
257
|
+
expect(res).to.be.eql({foo: 'transformed'});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('transforms a property value even it is not provided', function () {
|
|
261
|
+
const schema = new Schema();
|
|
262
|
+
const myTransformer = () => 'transformed';
|
|
263
|
+
schema
|
|
264
|
+
.getService(PropertyTransformerRegistry)
|
|
265
|
+
.addTransformer('myTransformer', myTransformer);
|
|
266
|
+
schema.defineModel({
|
|
267
|
+
name: 'model',
|
|
268
|
+
properties: {
|
|
269
|
+
foo: {
|
|
270
|
+
type: DataType.STRING,
|
|
271
|
+
transform: ['myTransformer'],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
const T = schema.getService(ModelDataTransformer);
|
|
276
|
+
const res = T.transform('model', {});
|
|
277
|
+
expect(res).to.be.eql({foo: 'transformed'});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('transforms undefined and null values', function () {
|
|
281
|
+
const schema = new Schema();
|
|
282
|
+
const myTransformer = () => 'transformed';
|
|
283
|
+
schema
|
|
284
|
+
.getService(PropertyTransformerRegistry)
|
|
285
|
+
.addTransformer('myTransformer', myTransformer);
|
|
286
|
+
schema.defineModel({
|
|
287
|
+
name: 'model',
|
|
288
|
+
properties: {
|
|
289
|
+
foo: {
|
|
290
|
+
type: DataType.STRING,
|
|
291
|
+
transform: ['myTransformer'],
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
const T = schema.getService(ModelDataTransformer);
|
|
296
|
+
const res1 = T.transform('model', {foo: undefined});
|
|
297
|
+
const res2 = T.transform('model', {foo: null});
|
|
298
|
+
expect(res1).to.be.eql({foo: 'transformed'});
|
|
299
|
+
expect(res2).to.be.eql({foo: 'transformed'});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('the parameter "isPartial" prevents to transform values of not provided properties', function () {
|
|
303
|
+
const schema = new Schema();
|
|
304
|
+
const myTransformer = () => 'transformed';
|
|
305
|
+
schema
|
|
306
|
+
.getService(PropertyTransformerRegistry)
|
|
307
|
+
.addTransformer('myTransformer', myTransformer);
|
|
308
|
+
schema.defineModel({
|
|
309
|
+
name: 'model',
|
|
310
|
+
properties: {
|
|
311
|
+
foo: {
|
|
312
|
+
type: DataType.STRING,
|
|
313
|
+
transform: ['myTransformer'],
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
const T = schema.getService(ModelDataTransformer);
|
|
318
|
+
const res1 = T.transform('model', {});
|
|
319
|
+
const res2 = T.transform('model', {}, true);
|
|
320
|
+
expect(res1).to.be.eql({foo: 'transformed'});
|
|
321
|
+
expect(res2).to.be.eql({});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('the option "transform" with an object value', function () {
|
|
326
|
+
it('transforms given properties by their transformers', function () {
|
|
327
|
+
const schema = new Schema();
|
|
328
|
+
const myTransformer = value => String(value);
|
|
329
|
+
schema
|
|
330
|
+
.getService(PropertyTransformerRegistry)
|
|
331
|
+
.addTransformer('myTransformer', myTransformer);
|
|
332
|
+
schema.defineModel({
|
|
333
|
+
name: 'model',
|
|
334
|
+
properties: {
|
|
335
|
+
foo: {
|
|
336
|
+
type: DataType.ANY,
|
|
337
|
+
transform: {myTransformer: true},
|
|
338
|
+
},
|
|
339
|
+
bar: {
|
|
340
|
+
type: DataType.ANY,
|
|
341
|
+
transform: {myTransformer: true},
|
|
342
|
+
},
|
|
343
|
+
baz: {
|
|
344
|
+
type: DataType.ANY,
|
|
345
|
+
transform: {myTransformer: true},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
const T = schema.getService(ModelDataTransformer);
|
|
350
|
+
const res = T.transform('model', {foo: 1, bar: 2, baz: 3});
|
|
351
|
+
expect(res).to.be.eql({foo: '1', bar: '2', baz: '3'});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('transforms the property value by its transformers in specified order', function () {
|
|
355
|
+
const schema = new Schema();
|
|
356
|
+
const order = [];
|
|
357
|
+
const myTransformer1 = value => {
|
|
358
|
+
order.push('myTransformer1');
|
|
359
|
+
return value + '1';
|
|
360
|
+
};
|
|
361
|
+
const myTransformer2 = value => {
|
|
362
|
+
order.push('myTransformer2');
|
|
363
|
+
return value + '2';
|
|
364
|
+
};
|
|
365
|
+
schema
|
|
366
|
+
.getService(PropertyTransformerRegistry)
|
|
367
|
+
.addTransformer('myTransformer1', myTransformer1)
|
|
368
|
+
.addTransformer('myTransformer2', myTransformer2);
|
|
369
|
+
schema.defineModel({
|
|
370
|
+
name: 'model',
|
|
371
|
+
properties: {
|
|
372
|
+
foo: {
|
|
373
|
+
type: DataType.ANY,
|
|
374
|
+
transform: {
|
|
375
|
+
myTransformer1: true,
|
|
376
|
+
myTransformer2: true,
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
const T = schema.getService(ModelDataTransformer);
|
|
382
|
+
const res = T.transform('model', {foo: 'value'});
|
|
383
|
+
expect(res).to.be.eql({foo: 'value12'});
|
|
384
|
+
expect(order).to.be.eql(['myTransformer1', 'myTransformer2']);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('passes specific arguments to the transformer function', function () {
|
|
388
|
+
const schema = new Schema();
|
|
389
|
+
const myTransformer = (value, options, context) => {
|
|
390
|
+
expect(value).to.be.eq('input');
|
|
391
|
+
expect(options).to.be.eql({
|
|
392
|
+
option1: 'value1',
|
|
393
|
+
option2: 'value2',
|
|
394
|
+
});
|
|
395
|
+
expect(context).to.be.eql({
|
|
396
|
+
transformerName: 'myTransformer',
|
|
397
|
+
modelName: 'model',
|
|
398
|
+
propName: 'foo',
|
|
399
|
+
});
|
|
400
|
+
return 'transformed';
|
|
401
|
+
};
|
|
402
|
+
schema
|
|
403
|
+
.getService(PropertyTransformerRegistry)
|
|
404
|
+
.addTransformer('myTransformer', myTransformer);
|
|
405
|
+
schema.defineModel({
|
|
406
|
+
name: 'model',
|
|
407
|
+
properties: {
|
|
408
|
+
foo: {
|
|
409
|
+
type: DataType.ANY,
|
|
410
|
+
transform: {
|
|
411
|
+
myTransformer: {
|
|
412
|
+
option1: 'value1',
|
|
413
|
+
option2: 'value2',
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
const T = schema.getService(ModelDataTransformer);
|
|
420
|
+
const res = T.transform('model', {foo: 'input'});
|
|
421
|
+
expect(res).to.be.eql({foo: 'transformed'});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('transforms a property value even it is not provided', function () {
|
|
425
|
+
const schema = new Schema();
|
|
426
|
+
const myTransformer = () => 'transformed';
|
|
427
|
+
schema
|
|
428
|
+
.getService(PropertyTransformerRegistry)
|
|
429
|
+
.addTransformer('myTransformer', myTransformer);
|
|
430
|
+
schema.defineModel({
|
|
431
|
+
name: 'model',
|
|
432
|
+
properties: {
|
|
433
|
+
foo: {
|
|
434
|
+
type: DataType.STRING,
|
|
435
|
+
transform: {
|
|
436
|
+
myTransformer: true,
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
const T = schema.getService(ModelDataTransformer);
|
|
442
|
+
const res = T.transform('model', {});
|
|
443
|
+
expect(res).to.be.eql({foo: 'transformed'});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('transforms undefined and null values', function () {
|
|
447
|
+
const schema = new Schema();
|
|
448
|
+
const myTransformer = () => 'transformed';
|
|
449
|
+
schema
|
|
450
|
+
.getService(PropertyTransformerRegistry)
|
|
451
|
+
.addTransformer('myTransformer', myTransformer);
|
|
452
|
+
schema.defineModel({
|
|
453
|
+
name: 'model',
|
|
454
|
+
properties: {
|
|
455
|
+
foo: {
|
|
456
|
+
type: DataType.STRING,
|
|
457
|
+
transform: {
|
|
458
|
+
myTransformer: true,
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
const T = schema.getService(ModelDataTransformer);
|
|
464
|
+
const res1 = T.transform('model', {foo: undefined});
|
|
465
|
+
const res2 = T.transform('model', {foo: null});
|
|
466
|
+
expect(res1).to.be.eql({foo: 'transformed'});
|
|
467
|
+
expect(res2).to.be.eql({foo: 'transformed'});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('the parameter "isPartial" prevents to transform values of not provided properties', function () {
|
|
471
|
+
const schema = new Schema();
|
|
472
|
+
const myTransformer = () => 'transformed';
|
|
473
|
+
schema
|
|
474
|
+
.getService(PropertyTransformerRegistry)
|
|
475
|
+
.addTransformer('myTransformer', myTransformer);
|
|
476
|
+
schema.defineModel({
|
|
477
|
+
name: 'model',
|
|
478
|
+
properties: {
|
|
479
|
+
foo: {
|
|
480
|
+
type: DataType.STRING,
|
|
481
|
+
transform: {
|
|
482
|
+
myTransformer: true,
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
const T = schema.getService(ModelDataTransformer);
|
|
488
|
+
const res1 = T.transform('model', {});
|
|
489
|
+
const res2 = T.transform('model', {}, true);
|
|
490
|
+
expect(res1).to.be.eql({foo: 'transformed'});
|
|
491
|
+
expect(res2).to.be.eql({});
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('the option "transform" requires a non-empty String, an Array or an Object', function () {
|
|
496
|
+
const schema = new Schema();
|
|
497
|
+
schema
|
|
498
|
+
.getService(PropertyTransformerRegistry)
|
|
499
|
+
.addTransformer('myTransformer', () => 'transformed');
|
|
500
|
+
schema.defineModel({
|
|
501
|
+
name: 'model',
|
|
502
|
+
properties: {
|
|
503
|
+
foo: {
|
|
504
|
+
type: DataType.STRING,
|
|
505
|
+
transform: undefined,
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
const T = schema.getService(ModelDataTransformer);
|
|
510
|
+
const throwable = v => () => {
|
|
511
|
+
const models = schema.getService(DefinitionRegistry)['_models'];
|
|
512
|
+
models.model.properties.foo.transform = v;
|
|
513
|
+
T.transform('model', {});
|
|
514
|
+
};
|
|
515
|
+
const error = v =>
|
|
516
|
+
format(
|
|
517
|
+
'The provided option "transform" of the property "foo" in the model "model" ' +
|
|
518
|
+
'should be a non-empty String, an Array of String or an Object, ' +
|
|
519
|
+
'but %s given.',
|
|
520
|
+
v,
|
|
521
|
+
);
|
|
522
|
+
expect(throwable('')).to.throw(error('""'));
|
|
523
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
524
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
525
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
526
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
527
|
+
throwable('myTransformer')();
|
|
528
|
+
throwable(['myTransformer'])();
|
|
529
|
+
throwable([])();
|
|
530
|
+
throwable({myTransformer: true})();
|
|
531
|
+
throwable({})();
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
});
|
|
@@ -52,6 +52,13 @@ export class ModelDataValidator extends Service {
|
|
|
52
52
|
* @returns {undefined}
|
|
53
53
|
*/
|
|
54
54
|
_validatePropertyValue(modelName, propName, propDef, propValue) {
|
|
55
|
+
// property validators
|
|
56
|
+
this._validateValueByPropertyValidators(
|
|
57
|
+
modelName,
|
|
58
|
+
propName,
|
|
59
|
+
propDef,
|
|
60
|
+
propValue,
|
|
61
|
+
);
|
|
55
62
|
// undefined and null
|
|
56
63
|
if (propValue == null) {
|
|
57
64
|
const isRequired =
|
|
@@ -64,15 +71,8 @@ export class ModelDataValidator extends Service {
|
|
|
64
71
|
propValue,
|
|
65
72
|
);
|
|
66
73
|
}
|
|
67
|
-
//
|
|
74
|
+
// property type
|
|
68
75
|
this._validateValueByPropertyType(modelName, propName, propDef, propValue);
|
|
69
|
-
// Property validators.
|
|
70
|
-
this._validateValueByPropertyValidators(
|
|
71
|
-
modelName,
|
|
72
|
-
propName,
|
|
73
|
-
propDef,
|
|
74
|
-
propValue,
|
|
75
|
-
);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -164,9 +164,7 @@ export class ModelDataValidator extends Service {
|
|
|
164
164
|
_validateValueByPropertyValidators(modelName, propName, propDef, propValue) {
|
|
165
165
|
if (typeof propDef === 'string' || propDef.validate == null) return;
|
|
166
166
|
const validateDef = propDef.validate;
|
|
167
|
-
const
|
|
168
|
-
PropertyValidatorRegistry,
|
|
169
|
-
);
|
|
167
|
+
const validatorRegistry = this.getService(PropertyValidatorRegistry);
|
|
170
168
|
const createError = validatorName =>
|
|
171
169
|
new InvalidArgumentError(
|
|
172
170
|
'The property %v of the model %v has an invalid value %v ' +
|
|
@@ -177,7 +175,7 @@ export class ModelDataValidator extends Service {
|
|
|
177
175
|
validatorName,
|
|
178
176
|
);
|
|
179
177
|
const validateBy = (validatorName, validatorOptions = undefined) => {
|
|
180
|
-
const validator =
|
|
178
|
+
const validator = validatorRegistry.getValidator(validatorName);
|
|
181
179
|
const context = {validatorName, modelName, propName};
|
|
182
180
|
const valid = validator(propValue, validatorOptions, context);
|
|
183
181
|
if (valid instanceof Promise) {
|