@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.
Files changed (137) hide show
  1. package/README.md +31 -0
  2. package/docs/assets/navigation.js +1 -1
  3. package/docs/assets/search.js +1 -1
  4. package/docs/classes/Adapter.html +1 -1
  5. package/docs/classes/AdapterLoader.html +1 -1
  6. package/docs/classes/AdapterRegistry.html +1 -1
  7. package/docs/classes/BelongsToResolver.html +1 -1
  8. package/docs/classes/DatasourceDefinitionValidator.html +1 -1
  9. package/docs/classes/DefinitionRegistry.html +1 -1
  10. package/docs/classes/FieldsClauseTool.html +1 -1
  11. package/docs/classes/HasManyResolver.html +1 -1
  12. package/docs/classes/HasOneResolver.html +1 -1
  13. package/docs/classes/IncludeClauseTool.html +1 -1
  14. package/docs/classes/InvalidArgumentError.html +1 -1
  15. package/docs/classes/InvalidOperatorValueError.html +1 -1
  16. package/docs/classes/ModelDataSanitizer.html +1 -1
  17. package/docs/classes/ModelDataTransformer.html +16 -0
  18. package/docs/classes/ModelDataValidator.html +1 -1
  19. package/docs/classes/ModelDefinitionUtils.html +1 -1
  20. package/docs/classes/ModelDefinitionValidator.html +1 -1
  21. package/docs/classes/NotImplementedError.html +1 -1
  22. package/docs/classes/OperatorClauseTool.html +1 -1
  23. package/docs/classes/OrderClauseTool.html +1 -1
  24. package/docs/classes/PrimaryKeysDefinitionValidator.html +1 -1
  25. package/docs/classes/PropertiesDefinitionValidator.html +1 -1
  26. package/docs/classes/PropertyTransformerRegistry.html +20 -0
  27. package/docs/classes/PropertyValidatorRegistry.html +1 -1
  28. package/docs/classes/ReferencesManyResolver.html +1 -1
  29. package/docs/classes/RelationsDefinitionValidator.html +1 -1
  30. package/docs/classes/Repository.html +1 -1
  31. package/docs/classes/RepositoryRegistry.html +1 -1
  32. package/docs/classes/Schema.html +1 -1
  33. package/docs/classes/SliceClauseTool.html +1 -1
  34. package/docs/classes/WhereClauseTool.html +1 -1
  35. package/docs/enums/DataType.html +1 -1
  36. package/docs/enums/DecoratorTargetType.html +1 -1
  37. package/docs/enums/RelationType.html +1 -1
  38. package/docs/functions/capitalize.html +1 -1
  39. package/docs/functions/cloneDeep.html +1 -1
  40. package/docs/functions/excludeObjectKeys.html +1 -1
  41. package/docs/functions/getCtorName.html +1 -1
  42. package/docs/functions/getDecoratorTargetType.html +1 -1
  43. package/docs/functions/getValueByPath.html +1 -1
  44. package/docs/functions/isCtor.html +1 -1
  45. package/docs/functions/isPureObject.html +1 -1
  46. package/docs/functions/selectObjectKeys.html +1 -1
  47. package/docs/functions/singularize.html +1 -1
  48. package/docs/functions/stringToRegexp.html +1 -1
  49. package/docs/index.html +16 -1
  50. package/docs/interfaces/AndClause.html +1 -1
  51. package/docs/interfaces/Constructor.html +1 -1
  52. package/docs/interfaces/OrClause.html +1 -1
  53. package/docs/modules.html +6 -1
  54. package/docs/types/AnyObject.html +1 -1
  55. package/docs/types/BelongsToDefinition.html +1 -1
  56. package/docs/types/DEFAULT_PRIMARY_KEY_PROPERTY_NAME.html +1 -1
  57. package/docs/types/DatasourceDefinition.html +1 -1
  58. package/docs/types/FieldsClause.html +1 -1
  59. package/docs/types/FilterClause.html +1 -1
  60. package/docs/types/Flatten.html +1 -1
  61. package/docs/types/FullPropertyDefinition.html +1 -1
  62. package/docs/types/HasManyDefinition.html +1 -1
  63. package/docs/types/HasOneDefinition.html +1 -1
  64. package/docs/types/Identity.html +1 -1
  65. package/docs/types/IncludeClause.html +1 -1
  66. package/docs/types/ItemFilterClause.html +1 -1
  67. package/docs/types/ModelData.html +1 -1
  68. package/docs/types/ModelDefinition.html +1 -1
  69. package/docs/types/ModelId.html +1 -1
  70. package/docs/types/NestedIncludeClause.html +1 -1
  71. package/docs/types/NormalizedFieldsClause.html +1 -1
  72. package/docs/types/NormalizedIncludeClause.html +1 -1
  73. package/docs/types/OperatorClause.html +1 -1
  74. package/docs/types/OptionalUnlessRequiredId.html +1 -1
  75. package/docs/types/OrderClause.html +1 -1
  76. package/docs/types/PartialBy.html +1 -1
  77. package/docs/types/PartialWithoutId.html +1 -1
  78. package/docs/types/PolyBelongsToDefinition.html +1 -1
  79. package/docs/types/PolyHasManyDefinitionWithTargetKeys.html +1 -1
  80. package/docs/types/PolyHasManyDefinitionWithTargetRelationName.html +1 -1
  81. package/docs/types/PolyHasOneDefinitionWithTargetKeys.html +1 -1
  82. package/docs/types/PolyHasOneDefinitionWithTargetRelationName.html +1 -1
  83. package/docs/types/PropertiesClause.html +1 -1
  84. package/docs/types/PropertyDefinition.html +1 -1
  85. package/docs/types/PropertyDefinitionMap.html +1 -1
  86. package/docs/types/PropertyTransformOptions.html +2 -0
  87. package/docs/types/PropertyTransformer.html +2 -0
  88. package/docs/types/PropertyTransformerContext.html +2 -0
  89. package/docs/types/PropertyValidateOptions.html +1 -1
  90. package/docs/types/PropertyValidator.html +1 -1
  91. package/docs/types/PropertyValidatorContext.html +1 -1
  92. package/docs/types/ReferencesManyDefinition.html +1 -1
  93. package/docs/types/RelationDefinition.html +1 -1
  94. package/docs/types/RelationDefinitionMap.html +1 -1
  95. package/docs/types/WhereClause.html +1 -1
  96. package/docs/types/WithoutId.html +1 -1
  97. package/package.json +3 -3
  98. package/src/adapter/adapter.js +2 -0
  99. package/src/adapter/adapter.spec.js +9 -3
  100. package/src/adapter/decorator/data-transformation-decorator.d.ts +14 -0
  101. package/src/adapter/decorator/data-transformation-decorator.js +54 -0
  102. package/src/adapter/decorator/data-transformation-decorator.spec.js +95 -0
  103. package/src/adapter/decorator/data-validation-decorator.js +6 -5
  104. package/src/adapter/decorator/data-validation-decorator.spec.js +49 -21
  105. package/src/adapter/decorator/default-values-decorator.spec.js +89 -113
  106. package/src/adapter/decorator/index.d.ts +1 -0
  107. package/src/adapter/decorator/index.js +1 -0
  108. package/src/definition/model/index.d.ts +1 -0
  109. package/src/definition/model/index.js +1 -0
  110. package/src/definition/model/model-data-transformer.d.ts +15 -0
  111. package/src/definition/model/model-data-transformer.js +96 -0
  112. package/src/definition/model/model-data-transformer.spec.js +534 -0
  113. package/src/definition/model/model-data-validator.js +10 -12
  114. package/src/definition/model/model-data-validator.spec.js +133 -32
  115. package/src/definition/model/properties/index.d.ts +1 -0
  116. package/src/definition/model/properties/index.js +1 -0
  117. package/src/definition/model/properties/properties-definition-validator.js +47 -0
  118. package/src/definition/model/properties/properties-definition-validator.spec.js +52 -0
  119. package/src/definition/model/properties/property-transformer/builtin/index.d.ts +1 -0
  120. package/src/definition/model/properties/property-transformer/builtin/index.js +1 -0
  121. package/src/definition/model/properties/property-transformer/builtin/trim-transformer.d.ts +6 -0
  122. package/src/definition/model/properties/property-transformer/builtin/trim-transformer.js +19 -0
  123. package/src/definition/model/properties/property-transformer/builtin/trim-transformer.spec.js +39 -0
  124. package/src/definition/model/properties/property-transformer/index.d.ts +2 -0
  125. package/src/definition/model/properties/property-transformer/index.js +2 -0
  126. package/src/definition/model/properties/property-transformer/property-transformer-registry.d.ts +29 -0
  127. package/src/definition/model/properties/property-transformer/property-transformer-registry.js +72 -0
  128. package/src/definition/model/properties/property-transformer/property-transformer-registry.spec.js +129 -0
  129. package/src/definition/model/properties/property-transformer/property-transformer.d.ts +25 -0
  130. package/src/definition/model/properties/property-transformer/property-transformer.js +1 -0
  131. package/src/definition/model/properties/property-validator/builtin/max-length-validator.js +1 -1
  132. package/src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js +10 -3
  133. package/src/definition/model/properties/property-validator/builtin/min-length-validator.js +1 -1
  134. package/src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js +10 -3
  135. package/src/definition/model/properties/property-validator/builtin/regexp-validator.js +1 -1
  136. package/src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js +10 -3
  137. 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
- // Property type.
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 propertyValidatorRegistry = this.getService(
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 = propertyValidatorRegistry.getValidator(validatorName);
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) {