@trayio/commons 4.5.0 → 4.7.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.
|
@@ -4,13 +4,12 @@ import * as t from 'io-ts';
|
|
|
4
4
|
import { DynamicObject } from '../dynamictype/DynamicType';
|
|
5
5
|
declare const dynamicSchemaTypeDescriptor: t.BrandC<t.Type<DynamicObject, DynamicObject, unknown>, DynamicSchemaBrand>;
|
|
6
6
|
export type DynamicSchema = t.TypeOf<typeof dynamicSchemaTypeDescriptor>;
|
|
7
|
-
export declare const recursivelyRemoveEmptyRequiredAndAddAdditionalProperties: (schema: DynamicObject, strict: O.Option<boolean>) => void;
|
|
8
7
|
export interface DynamicSchemaBrand {
|
|
9
8
|
readonly DynamicSchema: unique symbol;
|
|
10
9
|
}
|
|
11
10
|
export interface DynamicSchemaInterface {
|
|
12
|
-
fromDynamicObject: (
|
|
13
|
-
validate: (schema: DynamicSchema | DynamicObject, value: DynamicObject, strict
|
|
11
|
+
fromDynamicObject: (schema: DynamicObject, strict?: O.Option<boolean>) => E.Either<Error, DynamicSchema>;
|
|
12
|
+
validate: (schema: DynamicSchema | DynamicObject, value: DynamicObject, strict?: O.Option<boolean>) => E.Either<Error, DynamicObject>;
|
|
14
13
|
}
|
|
15
14
|
export declare const DynamicSchema: DynamicSchemaInterface;
|
|
16
15
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DynamicSchema.d.ts","sourceRoot":"","sources":["../../src/dynamicschema/DynamicSchema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,CAAC,MAAM,OAAO,CAAC;AAG3B,OAAO,EACN,aAAa,EAEb,MAAM,4BAA4B,CAAC;AAqBpC,QAAA,MAAM,2BAA2B,6EAKhC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,2BAA2B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"DynamicSchema.d.ts","sourceRoot":"","sources":["../../src/dynamicschema/DynamicSchema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,CAAC,MAAM,OAAO,CAAC;AAG3B,OAAO,EACN,aAAa,EAEb,MAAM,4BAA4B,CAAC;AAqBpC,QAAA,MAAM,2BAA2B,6EAKhC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,2BAA2B,CAAC,CAAC;AA4CzE,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,sBAAsB;IACtC,iBAAiB,EAAE,CAClB,MAAM,EAAE,aAAa,EACrB,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KACtB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACpC,QAAQ,EAAE,CACT,MAAM,EAAE,aAAa,GAAG,aAAa,EACrC,KAAK,EAAE,aAAa,EACpB,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KACtB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;CACpC;AAED,eAAO,MAAM,aAAa,EAAE,sBA+B3B,CAAC"}
|
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.DynamicSchema =
|
|
29
|
+
exports.DynamicSchema = void 0;
|
|
30
30
|
const ajv_draft_04_1 = __importDefault(require("ajv-draft-04"));
|
|
31
31
|
const ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
32
32
|
const E = __importStar(require("fp-ts/Either"));
|
|
@@ -37,7 +37,7 @@ const deep_copy_ts_1 = require("deep-copy-ts");
|
|
|
37
37
|
const DynamicType_1 = require("../dynamictype/DynamicType");
|
|
38
38
|
const TypeCodec_1 = require("../codec/TypeCodec");
|
|
39
39
|
const Try_1 = require("../try/Try");
|
|
40
|
-
const ajv = new ajv_draft_04_1.default({ strict: false });
|
|
40
|
+
const ajv = new ajv_draft_04_1.default({ strict: false, removeAdditional: 'all' });
|
|
41
41
|
/*
|
|
42
42
|
We define additional formats for the JSON schema
|
|
43
43
|
See: https://ajv.js.org/packages/ajv-formats.html#formats
|
|
@@ -54,14 +54,15 @@ const isDynamicSchema = (o) => {
|
|
|
54
54
|
};
|
|
55
55
|
const dynamicSchemaTypeDescriptor = t.brand(DynamicType_1.dynamicObjectTypeDescriptor, (input) => isDynamicSchema(input), 'DynamicSchema');
|
|
56
56
|
const dynamicSchemaTypeCodec = TypeCodec_1.TypeCodec.fromDescriptor(dynamicSchemaTypeDescriptor);
|
|
57
|
-
const
|
|
57
|
+
const preprocessSchema = (schema, strict) => {
|
|
58
58
|
const hasStrict = O.isSome(strict);
|
|
59
|
+
delete schema.$schema;
|
|
59
60
|
if (schema.oneOf || schema.anyOf || schema.allOf) {
|
|
60
61
|
const schemas = (schema.oneOf ||
|
|
61
62
|
schema.anyOf ||
|
|
62
63
|
schema.allOf);
|
|
63
64
|
schemas.forEach((subSchema) => {
|
|
64
|
-
(
|
|
65
|
+
preprocessSchema(subSchema, strict);
|
|
65
66
|
});
|
|
66
67
|
}
|
|
67
68
|
if (schema.properties) {
|
|
@@ -77,24 +78,28 @@ const recursivelyRemoveEmptyRequiredAndAddAdditionalProperties = (schema, strict
|
|
|
77
78
|
Object.keys(properties).forEach((key) => {
|
|
78
79
|
const property = properties[key];
|
|
79
80
|
if (property.type === 'object' || property.type === 'array') {
|
|
80
|
-
(
|
|
81
|
+
preprocessSchema(property, strict);
|
|
81
82
|
}
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
85
|
if (schema.type === 'array') {
|
|
85
|
-
(
|
|
86
|
+
preprocessSchema(schema.items, strict);
|
|
86
87
|
}
|
|
87
88
|
};
|
|
88
|
-
exports.recursivelyRemoveEmptyRequiredAndAddAdditionalProperties = recursivelyRemoveEmptyRequiredAndAddAdditionalProperties;
|
|
89
89
|
exports.DynamicSchema = {
|
|
90
|
-
fromDynamicObject: (
|
|
91
|
-
validate: (schema, value, strict) => {
|
|
90
|
+
fromDynamicObject: (schema, strict = O.none) => {
|
|
92
91
|
const copiedSchema = (0, deep_copy_ts_1.deepCopy)(schema);
|
|
93
|
-
(
|
|
92
|
+
preprocessSchema(copiedSchema, strict);
|
|
93
|
+
return (0, function_1.pipe)(dynamicSchemaTypeCodec.decode(copiedSchema), E.mapLeft((e) => new Error(`Failed to create DynamicSchema: ${e}`)));
|
|
94
|
+
},
|
|
95
|
+
validate: (schema, value, strict = O.none) => {
|
|
96
|
+
const copiedSchema = (0, deep_copy_ts_1.deepCopy)(schema);
|
|
97
|
+
preprocessSchema(copiedSchema, strict);
|
|
98
|
+
const copiedValue = (0, deep_copy_ts_1.deepCopy)(value);
|
|
94
99
|
const validateE = (0, Try_1.tryToEither)(() => ajv.compile(copiedSchema));
|
|
95
100
|
return (0, function_1.pipe)(validateE, E.chain((validate) => {
|
|
96
|
-
if (validate(
|
|
97
|
-
return E.right(
|
|
101
|
+
if (validate(copiedValue)) {
|
|
102
|
+
return E.right(copiedValue);
|
|
98
103
|
}
|
|
99
104
|
return E.left(new Error(ajv.errorsText(validate.errors)));
|
|
100
105
|
}));
|
|
@@ -30,6 +30,7 @@ describe('DynamicSchema', () => {
|
|
|
30
30
|
let validSchema;
|
|
31
31
|
let invalidSchema;
|
|
32
32
|
let value;
|
|
33
|
+
let valueWithExtraFields;
|
|
33
34
|
beforeEach(() => {
|
|
34
35
|
validSchema = {
|
|
35
36
|
type: 'object',
|
|
@@ -40,8 +41,27 @@ describe('DynamicSchema', () => {
|
|
|
40
41
|
age: {
|
|
41
42
|
type: 'number',
|
|
42
43
|
},
|
|
44
|
+
races: {
|
|
45
|
+
type: 'array',
|
|
46
|
+
items: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
name: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
},
|
|
52
|
+
car: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
model: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
43
63
|
},
|
|
44
|
-
required: ['name', 'age'],
|
|
64
|
+
required: ['name', 'age', 'races'],
|
|
45
65
|
advanced: [], // not part of json schema but the validator should not be strict with extra elements
|
|
46
66
|
};
|
|
47
67
|
invalidSchema = {
|
|
@@ -59,12 +79,34 @@ describe('DynamicSchema', () => {
|
|
|
59
79
|
value = {
|
|
60
80
|
name: 'John Doe',
|
|
61
81
|
age: 30,
|
|
82
|
+
races: [
|
|
83
|
+
{
|
|
84
|
+
name: 'monza',
|
|
85
|
+
car: {
|
|
86
|
+
model: 'ferrari',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
valueWithExtraFields = {
|
|
92
|
+
name: 'John Doe',
|
|
93
|
+
age: 30,
|
|
94
|
+
extra: 'field',
|
|
95
|
+
races: [
|
|
96
|
+
{
|
|
97
|
+
name: 'monza',
|
|
98
|
+
country: 'italy',
|
|
99
|
+
car: {
|
|
100
|
+
model: 'ferrari',
|
|
101
|
+
year: 2017,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
62
105
|
};
|
|
63
106
|
});
|
|
64
107
|
describe('fromDynamicObject', () => {
|
|
65
108
|
it('should return a Right DynamicSchema when given a valid schema object', () => {
|
|
66
109
|
const result = DynamicSchema_1.DynamicSchema.fromDynamicObject(validSchema);
|
|
67
|
-
console.log(JSON.stringify(result));
|
|
68
110
|
expect(E.isRight(result)).toBe(true);
|
|
69
111
|
if (E.isRight(result)) {
|
|
70
112
|
expect(result.right).toEqual(validSchema);
|
|
@@ -78,40 +120,35 @@ describe('DynamicSchema', () => {
|
|
|
78
120
|
expect(result.left.message).toContain('Failed to create DynamicSchema:');
|
|
79
121
|
}
|
|
80
122
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
expect(result.left).toBeInstanceOf(Error);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
123
|
+
it('should remove empty $schema property', () => {
|
|
124
|
+
const schema = {
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties: {
|
|
127
|
+
name: {
|
|
128
|
+
type: 'string',
|
|
129
|
+
},
|
|
130
|
+
age: {
|
|
131
|
+
type: 'number',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
advanced: [],
|
|
135
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
136
|
+
};
|
|
137
|
+
const expectedSchema = {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
name: {
|
|
141
|
+
type: 'string',
|
|
142
|
+
},
|
|
143
|
+
age: {
|
|
144
|
+
type: 'number',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
advanced: [],
|
|
148
|
+
};
|
|
149
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.none);
|
|
150
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
112
151
|
});
|
|
113
|
-
});
|
|
114
|
-
describe('recursivelyRemoveEmptyRequiredAndAddAdditionalProperties', () => {
|
|
115
152
|
it('should correctly remove empty required arrays and not add additionalProperties when strict is not set', () => {
|
|
116
153
|
const schema = {
|
|
117
154
|
type: 'object',
|
|
@@ -138,8 +175,8 @@ describe('DynamicSchema', () => {
|
|
|
138
175
|
},
|
|
139
176
|
advanced: [],
|
|
140
177
|
};
|
|
141
|
-
|
|
142
|
-
expect(
|
|
178
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.none);
|
|
179
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
143
180
|
});
|
|
144
181
|
it('should correctly keep populated required arrays and not add additionalProperties when strict is not set', () => {
|
|
145
182
|
const schema = {
|
|
@@ -168,8 +205,8 @@ describe('DynamicSchema', () => {
|
|
|
168
205
|
required: ['age', 'name'],
|
|
169
206
|
advanced: [],
|
|
170
207
|
};
|
|
171
|
-
|
|
172
|
-
expect(
|
|
208
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.none);
|
|
209
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
173
210
|
});
|
|
174
211
|
it('should correctly remove empty required and add additionalProperties=true to object schema when strict is set', () => {
|
|
175
212
|
const schema = {
|
|
@@ -198,8 +235,8 @@ describe('DynamicSchema', () => {
|
|
|
198
235
|
advanced: [],
|
|
199
236
|
additionalProperties: true,
|
|
200
237
|
};
|
|
201
|
-
|
|
202
|
-
expect(
|
|
238
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.some(false));
|
|
239
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
203
240
|
});
|
|
204
241
|
it('should correctly add additionalProperties=false to object schema', () => {
|
|
205
242
|
const schema = validSchema;
|
|
@@ -212,13 +249,34 @@ describe('DynamicSchema', () => {
|
|
|
212
249
|
age: {
|
|
213
250
|
type: 'number',
|
|
214
251
|
},
|
|
252
|
+
races: {
|
|
253
|
+
type: 'array',
|
|
254
|
+
items: {
|
|
255
|
+
type: 'object',
|
|
256
|
+
additionalProperties: false,
|
|
257
|
+
properties: {
|
|
258
|
+
name: {
|
|
259
|
+
type: 'string',
|
|
260
|
+
},
|
|
261
|
+
car: {
|
|
262
|
+
type: 'object',
|
|
263
|
+
additionalProperties: false,
|
|
264
|
+
properties: {
|
|
265
|
+
model: {
|
|
266
|
+
type: 'string',
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
215
273
|
},
|
|
216
|
-
required: ['name', 'age'],
|
|
274
|
+
required: ['name', 'age', 'races'],
|
|
217
275
|
advanced: [],
|
|
218
276
|
additionalProperties: false,
|
|
219
277
|
};
|
|
220
|
-
|
|
221
|
-
expect(
|
|
278
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.some(true));
|
|
279
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
222
280
|
});
|
|
223
281
|
it('should correctly add additionalProperties=true to complex object schema', () => {
|
|
224
282
|
const schema = {
|
|
@@ -284,15 +342,15 @@ describe('DynamicSchema', () => {
|
|
|
284
342
|
},
|
|
285
343
|
additionalProperties: true,
|
|
286
344
|
};
|
|
287
|
-
|
|
288
|
-
expect(
|
|
345
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.some(false));
|
|
346
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
289
347
|
});
|
|
290
348
|
it('should skip mutation if there is no object type schema', () => {
|
|
291
349
|
const schema = {
|
|
292
350
|
type: 'string',
|
|
293
351
|
};
|
|
294
|
-
|
|
295
|
-
expect(
|
|
352
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.some(true));
|
|
353
|
+
expect(actualSchemaE).toEqual(E.right(schema));
|
|
296
354
|
});
|
|
297
355
|
it('should correctly add additionalProperties=true to oneOf schemas', () => {
|
|
298
356
|
const schema = {
|
|
@@ -339,8 +397,8 @@ describe('DynamicSchema', () => {
|
|
|
339
397
|
},
|
|
340
398
|
],
|
|
341
399
|
};
|
|
342
|
-
|
|
343
|
-
expect(
|
|
400
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.some(false));
|
|
401
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
344
402
|
});
|
|
345
403
|
it('should correctly add additionalProperties=true to anyOf schemas', () => {
|
|
346
404
|
const schema = {
|
|
@@ -387,8 +445,8 @@ describe('DynamicSchema', () => {
|
|
|
387
445
|
},
|
|
388
446
|
],
|
|
389
447
|
};
|
|
390
|
-
|
|
391
|
-
expect(
|
|
448
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.some(false));
|
|
449
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
392
450
|
});
|
|
393
451
|
it('should correctly add additionalProperties=true to allOf schemas', () => {
|
|
394
452
|
const schema = {
|
|
@@ -435,8 +493,50 @@ describe('DynamicSchema', () => {
|
|
|
435
493
|
},
|
|
436
494
|
],
|
|
437
495
|
};
|
|
438
|
-
|
|
439
|
-
expect(
|
|
496
|
+
const actualSchemaE = DynamicSchema_1.DynamicSchema.fromDynamicObject(schema, O.some(false));
|
|
497
|
+
expect(actualSchemaE).toEqual(E.right(expectedSchema));
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
describe('validate', () => {
|
|
501
|
+
it('should return a Right with the value when the value matches the schema', () => {
|
|
502
|
+
const schema = DynamicSchema_1.DynamicSchema.fromDynamicObject(validSchema);
|
|
503
|
+
if (E.isRight(schema)) {
|
|
504
|
+
const result = DynamicSchema_1.DynamicSchema.validate(schema.right, value, O.none);
|
|
505
|
+
expect(E.isRight(result)).toBe(true);
|
|
506
|
+
if (E.isRight(result)) {
|
|
507
|
+
expect(result.right).toEqual(value);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
it('should return a Right with the value when strict is false and value matches the schema', () => {
|
|
512
|
+
const schema = DynamicSchema_1.DynamicSchema.fromDynamicObject(validSchema);
|
|
513
|
+
if (E.isRight(schema)) {
|
|
514
|
+
const result = DynamicSchema_1.DynamicSchema.validate(schema.right, value, O.some(false));
|
|
515
|
+
expect(E.isRight(result)).toBe(true);
|
|
516
|
+
if (E.isRight(result)) {
|
|
517
|
+
expect(result.right).toEqual(value);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
it('should return a Right with non extra values when value matches the schema but has extra fields', () => {
|
|
522
|
+
const schema = DynamicSchema_1.DynamicSchema.fromDynamicObject(validSchema);
|
|
523
|
+
if (E.isRight(schema)) {
|
|
524
|
+
const result = DynamicSchema_1.DynamicSchema.validate(schema.right, valueWithExtraFields, O.some(false));
|
|
525
|
+
expect(E.isRight(result)).toBe(true);
|
|
526
|
+
if (E.isRight(result)) {
|
|
527
|
+
expect(result.right).toEqual(value);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
it('should return a Left Error when the value does not match the schema', () => {
|
|
532
|
+
const schema = DynamicSchema_1.DynamicSchema.fromDynamicObject(validSchema);
|
|
533
|
+
if (E.isRight(schema)) {
|
|
534
|
+
const result = DynamicSchema_1.DynamicSchema.validate(schema.right, invalidSchema, O.none);
|
|
535
|
+
expect(E.isLeft(result)).toBe(true);
|
|
536
|
+
if (E.isLeft(result)) {
|
|
537
|
+
expect(result.left).toBeInstanceOf(Error);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
440
540
|
});
|
|
441
541
|
});
|
|
442
542
|
});
|