@cleverbrush/schema 0.0.17 → 1.0.0-beta.0
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 +213 -163
- package/dist/builders/AliasSchemaBuilder.d.ts +14 -0
- package/dist/builders/AliasSchemaBuilder.js +91 -0
- package/dist/builders/ArraySchemaBuilder.d.ts +15 -0
- package/dist/builders/ArraySchemaBuilder.js +141 -0
- package/dist/builders/BooleanSchemaBuilder.d.ts +31 -0
- package/dist/builders/BooleanSchemaBuilder.js +90 -0
- package/dist/builders/FunctionSchemaBuilder.d.ts +25 -0
- package/dist/builders/FunctionSchemaBuilder.js +66 -0
- package/dist/builders/NumberSchemaBuilder.d.ts +61 -0
- package/dist/builders/NumberSchemaBuilder.js +206 -0
- package/dist/builders/ObjectSchemaBuilder.d.ts +83 -0
- package/dist/builders/ObjectSchemaBuilder.js +344 -0
- package/dist/builders/SchemaBuilder.d.ts +36 -0
- package/dist/builders/SchemaBuilder.js +88 -0
- package/dist/builders/StringSchemaBuilder.d.ts +14 -0
- package/dist/builders/StringSchemaBuilder.js +140 -0
- package/dist/builders/UnionSchemaBuilder.d.ts +10 -0
- package/dist/builders/UnionSchemaBuilder.js +100 -0
- package/dist/defaultSchemas.d.ts +4 -0
- package/dist/defaultSchemas.js +45 -0
- package/dist/index.d.ts +38 -90
- package/dist/index.js +30 -4
- package/dist/schema.d.ts +242 -0
- package/dist/schema.js +2 -0
- package/dist/schemaRegistry.d.ts +54 -0
- package/dist/schemaRegistry.js +301 -0
- package/dist/validators/validateArray.d.ts +3 -2
- package/dist/validators/validateBoolean.d.ts +2 -2
- package/dist/validators/validateBoolean.js +1 -4
- package/dist/validators/validateFunction.d.ts +2 -0
- package/dist/validators/validateFunction.js +21 -0
- package/dist/validators/validateNumber.d.ts +2 -2
- package/dist/validators/validateNumber.js +1 -1
- package/dist/validators/validateObject.d.ts +3 -2
- package/dist/validators/validateObject.js +33 -11
- package/dist/validators/validateString.d.ts +2 -2
- package/dist/validators/validateString.js +1 -1
- package/dist/validators/validateUnion.d.ts +3 -0
- package/dist/validators/validateUnion.js +30 -0
- package/package.json +3 -3
- package/src/builders/AliasSchemaBuilder.test.ts +124 -0
- package/src/builders/AliasSchemaBuilder.ts +250 -0
- package/src/builders/ArraySchemaBuilder.test.ts +270 -0
- package/src/builders/ArraySchemaBuilder.ts +381 -0
- package/src/builders/BooleanSchemaBuilder.test.ts +196 -0
- package/src/builders/BooleanSchemaBuilder.ts +155 -0
- package/src/builders/FunctionSchemaBuilder.test.ts +134 -0
- package/src/builders/FunctionSchemaBuilder.ts +109 -0
- package/src/builders/NumberSchemaBuilder.test.ts +493 -0
- package/src/builders/NumberSchemaBuilder.ts +789 -0
- package/src/builders/ObjectSchemaBuilder.test.ts +657 -0
- package/src/builders/ObjectSchemaBuilder.ts +794 -0
- package/src/builders/SchemaBuilder.test.ts +73 -0
- package/src/builders/SchemaBuilder.ts +135 -0
- package/src/builders/StringSchemaBuilder.test.ts +318 -0
- package/src/builders/StringSchemaBuilder.ts +392 -0
- package/src/builders/UnionSchemaBuilder.test.ts +162 -0
- package/src/builders/UnionSchemaBuilder.ts +159 -0
- package/src/defaultSchemas.ts +44 -0
- package/src/index.ts +70 -190
- package/src/schema.ts +922 -0
- package/src/schemaRegistry.builders.test.ts +1438 -0
- package/src/{schemaValidator.test.ts → schemaRegistry.test.ts} +561 -185
- package/src/schemaRegistry.ts +532 -0
- package/src/validators/validateArray.ts +4 -7
- package/src/validators/validateBoolean.ts +2 -11
- package/src/validators/validateFunction.ts +25 -0
- package/src/validators/validateNumber.ts +2 -7
- package/src/validators/validateObject.ts +32 -21
- package/src/validators/validateString.ts +2 -7
- package/src/validators/validateUnion.ts +36 -0
- package/dist/schemaValidator.d.ts +0 -16
- package/dist/schemaValidator.js +0 -234
- package/src/schemaValidator.ts +0 -367
|
@@ -0,0 +1,1438 @@
|
|
|
1
|
+
import { InferType, Schema } from './schema.js';
|
|
2
|
+
import { object } from './builders/ObjectSchemaBuilder.js';
|
|
3
|
+
import { number } from './builders/NumberSchemaBuilder.js';
|
|
4
|
+
import { string } from './builders/StringSchemaBuilder.js';
|
|
5
|
+
import { array } from './builders/ArraySchemaBuilder.js';
|
|
6
|
+
import { union } from './builders/UnionSchemaBuilder.js';
|
|
7
|
+
import { boolean } from './builders/BooleanSchemaBuilder.js';
|
|
8
|
+
import { alias } from './builders/AliasSchemaBuilder.js';
|
|
9
|
+
|
|
10
|
+
import SchemaRegistry from './schemaRegistry.js';
|
|
11
|
+
|
|
12
|
+
const getUserSchema = () =>
|
|
13
|
+
object({
|
|
14
|
+
id: number(),
|
|
15
|
+
name: object().addProps({
|
|
16
|
+
first: string().minLength(1).maxLength(100),
|
|
17
|
+
last: string().minLength(1).maxLength(100)
|
|
18
|
+
}),
|
|
19
|
+
incomePerMonth: number().min(0),
|
|
20
|
+
bornAt: object(),
|
|
21
|
+
aliases: array()
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('Can add schema', () => {
|
|
25
|
+
const validator = new SchemaRegistry().addSchema(
|
|
26
|
+
'something',
|
|
27
|
+
({ object, string }) =>
|
|
28
|
+
object().addProps({
|
|
29
|
+
a: string()
|
|
30
|
+
})
|
|
31
|
+
);
|
|
32
|
+
expect(validator.schemas.something).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('Error thrown on adding a schema with duplicate name', () => {
|
|
36
|
+
expect(() =>
|
|
37
|
+
new SchemaRegistry()
|
|
38
|
+
.addSchema('smth', {} as any)
|
|
39
|
+
.addSchema('smth', {} as any)
|
|
40
|
+
).toThrow();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('Union - Array as schema type', async () => {
|
|
44
|
+
const validator = new SchemaRegistry()
|
|
45
|
+
.addSchema('name', ({ object, string }) =>
|
|
46
|
+
object().addProps({
|
|
47
|
+
name: string()
|
|
48
|
+
})
|
|
49
|
+
)
|
|
50
|
+
.addSchema('number_or_string', ({ number, string, alias, union }) =>
|
|
51
|
+
union(number()).or(string()).or(alias('name'))
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
let result = await validator.schemas.number_or_string.validate(123);
|
|
55
|
+
|
|
56
|
+
expect(result).toHaveProperty('valid', true);
|
|
57
|
+
|
|
58
|
+
result = await validator.schemas.number_or_string.validate('some string');
|
|
59
|
+
|
|
60
|
+
expect(result).toHaveProperty('valid', true);
|
|
61
|
+
|
|
62
|
+
result = await validator.schemas.number_or_string.validate({});
|
|
63
|
+
|
|
64
|
+
expect(result).toHaveProperty('valid', false);
|
|
65
|
+
|
|
66
|
+
result = await validator.schemas.number_or_string.validate({
|
|
67
|
+
name: 'some name'
|
|
68
|
+
});
|
|
69
|
+
expect(result).toHaveProperty('valid', true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('Union - Schema Object - 1', async () => {
|
|
73
|
+
const validator = new SchemaRegistry().addSchema(
|
|
74
|
+
'address',
|
|
75
|
+
({ object, union, number, string }) =>
|
|
76
|
+
object().addProps({
|
|
77
|
+
first: union(number().min(10).max(20)).or(string().minLength(2))
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
let result = await validator.schemas.address.validate({
|
|
82
|
+
first: 15
|
|
83
|
+
});
|
|
84
|
+
expect(result).toHaveProperty('valid', true);
|
|
85
|
+
result = await validator.schemas.address.validate({
|
|
86
|
+
first: 25
|
|
87
|
+
});
|
|
88
|
+
expect(result).toHaveProperty('valid', false);
|
|
89
|
+
result = await validator.schemas.address.validate({
|
|
90
|
+
first: 'some string'
|
|
91
|
+
});
|
|
92
|
+
expect(result).toHaveProperty('valid', true);
|
|
93
|
+
result = await validator.schemas.address.validate({
|
|
94
|
+
first: 's'
|
|
95
|
+
});
|
|
96
|
+
expect(result).toHaveProperty('valid', false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('Union - Schema Object - 2', async () => {
|
|
100
|
+
const validator = new SchemaRegistry().addSchema(
|
|
101
|
+
'address',
|
|
102
|
+
({ object, number, string, union }) =>
|
|
103
|
+
object({
|
|
104
|
+
first: union(number().min(10).max(20))
|
|
105
|
+
.or(string().minLength(2))
|
|
106
|
+
.optional()
|
|
107
|
+
.nullable()
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
let result = await validator.schemas.address.validate({});
|
|
111
|
+
expect(result).toHaveProperty('valid', true);
|
|
112
|
+
result = await validator.schemas.address.validate({
|
|
113
|
+
first: null
|
|
114
|
+
});
|
|
115
|
+
expect(result).toHaveProperty('valid', true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('Validate - number by schema', async () => {
|
|
119
|
+
const validator = new SchemaRegistry();
|
|
120
|
+
const result = await validator.validate(number(), 10);
|
|
121
|
+
expect(result).toHaveProperty('valid', true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('Validate - number by schema - not number passed', async () => {
|
|
125
|
+
const validator = new SchemaRegistry();
|
|
126
|
+
const result = await validator.validate(number(), 'str');
|
|
127
|
+
expect(result).toHaveProperty('valid', false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('Validate - number by schema - min', async () => {
|
|
131
|
+
const validator = new SchemaRegistry();
|
|
132
|
+
const result = await validator.validate(number().min(1000), 10);
|
|
133
|
+
expect(result).toHaveProperty('valid', false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('Validate - number by schema - min 2', async () => {
|
|
137
|
+
const validator = new SchemaRegistry();
|
|
138
|
+
const result = await validator.validate(number().min(1000), 10000);
|
|
139
|
+
expect(result).toEqual({
|
|
140
|
+
valid: true
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('Validate - number by schema - min 3', async () => {
|
|
145
|
+
const validator = new SchemaRegistry();
|
|
146
|
+
const result = await validator.validate(number().min(1000), 1000);
|
|
147
|
+
expect(result).toHaveProperty('valid', true);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('Validate - number by schema - min 4', async () => {
|
|
151
|
+
const validator = new SchemaRegistry();
|
|
152
|
+
const mk = jest.fn();
|
|
153
|
+
validator
|
|
154
|
+
.validate(number().min('wdd' as any), 10)
|
|
155
|
+
.catch(mk)
|
|
156
|
+
.then(() => {
|
|
157
|
+
expect(mk).toBeCalled();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('Validate - number by schema - max', async () => {
|
|
162
|
+
const validator = new SchemaRegistry();
|
|
163
|
+
const result = await validator.validate(number().max(1000), 20000);
|
|
164
|
+
expect(result).toHaveProperty('valid', false);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('Validate - number by schema - max 2', async () => {
|
|
168
|
+
const validator = new SchemaRegistry();
|
|
169
|
+
const result = await validator.validate(number().max(40000), 10000);
|
|
170
|
+
expect(result).toHaveProperty('valid', true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('Validate - number by schema - max 3', async () => {
|
|
174
|
+
const validator = new SchemaRegistry();
|
|
175
|
+
const result = await validator.validate(number().max(1000), 1000);
|
|
176
|
+
expect(result).toHaveProperty('valid', true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('Validate - number by schema - max 4', async () => {
|
|
180
|
+
const validator = new SchemaRegistry();
|
|
181
|
+
const mk = jest.fn();
|
|
182
|
+
validator
|
|
183
|
+
.validate(number().max('string' as any), 10)
|
|
184
|
+
.catch(mk)
|
|
185
|
+
.then(() => {
|
|
186
|
+
expect(mk).toBeCalled();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('Validate - number by schema - range', async () => {
|
|
191
|
+
const validator = new SchemaRegistry();
|
|
192
|
+
const result = await validator.validate(number().min(1).max(100), 10);
|
|
193
|
+
|
|
194
|
+
expect(result).toHaveProperty('valid', true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('Validate - number by schema - range - 2', async () => {
|
|
198
|
+
const validator = new SchemaRegistry();
|
|
199
|
+
const result = await validator.validate(number().min(-1).max(100), -210);
|
|
200
|
+
|
|
201
|
+
expect(result).toHaveProperty('valid', false);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('Validate - number by schema - range - 3', async () => {
|
|
205
|
+
const validator = new SchemaRegistry();
|
|
206
|
+
const result = await validator.validate(number().min(-100).max(100), -100);
|
|
207
|
+
|
|
208
|
+
expect(result).toHaveProperty('valid', true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('Validate - number by schema - range - 4', async () => {
|
|
212
|
+
const validator = new SchemaRegistry();
|
|
213
|
+
const result = await validator.validate(number().min(-100).max(100), 100);
|
|
214
|
+
|
|
215
|
+
expect(result).toHaveProperty('valid', true);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('Validate - number by schema - range - 5', async () => {
|
|
219
|
+
const validator = new SchemaRegistry();
|
|
220
|
+
const result = await validator.validate(number().min(-100).max(100), 200);
|
|
221
|
+
|
|
222
|
+
expect(result).toHaveProperty('valid', false);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('Validate - number by schema - range - 6', async () => {
|
|
226
|
+
const validator = new SchemaRegistry();
|
|
227
|
+
const result = await validator.validate(number().min(-100).max(100), 200);
|
|
228
|
+
|
|
229
|
+
expect(result).toHaveProperty('valid', false);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('Validate - number by schema - NaN', async () => {
|
|
233
|
+
const validator = new SchemaRegistry();
|
|
234
|
+
const result = await validator.validate(number().min(-100).max(100), 0 / 0);
|
|
235
|
+
expect(result).toHaveProperty('valid', false);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('Validate - number by schema - NaN - 2', async () => {
|
|
239
|
+
const validator = new SchemaRegistry();
|
|
240
|
+
const result = await validator.validate(number().canBeNaN(), 0 / 0);
|
|
241
|
+
|
|
242
|
+
expect(result).toHaveProperty('valid', true);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('Validate - number by schema - Infinity', async () => {
|
|
246
|
+
const validator = new SchemaRegistry();
|
|
247
|
+
const result = await validator.validate(number(), 100 / 0);
|
|
248
|
+
|
|
249
|
+
expect(result).toHaveProperty('valid', false);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('Validate - number by schema - Infinity - 2', async () => {
|
|
253
|
+
const validator = new SchemaRegistry();
|
|
254
|
+
const result = await validator.validate(number().canBeInfinite(), 100 / 0);
|
|
255
|
+
|
|
256
|
+
expect(result).toHaveProperty('valid', true);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('Validate - number by schema - custom validators - 1', async () => {
|
|
260
|
+
const validator = new SchemaRegistry();
|
|
261
|
+
const schema = number().addValidator(async (value) => {
|
|
262
|
+
if ((value & 0xb01) === 0)
|
|
263
|
+
return {
|
|
264
|
+
valid: false,
|
|
265
|
+
errors: ['value should be odd!']
|
|
266
|
+
};
|
|
267
|
+
return {
|
|
268
|
+
valid: true
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
let result = await validator.validate(schema, 101);
|
|
272
|
+
|
|
273
|
+
expect(result).toHaveProperty('valid', true);
|
|
274
|
+
|
|
275
|
+
result = await validator.validate(schema, 100);
|
|
276
|
+
expect(result).toHaveProperty('valid', false);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test('Validate - number by schema - custom validators - 2', async () => {
|
|
280
|
+
const validator = new SchemaRegistry();
|
|
281
|
+
const schema = number()
|
|
282
|
+
.addValidator((value) => {
|
|
283
|
+
if ((value & 0xb01) === 0)
|
|
284
|
+
return {
|
|
285
|
+
valid: false,
|
|
286
|
+
errors: ['value should be odd!']
|
|
287
|
+
};
|
|
288
|
+
return {
|
|
289
|
+
valid: true
|
|
290
|
+
};
|
|
291
|
+
})
|
|
292
|
+
.addValidator((value) => ({ valid: value > 100 }));
|
|
293
|
+
let result = await validator.validate(schema, 101);
|
|
294
|
+
|
|
295
|
+
expect(result).toHaveProperty('valid', true);
|
|
296
|
+
|
|
297
|
+
result = await validator.validate(schema, 99);
|
|
298
|
+
expect(result).toHaveProperty('valid', false);
|
|
299
|
+
|
|
300
|
+
result = await validator.validate(schema, 100);
|
|
301
|
+
expect(result).toHaveProperty('valid', false);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test('Validate - boolean - 1', async () => {
|
|
305
|
+
const validator = new SchemaRegistry();
|
|
306
|
+
const result = await validator.validate(boolean(), 300);
|
|
307
|
+
|
|
308
|
+
expect(result).toHaveProperty('valid', false);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('Validate - boolean - 2', async () => {
|
|
312
|
+
const validator = new SchemaRegistry();
|
|
313
|
+
const result = await validator.validate(boolean(), true);
|
|
314
|
+
|
|
315
|
+
expect(result).toHaveProperty('valid', true);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('Validate - boolean - 3', async () => {
|
|
319
|
+
const validator = new SchemaRegistry();
|
|
320
|
+
const result = await validator.validate(boolean(), false);
|
|
321
|
+
|
|
322
|
+
expect(result).toHaveProperty('valid', true);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test('Validate - boolean - 4', async () => {
|
|
326
|
+
const validator = new SchemaRegistry();
|
|
327
|
+
const result = await validator.validate(boolean().equals(true), false);
|
|
328
|
+
|
|
329
|
+
expect(result).toHaveProperty('valid', false);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('Validate - boolean - 5', async () => {
|
|
333
|
+
const validator = new SchemaRegistry();
|
|
334
|
+
const result = await validator.validate(boolean().equals(false), false);
|
|
335
|
+
|
|
336
|
+
expect(result).toHaveProperty('valid', true);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('Validate - string - schema object - 1', async () => {
|
|
340
|
+
const validator = new SchemaRegistry();
|
|
341
|
+
const result = await validator.validate(
|
|
342
|
+
string().equals('123456'),
|
|
343
|
+
'123456'
|
|
344
|
+
);
|
|
345
|
+
expect(result).toHaveProperty('valid', true);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test('Validate - string - schema object - 2', async () => {
|
|
349
|
+
const validator = new SchemaRegistry();
|
|
350
|
+
const result = await validator.validate(string().equals('123456'), '12345');
|
|
351
|
+
expect(result).toHaveProperty('valid', false);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test('Validate - string - schema object - 3', async () => {
|
|
355
|
+
const validator = new SchemaRegistry();
|
|
356
|
+
const result = await validator.validate(string().equals('123456'), 1234);
|
|
357
|
+
expect(result).toHaveProperty('valid', false);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test('Validate - string - length control - 1', async () => {
|
|
361
|
+
const validator = new SchemaRegistry();
|
|
362
|
+
let result = await validator.validate(
|
|
363
|
+
string().minLength(2).maxLength(3),
|
|
364
|
+
'U'
|
|
365
|
+
);
|
|
366
|
+
expect(result).toHaveProperty('valid', false);
|
|
367
|
+
|
|
368
|
+
result = await validator.validate(string().minLength(2).maxLength(3), 'US');
|
|
369
|
+
expect(result).toHaveProperty('valid', true);
|
|
370
|
+
|
|
371
|
+
result = await validator.validate(
|
|
372
|
+
string().minLength(2).maxLength(3),
|
|
373
|
+
'USA'
|
|
374
|
+
);
|
|
375
|
+
expect(result).toHaveProperty('valid', true);
|
|
376
|
+
|
|
377
|
+
result = await validator.validate(
|
|
378
|
+
string().minLength(2).maxLength(3),
|
|
379
|
+
'USA'
|
|
380
|
+
);
|
|
381
|
+
expect(result).toHaveProperty('valid', true);
|
|
382
|
+
|
|
383
|
+
result = await validator.validate(
|
|
384
|
+
string().minLength(2).maxLength(3),
|
|
385
|
+
'United States of America'
|
|
386
|
+
);
|
|
387
|
+
expect(result).toHaveProperty('valid', false);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test('Validate - array - size control - 1', async () => {
|
|
391
|
+
const validator = new SchemaRegistry();
|
|
392
|
+
let result = await validator.validate(
|
|
393
|
+
array().minLength(1).maxLength(3),
|
|
394
|
+
[]
|
|
395
|
+
);
|
|
396
|
+
expect(result).toHaveProperty('valid', false);
|
|
397
|
+
|
|
398
|
+
result = await validator.validate(array().minLength(1).maxLength(3), [1]);
|
|
399
|
+
expect(result).toHaveProperty('valid', true);
|
|
400
|
+
|
|
401
|
+
result = await validator.validate(
|
|
402
|
+
array().minLength(1).maxLength(3),
|
|
403
|
+
[1, 2]
|
|
404
|
+
);
|
|
405
|
+
expect(result).toHaveProperty('valid', true);
|
|
406
|
+
|
|
407
|
+
result = await validator.validate(
|
|
408
|
+
array().minLength(1).maxLength(3),
|
|
409
|
+
[1, 2, 3]
|
|
410
|
+
);
|
|
411
|
+
expect(result).toHaveProperty('valid', true);
|
|
412
|
+
|
|
413
|
+
result = await validator.validate(
|
|
414
|
+
array().minLength(1).maxLength(3),
|
|
415
|
+
[1, 2, 3, 4]
|
|
416
|
+
);
|
|
417
|
+
expect(result).toHaveProperty('valid', false);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test('Validate - array - ofType - 1', async () => {
|
|
421
|
+
const validator = new SchemaRegistry();
|
|
422
|
+
let result = await validator.validate(array().ofType(number()), [
|
|
423
|
+
'1',
|
|
424
|
+
2,
|
|
425
|
+
3
|
|
426
|
+
]);
|
|
427
|
+
expect(result).toHaveProperty('valid', false);
|
|
428
|
+
|
|
429
|
+
result = await validator.validate(array().ofType(number()), [1, 2, 3]);
|
|
430
|
+
expect(result).toHaveProperty('valid', true);
|
|
431
|
+
|
|
432
|
+
result = await validator.validate(
|
|
433
|
+
array().ofType(number().min(10)),
|
|
434
|
+
[-1, 12, 13]
|
|
435
|
+
);
|
|
436
|
+
expect(result).toHaveProperty('valid', false);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test('Validate - object - 1', async () => {
|
|
440
|
+
const validator = new SchemaRegistry().addSchema('user', getUserSchema());
|
|
441
|
+
const schema = getUserSchema();
|
|
442
|
+
type User = InferType<typeof schema>;
|
|
443
|
+
const user: User = {
|
|
444
|
+
id: 1,
|
|
445
|
+
name: {
|
|
446
|
+
first: 'Andrew',
|
|
447
|
+
last: 'Zolotukhin'
|
|
448
|
+
},
|
|
449
|
+
bornAt: new Date(1986, 4, 30),
|
|
450
|
+
incomePerMonth: 134234,
|
|
451
|
+
aliases: []
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
let result = await validator.validate('user', user);
|
|
455
|
+
|
|
456
|
+
expect(result).toHaveProperty('valid', true);
|
|
457
|
+
|
|
458
|
+
result = await validator.validate('user', 10);
|
|
459
|
+
|
|
460
|
+
expect(result).toHaveProperty('valid', false);
|
|
461
|
+
|
|
462
|
+
result = await validator.validate(
|
|
463
|
+
object({
|
|
464
|
+
name: string().maxLength(5),
|
|
465
|
+
address: object({
|
|
466
|
+
street: string(),
|
|
467
|
+
house: number().optional()
|
|
468
|
+
})
|
|
469
|
+
}),
|
|
470
|
+
{
|
|
471
|
+
name: 'Andr',
|
|
472
|
+
address: {
|
|
473
|
+
street: 'something'
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
expect(result).toHaveProperty('valid', true);
|
|
479
|
+
|
|
480
|
+
result = await validator.validate(
|
|
481
|
+
object({
|
|
482
|
+
a: number(),
|
|
483
|
+
b: number()
|
|
484
|
+
}).addValidator((value: { a: number; b: number }) => {
|
|
485
|
+
if (value.a + value.b === 5) {
|
|
486
|
+
return {
|
|
487
|
+
valid: true
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
valid: false,
|
|
492
|
+
error: ['some error']
|
|
493
|
+
};
|
|
494
|
+
}),
|
|
495
|
+
{
|
|
496
|
+
a: 1,
|
|
497
|
+
b: 3
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
expect(result).toHaveProperty('valid', false);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
test('Validate - one of - 1', async () => {
|
|
504
|
+
const validator = new SchemaRegistry();
|
|
505
|
+
|
|
506
|
+
let result = await validator.validate(
|
|
507
|
+
union(number()).or(object()),
|
|
508
|
+
'something'
|
|
509
|
+
);
|
|
510
|
+
expect(result).toHaveProperty('valid', false);
|
|
511
|
+
|
|
512
|
+
result = await validator.validate(
|
|
513
|
+
union(number()).or(string()),
|
|
514
|
+
'something'
|
|
515
|
+
);
|
|
516
|
+
expect(result).toHaveProperty('valid', true);
|
|
517
|
+
|
|
518
|
+
result = await validator.validate(
|
|
519
|
+
union(number())
|
|
520
|
+
.or(string())
|
|
521
|
+
.or(
|
|
522
|
+
object({
|
|
523
|
+
first: string(),
|
|
524
|
+
last: string()
|
|
525
|
+
})
|
|
526
|
+
),
|
|
527
|
+
{
|
|
528
|
+
first: 'something',
|
|
529
|
+
last: 'another'
|
|
530
|
+
}
|
|
531
|
+
);
|
|
532
|
+
expect(result).toHaveProperty('valid', true);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test('Validate schema - 1', async () => {
|
|
536
|
+
const validator = new SchemaRegistry()
|
|
537
|
+
.addSchema(
|
|
538
|
+
'EqualsFilterCondition',
|
|
539
|
+
({ object, number, string, union }) =>
|
|
540
|
+
object({
|
|
541
|
+
operation: string().equals('equals'),
|
|
542
|
+
value: union(object()).or(string()).or(number())
|
|
543
|
+
})
|
|
544
|
+
)
|
|
545
|
+
.addSchema('LikeFilterCondition', ({ object, string }) =>
|
|
546
|
+
object({
|
|
547
|
+
operation: string().equals('like'),
|
|
548
|
+
value: string()
|
|
549
|
+
})
|
|
550
|
+
)
|
|
551
|
+
.addSchema('StringFilterCondition', ({ union, alias }) =>
|
|
552
|
+
union(alias('EqualsFilterCondition')).or(
|
|
553
|
+
alias('LikeFilterCondition')
|
|
554
|
+
)
|
|
555
|
+
)
|
|
556
|
+
.addSchema('AuthorFilter', ({ alias, object }) =>
|
|
557
|
+
object({
|
|
558
|
+
fullName: alias('StringFilterCondition')
|
|
559
|
+
})
|
|
560
|
+
)
|
|
561
|
+
.addSchema('Date', ({ object }) =>
|
|
562
|
+
object().addValidator((value) =>
|
|
563
|
+
value instanceof Date && !Number.isNaN(value)
|
|
564
|
+
? {
|
|
565
|
+
valid: true
|
|
566
|
+
}
|
|
567
|
+
: {
|
|
568
|
+
valid: false,
|
|
569
|
+
errors: ['should be a valid Date object']
|
|
570
|
+
}
|
|
571
|
+
)
|
|
572
|
+
)
|
|
573
|
+
.addSchema(
|
|
574
|
+
'AuthorsReportSpecification',
|
|
575
|
+
({ object, string, alias, union }) =>
|
|
576
|
+
object({
|
|
577
|
+
type: string().equals('author'),
|
|
578
|
+
start: alias('Date'),
|
|
579
|
+
end: alias('Date'),
|
|
580
|
+
selectionStart: alias('Date'),
|
|
581
|
+
selectionEnd: alias('Date'),
|
|
582
|
+
granularity: union(string().equals('day'))
|
|
583
|
+
.or(string().equals('week'))
|
|
584
|
+
.or(string().equals('month')),
|
|
585
|
+
metrics: array().ofType(
|
|
586
|
+
union(string().equals('articlesPublished'))
|
|
587
|
+
.or(string().equals('searchReferrers'))
|
|
588
|
+
.or(string().equals('socialReferrers'))
|
|
589
|
+
.or(string().equals('views'))
|
|
590
|
+
.or(string().equals('visitors'))
|
|
591
|
+
.or(string().equals('newVisitors'))
|
|
592
|
+
),
|
|
593
|
+
filters: object({
|
|
594
|
+
author: alias('AuthorFilter')
|
|
595
|
+
})
|
|
596
|
+
}).addValidator((value) =>
|
|
597
|
+
value.start <= value.end &&
|
|
598
|
+
value.selectionStart <= value.selectionEnd &&
|
|
599
|
+
value.selectionStart >= value.start &&
|
|
600
|
+
value.selectionStart <= value.end &&
|
|
601
|
+
value.selectionEnd >= value.start &&
|
|
602
|
+
value.selectionEnd <= value.end
|
|
603
|
+
? {
|
|
604
|
+
valid: true
|
|
605
|
+
}
|
|
606
|
+
: {
|
|
607
|
+
valid: false,
|
|
608
|
+
errors: [
|
|
609
|
+
'selectionStart <=> selectionEnd should be inside the start <=> end interval'
|
|
610
|
+
]
|
|
611
|
+
}
|
|
612
|
+
)
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
let reportSpec = {
|
|
616
|
+
type: 'author',
|
|
617
|
+
start: new Date(2021, 0, 1),
|
|
618
|
+
end: new Date(),
|
|
619
|
+
selectionStart: new Date(2022, 0, 1),
|
|
620
|
+
selectionEnd: new Date(2022, 2, 1),
|
|
621
|
+
granularity: 'day',
|
|
622
|
+
metrics: [
|
|
623
|
+
'articlesPublished',
|
|
624
|
+
'searchReferrers',
|
|
625
|
+
'socialReferrers',
|
|
626
|
+
'views',
|
|
627
|
+
'visitors',
|
|
628
|
+
'newVisitors'
|
|
629
|
+
],
|
|
630
|
+
filters: {
|
|
631
|
+
author: {
|
|
632
|
+
fullName: {
|
|
633
|
+
operation: 'between',
|
|
634
|
+
value: 'some name'
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
let result = await validator.schemas.AuthorsReportSpecification.validate(
|
|
641
|
+
reportSpec
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
expect(result).toHaveProperty('valid', false);
|
|
645
|
+
|
|
646
|
+
reportSpec = {
|
|
647
|
+
type: 'author',
|
|
648
|
+
start: new Date(2021, 0, 1),
|
|
649
|
+
end: new Date(),
|
|
650
|
+
selectionStart: new Date(2022, 0, 1),
|
|
651
|
+
selectionEnd: new Date(2022, 2, 1),
|
|
652
|
+
granularity: 'day',
|
|
653
|
+
metrics: [
|
|
654
|
+
'articlesPublished',
|
|
655
|
+
'searchReferrers',
|
|
656
|
+
'socialReferrers',
|
|
657
|
+
'views',
|
|
658
|
+
'visitors',
|
|
659
|
+
'newVisitors'
|
|
660
|
+
],
|
|
661
|
+
filters: {
|
|
662
|
+
author: {
|
|
663
|
+
fullName: {
|
|
664
|
+
operation: 'equals',
|
|
665
|
+
value: 'some name'
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
result = await validator.schemas.AuthorsReportSpecification.validate(
|
|
672
|
+
reportSpec
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
expect(result).toHaveProperty('valid', true);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test('Validate schema - 2', async () => {
|
|
679
|
+
const validator = new SchemaRegistry()
|
|
680
|
+
.addSchema('Date', ({ object }) =>
|
|
681
|
+
object().addValidator((value) =>
|
|
682
|
+
value instanceof Date && !Number.isNaN(value)
|
|
683
|
+
? {
|
|
684
|
+
valid: true
|
|
685
|
+
}
|
|
686
|
+
: {
|
|
687
|
+
valid: false,
|
|
688
|
+
errors: ['should be a valid Date object']
|
|
689
|
+
}
|
|
690
|
+
)
|
|
691
|
+
)
|
|
692
|
+
.addSchema('Module.Schema1', ({ object, string }) =>
|
|
693
|
+
object({
|
|
694
|
+
a: string()
|
|
695
|
+
})
|
|
696
|
+
)
|
|
697
|
+
.addSchema('Module.Schema2', ({ object, number }) =>
|
|
698
|
+
object({
|
|
699
|
+
b: number()
|
|
700
|
+
})
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
const result = await validator.validate(
|
|
704
|
+
object({
|
|
705
|
+
date: alias('Date').optional()
|
|
706
|
+
}),
|
|
707
|
+
{}
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
expect(result).toHaveProperty('valid', true);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
test('Not required alternative schema alias', async () => {
|
|
714
|
+
const validator = new SchemaRegistry()
|
|
715
|
+
.addSchema('Alternate1', ({ object, string }) =>
|
|
716
|
+
object({
|
|
717
|
+
a1: string()
|
|
718
|
+
})
|
|
719
|
+
)
|
|
720
|
+
.addSchema('Alternate2', ({ object, string }) =>
|
|
721
|
+
object({
|
|
722
|
+
a2: string()
|
|
723
|
+
})
|
|
724
|
+
)
|
|
725
|
+
.addSchema('Alternate', ({ union, alias }) =>
|
|
726
|
+
union(alias('Alternate1')).or(alias('Alternate2'))
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
const s = alias('Alternate').optional();
|
|
730
|
+
let result = await validator.validate(s, undefined);
|
|
731
|
+
|
|
732
|
+
expect(result).toHaveProperty('valid', true);
|
|
733
|
+
|
|
734
|
+
result = await validator.validate(
|
|
735
|
+
{
|
|
736
|
+
type: 'alias',
|
|
737
|
+
isRequired: false,
|
|
738
|
+
schemaName: 'Alternate'
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
a2: 'something'
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
expect(result).toHaveProperty('valid', true);
|
|
746
|
+
|
|
747
|
+
result = await validator.validate(
|
|
748
|
+
{
|
|
749
|
+
type: 'alias',
|
|
750
|
+
isRequired: false,
|
|
751
|
+
schemaName: 'Alternate'
|
|
752
|
+
},
|
|
753
|
+
'invalid'
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
expect(result).toHaveProperty('valid', false);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
test('Preprocessors - 1', async () => {
|
|
760
|
+
const validator = new SchemaRegistry().addSchema(
|
|
761
|
+
'Date',
|
|
762
|
+
object().addValidator((value) =>
|
|
763
|
+
value instanceof Date && !Number.isNaN(value)
|
|
764
|
+
? {
|
|
765
|
+
valid: true
|
|
766
|
+
}
|
|
767
|
+
: {
|
|
768
|
+
valid: false,
|
|
769
|
+
errors: ['should be a valid Date object']
|
|
770
|
+
}
|
|
771
|
+
)
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
const schema = object({
|
|
775
|
+
bornAt: alias('Date')
|
|
776
|
+
}).setPropPreprocessor('bornAt', (value: any): Date | undefined => {
|
|
777
|
+
const time = Date.parse((value as Record<string, unknown>).toString());
|
|
778
|
+
if (Number.isNaN(time)) return undefined;
|
|
779
|
+
return new Date(time);
|
|
780
|
+
});
|
|
781
|
+
const result = await validator.validate(schema as Schema, {
|
|
782
|
+
bornAt: new Date().toJSON()
|
|
783
|
+
});
|
|
784
|
+
expect(result).toHaveProperty('valid', true);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
test('Preprocessors - 2', async () => {
|
|
788
|
+
const validator = new SchemaRegistry().addSchema(
|
|
789
|
+
'Date',
|
|
790
|
+
object().addValidator((value) =>
|
|
791
|
+
value instanceof Date && !Number.isNaN(value)
|
|
792
|
+
? {
|
|
793
|
+
valid: true
|
|
794
|
+
}
|
|
795
|
+
: {
|
|
796
|
+
valid: false,
|
|
797
|
+
errors: ['should be a valid Date object']
|
|
798
|
+
}
|
|
799
|
+
)
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
let schema = object({
|
|
803
|
+
bornAt: alias('Date')
|
|
804
|
+
}).setPropPreprocessor('bornAt', 'StringToDate');
|
|
805
|
+
|
|
806
|
+
validator.addPreprocessor(
|
|
807
|
+
'StringToDate',
|
|
808
|
+
(value: unknown): Date | undefined => {
|
|
809
|
+
const time = Date.parse(
|
|
810
|
+
(value as Record<string, unknown>).toString()
|
|
811
|
+
);
|
|
812
|
+
if (Number.isNaN(time)) return undefined;
|
|
813
|
+
return new Date(time);
|
|
814
|
+
}
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
expect(() =>
|
|
818
|
+
validator.addPreprocessor(
|
|
819
|
+
'StringToDate',
|
|
820
|
+
(value: unknown): Date | undefined => {
|
|
821
|
+
const time = Date.parse(
|
|
822
|
+
(value as Record<string, unknown>).toString()
|
|
823
|
+
);
|
|
824
|
+
if (Number.isNaN(time)) return undefined;
|
|
825
|
+
return new Date(time);
|
|
826
|
+
}
|
|
827
|
+
)
|
|
828
|
+
).toThrow();
|
|
829
|
+
|
|
830
|
+
const result = await validator.validate(schema as Schema, {
|
|
831
|
+
bornAt: new Date().toJSON()
|
|
832
|
+
});
|
|
833
|
+
expect(result).toHaveProperty('valid', true);
|
|
834
|
+
|
|
835
|
+
schema = object({
|
|
836
|
+
bornAt: alias('Date'),
|
|
837
|
+
diedAt: alias('Date')
|
|
838
|
+
})
|
|
839
|
+
.setPropPreprocessor('bornAt', 'StringToDate')
|
|
840
|
+
.setPropPreprocessor('diedAt', 'Unregistered') as any;
|
|
841
|
+
|
|
842
|
+
expect(async () => {
|
|
843
|
+
await validator.validate(schema as Schema, {
|
|
844
|
+
bornAt: new Date().toJSON()
|
|
845
|
+
});
|
|
846
|
+
}).rejects.toBeInstanceOf(Error);
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
test('Preprocessors - 3', async () => {
|
|
850
|
+
const validator = new SchemaRegistry().addSchema('Date', ({ object }) =>
|
|
851
|
+
object().addValidator((value: any) =>
|
|
852
|
+
value instanceof Date && !Number.isNaN(value)
|
|
853
|
+
? {
|
|
854
|
+
valid: true
|
|
855
|
+
}
|
|
856
|
+
: {
|
|
857
|
+
valid: false,
|
|
858
|
+
errors: ['should be a valid Date object']
|
|
859
|
+
}
|
|
860
|
+
)
|
|
861
|
+
);
|
|
862
|
+
validator.addPreprocessor(
|
|
863
|
+
'StringToDate',
|
|
864
|
+
(value: unknown): Date | undefined => {
|
|
865
|
+
const time = Date.parse(
|
|
866
|
+
(value as Record<string, unknown>).toString()
|
|
867
|
+
);
|
|
868
|
+
if (Number.isNaN(time)) return undefined;
|
|
869
|
+
return new Date(time);
|
|
870
|
+
}
|
|
871
|
+
);
|
|
872
|
+
|
|
873
|
+
let obj = [new Date().toJSON()];
|
|
874
|
+
|
|
875
|
+
let result = await validator.validate(
|
|
876
|
+
array().ofType(alias('Date')).addPreprocessor('StringToDate'),
|
|
877
|
+
obj
|
|
878
|
+
);
|
|
879
|
+
expect(result).toHaveProperty('valid', true);
|
|
880
|
+
|
|
881
|
+
obj = [new Date().toJSON()];
|
|
882
|
+
result = await validator.validate(
|
|
883
|
+
array()
|
|
884
|
+
.ofType(alias('Date'))
|
|
885
|
+
.addPreprocessor((value: unknown): Date | undefined => {
|
|
886
|
+
const time = Date.parse(
|
|
887
|
+
(value as Record<string, unknown>).toString()
|
|
888
|
+
);
|
|
889
|
+
if (Number.isNaN(time)) return undefined;
|
|
890
|
+
return new Date(time);
|
|
891
|
+
}),
|
|
892
|
+
obj
|
|
893
|
+
);
|
|
894
|
+
expect(result).toHaveProperty('valid', true);
|
|
895
|
+
|
|
896
|
+
obj = ['sdfsdf12', new Date().toJSON()];
|
|
897
|
+
result = await validator.validate(
|
|
898
|
+
array()
|
|
899
|
+
.ofType(alias('Date'))
|
|
900
|
+
.addPreprocessor((value: unknown): Date | undefined => {
|
|
901
|
+
const time = Date.parse(
|
|
902
|
+
(value as Record<string, unknown>).toString()
|
|
903
|
+
);
|
|
904
|
+
if (Number.isNaN(time)) return undefined;
|
|
905
|
+
return new Date(time);
|
|
906
|
+
}),
|
|
907
|
+
obj
|
|
908
|
+
);
|
|
909
|
+
expect(result).toHaveProperty('valid', false);
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
test('Preprocessors - 3', async () => {
|
|
913
|
+
const validator = new SchemaRegistry();
|
|
914
|
+
|
|
915
|
+
type SomeType = { age: number; marriedAt: number };
|
|
916
|
+
|
|
917
|
+
const schema = object({
|
|
918
|
+
age: number(),
|
|
919
|
+
marriedAt: number()
|
|
920
|
+
})
|
|
921
|
+
.setPropPreprocessor('age', (): number => 40)
|
|
922
|
+
.setPropPreprocessor('*', (value: SomeType): void => {
|
|
923
|
+
if (value.marriedAt > value.age) {
|
|
924
|
+
value.marriedAt = value.age;
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
const obj = {
|
|
929
|
+
age: 50,
|
|
930
|
+
marriedAt: 80
|
|
931
|
+
};
|
|
932
|
+
await validator.validate(schema as Schema, obj);
|
|
933
|
+
expect(obj).toHaveProperty('marriedAt', 40);
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
test('Preprocessors - 4', async () => {
|
|
937
|
+
const validator = new SchemaRegistry().addSchema('Date', ({ object }) =>
|
|
938
|
+
object().addValidator((value: any) =>
|
|
939
|
+
value instanceof Date && !Number.isNaN(value)
|
|
940
|
+
? {
|
|
941
|
+
valid: true
|
|
942
|
+
}
|
|
943
|
+
: {
|
|
944
|
+
valid: false,
|
|
945
|
+
errors: ['should be a valid Date object']
|
|
946
|
+
}
|
|
947
|
+
)
|
|
948
|
+
);
|
|
949
|
+
|
|
950
|
+
validator.addPreprocessor(
|
|
951
|
+
'StringToDate',
|
|
952
|
+
(value: unknown): Date | undefined => {
|
|
953
|
+
const time = Date.parse(
|
|
954
|
+
(value as Record<string, unknown>).toString()
|
|
955
|
+
);
|
|
956
|
+
if (Number.isNaN(time)) return undefined;
|
|
957
|
+
return new Date(time);
|
|
958
|
+
}
|
|
959
|
+
);
|
|
960
|
+
|
|
961
|
+
let obj = [new Date().toJSON()];
|
|
962
|
+
|
|
963
|
+
let result = await validator.validate(
|
|
964
|
+
array().ofType(alias('Date')).addPreprocessor('StringToDate'),
|
|
965
|
+
obj
|
|
966
|
+
);
|
|
967
|
+
expect(result).toHaveProperty('valid', true);
|
|
968
|
+
|
|
969
|
+
obj = [new Date().toJSON()];
|
|
970
|
+
result = await validator.validate(
|
|
971
|
+
array()
|
|
972
|
+
.ofType(alias('Date'))
|
|
973
|
+
.addPreprocessor((value: unknown): Date | undefined => {
|
|
974
|
+
const time = Date.parse(
|
|
975
|
+
(value as Record<string, unknown>).toString()
|
|
976
|
+
);
|
|
977
|
+
if (Number.isNaN(time)) return undefined;
|
|
978
|
+
return new Date(time);
|
|
979
|
+
}),
|
|
980
|
+
obj
|
|
981
|
+
);
|
|
982
|
+
expect(result).toHaveProperty('valid', true);
|
|
983
|
+
|
|
984
|
+
obj = ['sdfsdf12', new Date().toJSON()];
|
|
985
|
+
result = await validator.validate(
|
|
986
|
+
array()
|
|
987
|
+
.ofType(alias('Date'))
|
|
988
|
+
.addPreprocessor((value: unknown): Date | undefined => {
|
|
989
|
+
const time = Date.parse(
|
|
990
|
+
(value as Record<string, unknown>).toString()
|
|
991
|
+
);
|
|
992
|
+
if (Number.isNaN(time)) return undefined;
|
|
993
|
+
return new Date(time);
|
|
994
|
+
}),
|
|
995
|
+
obj
|
|
996
|
+
);
|
|
997
|
+
expect(result).toHaveProperty('valid', false);
|
|
998
|
+
});
|
|
999
|
+
test('Submodules - 2', async () => {
|
|
1000
|
+
const validator = new SchemaRegistry()
|
|
1001
|
+
.addSchema('Module1.Schema1', ({ object }) => object())
|
|
1002
|
+
.addSchema('Module1.Schema2', ({ object, number }) =>
|
|
1003
|
+
object({
|
|
1004
|
+
a: number()
|
|
1005
|
+
})
|
|
1006
|
+
);
|
|
1007
|
+
|
|
1008
|
+
const result = validator.schemas;
|
|
1009
|
+
|
|
1010
|
+
expect(result).toHaveProperty('Module1');
|
|
1011
|
+
expect(result).toHaveProperty('Module1.Schema1');
|
|
1012
|
+
expect(result).toHaveProperty('Module1.Schema2');
|
|
1013
|
+
|
|
1014
|
+
const result2 = await validator.schemas.Module1.Schema2.validate({
|
|
1015
|
+
a: 234
|
|
1016
|
+
});
|
|
1017
|
+
expect(result2).toHaveProperty('valid', true);
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
test('Submodules - 3', async () => {
|
|
1021
|
+
const validator = new SchemaRegistry()
|
|
1022
|
+
.addSchema('Module1.Schema1', ({ object, number }) =>
|
|
1023
|
+
object({
|
|
1024
|
+
b: number()
|
|
1025
|
+
})
|
|
1026
|
+
)
|
|
1027
|
+
.addSchema('Module1.Schema2', ({ object, alias }) =>
|
|
1028
|
+
object({
|
|
1029
|
+
a: alias('Module1.Schema1')
|
|
1030
|
+
})
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1033
|
+
const result2 = await validator.schemas.Module1.Schema2.validate({
|
|
1034
|
+
a: {
|
|
1035
|
+
b: 20
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
expect(result2).toHaveProperty('valid', true);
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
test('Submodules - 4', async () => {
|
|
1042
|
+
const validator = new SchemaRegistry()
|
|
1043
|
+
.addSchema('Module1.Schema1', ({ object, number }) =>
|
|
1044
|
+
object({
|
|
1045
|
+
b: number()
|
|
1046
|
+
})
|
|
1047
|
+
)
|
|
1048
|
+
.addSchema('Module1.Schema2', ({ object, alias }) =>
|
|
1049
|
+
object({
|
|
1050
|
+
a: alias('Module1.Schema3' as any)
|
|
1051
|
+
})
|
|
1052
|
+
);
|
|
1053
|
+
|
|
1054
|
+
await validator.schemas.Module1.Schema2.validate({
|
|
1055
|
+
a: {
|
|
1056
|
+
b: 20
|
|
1057
|
+
}
|
|
1058
|
+
}).catch((e) => expect(e).toBeInstanceOf(Error));
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
test('No unknown fields - 1', async () => {
|
|
1062
|
+
const validator = new SchemaRegistry().addSchema(
|
|
1063
|
+
'Schema1',
|
|
1064
|
+
({ object, number }) =>
|
|
1065
|
+
object({
|
|
1066
|
+
b: number()
|
|
1067
|
+
}).noUnknownProps()
|
|
1068
|
+
);
|
|
1069
|
+
|
|
1070
|
+
const result = await validator.schemas.Schema1.validate({
|
|
1071
|
+
b: 20,
|
|
1072
|
+
c: 'something'
|
|
1073
|
+
});
|
|
1074
|
+
expect(result).toHaveProperty('valid', false);
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
test('No unknown fields - 2', async () => {
|
|
1078
|
+
const validator = new SchemaRegistry().addSchema(
|
|
1079
|
+
'Schema1',
|
|
1080
|
+
({ object, number }) =>
|
|
1081
|
+
object({
|
|
1082
|
+
b: number()
|
|
1083
|
+
}).canHaveUnknownProps()
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
const result = await validator.schemas.Schema1.validate({
|
|
1087
|
+
b: 20,
|
|
1088
|
+
c: 'something'
|
|
1089
|
+
});
|
|
1090
|
+
expect(result).toHaveProperty('valid', true);
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
test('addSchemaFrom - 1', async () => {
|
|
1094
|
+
const registry = new SchemaRegistry()
|
|
1095
|
+
.addSchema('Person', ({ string, object }) =>
|
|
1096
|
+
object({
|
|
1097
|
+
first: string(),
|
|
1098
|
+
last: string()
|
|
1099
|
+
})
|
|
1100
|
+
)
|
|
1101
|
+
.addSchemaFrom('Person', 'PersonWithEmail', ({ schema, string }) =>
|
|
1102
|
+
schema.addProps({
|
|
1103
|
+
email: string()
|
|
1104
|
+
})
|
|
1105
|
+
);
|
|
1106
|
+
expect(
|
|
1107
|
+
registry.schemas.Person === registry.schemas.PersonWithEmail
|
|
1108
|
+
).toEqual(false);
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
test('Object returned - 1', async () => {
|
|
1112
|
+
const registry = new SchemaRegistry().addSchema(
|
|
1113
|
+
'Person',
|
|
1114
|
+
({ object, string }) =>
|
|
1115
|
+
object({
|
|
1116
|
+
first: string(),
|
|
1117
|
+
last: string()
|
|
1118
|
+
})
|
|
1119
|
+
);
|
|
1120
|
+
const p = {
|
|
1121
|
+
first: 'John',
|
|
1122
|
+
last: 'Smith'
|
|
1123
|
+
};
|
|
1124
|
+
const { valid, object } = await registry.schemas.Person.validate(p);
|
|
1125
|
+
|
|
1126
|
+
expect(valid).toEqual(true);
|
|
1127
|
+
expect(object === p).toEqual(true);
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
test('makeAllPropsOptional - 1', async () => {
|
|
1131
|
+
const registry = new SchemaRegistry().addSchema(
|
|
1132
|
+
'Schema1',
|
|
1133
|
+
({ object, number }) =>
|
|
1134
|
+
object({
|
|
1135
|
+
id: number(),
|
|
1136
|
+
first: number(),
|
|
1137
|
+
second: number()
|
|
1138
|
+
})
|
|
1139
|
+
.makeAllPropsOptional()
|
|
1140
|
+
.makePropRequired('id')
|
|
1141
|
+
);
|
|
1142
|
+
|
|
1143
|
+
const { valid } = await registry.schemas.Schema1.validate({
|
|
1144
|
+
id: 123
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
const { valid: valid2 } = await registry.schemas.Schema1.validate({});
|
|
1148
|
+
|
|
1149
|
+
expect(valid).toEqual(true);
|
|
1150
|
+
expect(valid2).toEqual(false);
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
test('no unknown props allowed by default', async () => {
|
|
1154
|
+
const registry = new SchemaRegistry().addSchema(
|
|
1155
|
+
'User',
|
|
1156
|
+
({ object, string }) =>
|
|
1157
|
+
object({
|
|
1158
|
+
firstName: string(),
|
|
1159
|
+
lastName: string()
|
|
1160
|
+
})
|
|
1161
|
+
);
|
|
1162
|
+
|
|
1163
|
+
const { valid } = await registry.schemas.User.validate({
|
|
1164
|
+
firstName: 'John',
|
|
1165
|
+
lastName: 'Smith',
|
|
1166
|
+
age: 20
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
expect(valid).toEqual(false);
|
|
1170
|
+
|
|
1171
|
+
const registry2 = registry.addSchemaFrom('User', 'UserOpen', ({ schema }) =>
|
|
1172
|
+
schema.canHaveUnknownProps()
|
|
1173
|
+
);
|
|
1174
|
+
|
|
1175
|
+
const { valid: valid2 } = await registry2.schemas.UserOpen.validate({
|
|
1176
|
+
firstName: 'John',
|
|
1177
|
+
lastName: 'Smith',
|
|
1178
|
+
age: 20
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
expect(valid2).toEqual(true);
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
test('Func - 1', async () => {
|
|
1185
|
+
const validator = new SchemaRegistry().addSchema('something', ({ func }) =>
|
|
1186
|
+
func()
|
|
1187
|
+
);
|
|
1188
|
+
expect(validator.schemas.something).toBeDefined();
|
|
1189
|
+
expect(await validator.schemas.something.validate(123)).toHaveProperty(
|
|
1190
|
+
'valid',
|
|
1191
|
+
false
|
|
1192
|
+
);
|
|
1193
|
+
expect(await validator.schemas.something.validate(() => 10)).toHaveProperty(
|
|
1194
|
+
'valid',
|
|
1195
|
+
true
|
|
1196
|
+
);
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
test('Func - 2', async () => {
|
|
1200
|
+
const validator = new SchemaRegistry().addSchema(
|
|
1201
|
+
'something',
|
|
1202
|
+
({ func, string, object }) =>
|
|
1203
|
+
object({
|
|
1204
|
+
name: string(),
|
|
1205
|
+
f: func()
|
|
1206
|
+
})
|
|
1207
|
+
);
|
|
1208
|
+
expect(
|
|
1209
|
+
await validator.schemas.something.validate({
|
|
1210
|
+
name: '123',
|
|
1211
|
+
f: 324
|
|
1212
|
+
})
|
|
1213
|
+
).toHaveProperty('valid', false);
|
|
1214
|
+
expect(
|
|
1215
|
+
await validator.schemas.something.validate({
|
|
1216
|
+
name: '1234',
|
|
1217
|
+
f: () => 10
|
|
1218
|
+
})
|
|
1219
|
+
).toHaveProperty('valid', true);
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
test('Func - 3', async () => {
|
|
1223
|
+
const validator = new SchemaRegistry().addSchema(
|
|
1224
|
+
'something',
|
|
1225
|
+
({ func, string, object }) =>
|
|
1226
|
+
object({
|
|
1227
|
+
name: string(),
|
|
1228
|
+
f: func().optional()
|
|
1229
|
+
})
|
|
1230
|
+
);
|
|
1231
|
+
|
|
1232
|
+
expect(
|
|
1233
|
+
await validator.schemas.something.validate({
|
|
1234
|
+
name: '123',
|
|
1235
|
+
f: 324
|
|
1236
|
+
})
|
|
1237
|
+
).toHaveProperty('valid', false);
|
|
1238
|
+
expect(
|
|
1239
|
+
await validator.schemas.something.validate({
|
|
1240
|
+
name: '1234'
|
|
1241
|
+
})
|
|
1242
|
+
).toHaveProperty('valid', true);
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
test('Func - 3', async () => {
|
|
1246
|
+
const validator = new SchemaRegistry().addSchema(
|
|
1247
|
+
'something',
|
|
1248
|
+
({ func, string, object, union }) =>
|
|
1249
|
+
object({
|
|
1250
|
+
name: string(),
|
|
1251
|
+
f: union(func(), object().canHaveUnknownProps()).optional()
|
|
1252
|
+
})
|
|
1253
|
+
);
|
|
1254
|
+
|
|
1255
|
+
expect(
|
|
1256
|
+
await validator.schemas.something.validate({
|
|
1257
|
+
name: '123',
|
|
1258
|
+
f: 324
|
|
1259
|
+
})
|
|
1260
|
+
).toHaveProperty('valid', false);
|
|
1261
|
+
expect(
|
|
1262
|
+
await validator.schemas.something.validate({
|
|
1263
|
+
name: '1234'
|
|
1264
|
+
})
|
|
1265
|
+
).toHaveProperty('valid', true);
|
|
1266
|
+
expect(
|
|
1267
|
+
await validator.schemas.something.validate({
|
|
1268
|
+
name: '1234',
|
|
1269
|
+
f: { a: 34 }
|
|
1270
|
+
})
|
|
1271
|
+
).toHaveProperty('valid', true);
|
|
1272
|
+
expect(
|
|
1273
|
+
await validator.schemas.something.validate({
|
|
1274
|
+
name: '1234',
|
|
1275
|
+
f: () => 20
|
|
1276
|
+
})
|
|
1277
|
+
).toHaveProperty('valid', true);
|
|
1278
|
+
expect(
|
|
1279
|
+
await validator.schemas.something.validate({
|
|
1280
|
+
name: '1234',
|
|
1281
|
+
f: '2345'
|
|
1282
|
+
})
|
|
1283
|
+
).toHaveProperty('valid', false);
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
test('External Schema - 1', async () => {
|
|
1287
|
+
const validator1 = new SchemaRegistry().addSchema(
|
|
1288
|
+
'person',
|
|
1289
|
+
({ object, number }) =>
|
|
1290
|
+
object({
|
|
1291
|
+
age: number().min(0),
|
|
1292
|
+
marriedAt: number().min(18)
|
|
1293
|
+
})
|
|
1294
|
+
);
|
|
1295
|
+
|
|
1296
|
+
const validator2 = new SchemaRegistry().addSchema(
|
|
1297
|
+
'company',
|
|
1298
|
+
({ object, string }) =>
|
|
1299
|
+
object({
|
|
1300
|
+
name: string(),
|
|
1301
|
+
director: validator1.schemas.person.schema.optional()
|
|
1302
|
+
})
|
|
1303
|
+
);
|
|
1304
|
+
|
|
1305
|
+
const result = await validator2.schemas.company.validate({
|
|
1306
|
+
name: 'Some',
|
|
1307
|
+
director: {
|
|
1308
|
+
age: 40,
|
|
1309
|
+
marriedAt: 22
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
expect(result).toHaveProperty('valid', true);
|
|
1314
|
+
|
|
1315
|
+
const result2 = await validator2.schemas.company.validate({
|
|
1316
|
+
name: 'Some',
|
|
1317
|
+
director: {
|
|
1318
|
+
age: -10,
|
|
1319
|
+
marriedAt: 22
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
expect(result2).toHaveProperty('valid', false);
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
test('External Schema - 2', async () => {
|
|
1327
|
+
const validator1 = new SchemaRegistry()
|
|
1328
|
+
.addSchema('address', ({ object, string }) =>
|
|
1329
|
+
object({
|
|
1330
|
+
city: string(),
|
|
1331
|
+
street: string().addValidator((v) => ({ valid: v.length > 0 }))
|
|
1332
|
+
}).addValidator((val) => ({ valid: val.city.length > 0 }))
|
|
1333
|
+
)
|
|
1334
|
+
.addSchema('person', ({ object, number, alias }) =>
|
|
1335
|
+
object({
|
|
1336
|
+
age: number().min(0),
|
|
1337
|
+
marriedAt: number()
|
|
1338
|
+
.min(18)
|
|
1339
|
+
.addValidator((v) => ({ valid: v > 10 })),
|
|
1340
|
+
address: alias('address')
|
|
1341
|
+
}).addValidator((val) => ({ valid: val.marriedAt < val.age }))
|
|
1342
|
+
);
|
|
1343
|
+
|
|
1344
|
+
const validator2 = new SchemaRegistry().addSchema(
|
|
1345
|
+
'company',
|
|
1346
|
+
({ object, string, external }) =>
|
|
1347
|
+
object({
|
|
1348
|
+
name: string().addValidator(() => ({ valid: true })),
|
|
1349
|
+
director: external(validator1, 'person')
|
|
1350
|
+
}).addValidator((v) => ({ valid: true }))
|
|
1351
|
+
);
|
|
1352
|
+
|
|
1353
|
+
const result = await validator2.schemas.company.validate({
|
|
1354
|
+
name: 'Some',
|
|
1355
|
+
director: {
|
|
1356
|
+
age: 40,
|
|
1357
|
+
marriedAt: 22,
|
|
1358
|
+
address: {
|
|
1359
|
+
city: 'City upon a Hill',
|
|
1360
|
+
street: '123 Blossom street'
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
expect(result).toHaveProperty('valid', true);
|
|
1366
|
+
|
|
1367
|
+
const result2 = await validator2.schemas.company.validate({
|
|
1368
|
+
name: 'Some',
|
|
1369
|
+
director: {
|
|
1370
|
+
age: -10,
|
|
1371
|
+
marriedAt: 22,
|
|
1372
|
+
address: {
|
|
1373
|
+
city: 'City upon a Hill',
|
|
1374
|
+
street: '123 Blossom street'
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
expect(result2).toHaveProperty('valid', false);
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
test('Empty Validators Bug', async () => {
|
|
1383
|
+
const isValidEmail = (s) => {
|
|
1384
|
+
if (typeof s !== 'string') return false;
|
|
1385
|
+
const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,8}$/;
|
|
1386
|
+
return regex.test(s);
|
|
1387
|
+
};
|
|
1388
|
+
const validator = new SchemaRegistry().addSchema(
|
|
1389
|
+
'Report',
|
|
1390
|
+
({ object, array, string }) =>
|
|
1391
|
+
object({
|
|
1392
|
+
recepients: array()
|
|
1393
|
+
.ofType(
|
|
1394
|
+
string().addValidator((val) => {
|
|
1395
|
+
if (isValidEmail(val)) {
|
|
1396
|
+
return { valid: true };
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
valid: false,
|
|
1400
|
+
errors: [`${val} is not a valid email`]
|
|
1401
|
+
};
|
|
1402
|
+
})
|
|
1403
|
+
)
|
|
1404
|
+
.addValidator((val) => {
|
|
1405
|
+
if (!Array.isArray(val)) {
|
|
1406
|
+
return {
|
|
1407
|
+
valid: false,
|
|
1408
|
+
errors: ['not an array']
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
const map = {};
|
|
1412
|
+
const lowerCased = val.map((v) => v.toLowerCase());
|
|
1413
|
+
for (let i = 0; i < lowerCased.length; i++) {
|
|
1414
|
+
if (lowerCased[i] in map) {
|
|
1415
|
+
return {
|
|
1416
|
+
valid: false,
|
|
1417
|
+
errors: ['no duplicates allowed']
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
map[lowerCased[i]] = true;
|
|
1421
|
+
}
|
|
1422
|
+
return { valid: true };
|
|
1423
|
+
})
|
|
1424
|
+
})
|
|
1425
|
+
);
|
|
1426
|
+
|
|
1427
|
+
const result1 = await validator.schemas.Report.validate({
|
|
1428
|
+
recepients: ['user1@domain1.com', 'user2@domain1.com']
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
expect(result1).toHaveProperty('valid', true);
|
|
1432
|
+
|
|
1433
|
+
const result2 = await validator.schemas.Report.validate({
|
|
1434
|
+
recepients: ['user1@domain1.com', 'user1@domain1.com']
|
|
1435
|
+
});
|
|
1436
|
+
|
|
1437
|
+
expect(result2).toHaveProperty('valid', false);
|
|
1438
|
+
});
|