@cleverbrush/schema 0.0.4 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/schemaValidator.d.ts +1 -1
- package/dist/schemaValidator.js +11 -2
- package/package.json +6 -2
- package/src/index.ts +2 -2
- package/src/schemaValidator.test.ts +245 -10
- package/src/schemaValidator.ts +17 -2
package/dist/index.d.ts
CHANGED
|
@@ -66,8 +66,8 @@ export interface ISchemasProvider<T = Record<string, never>> {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
export interface ISchemaValidator<T = Record<string, never>> {
|
|
69
|
-
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(name: keyof K, schema: L): SchemaValidator<T & {
|
|
70
|
-
[key in keyof K]:
|
|
69
|
+
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(name: keyof K, schema: L | Array<Schema<any>>): SchemaValidator<T & {
|
|
70
|
+
[key in keyof K]: typeof schema;
|
|
71
71
|
}>;
|
|
72
72
|
validate(schema: keyof T | DefaultSchemaType | Schema<any>, obj: any): Promise<ValidationResult>;
|
|
73
73
|
}
|
|
@@ -2,7 +2,7 @@ import { ISchemasProvider, Schema, ISchemaActions, ObjectSchemaDefinitionParam,
|
|
|
2
2
|
export default class SchemaValidator<T = Record<string, never>> implements ISchemasProvider<T>, ISchemaValidator<T> {
|
|
3
3
|
private _schemasMap;
|
|
4
4
|
private _schemasCache;
|
|
5
|
-
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(name: keyof K, schema: L): SchemaValidator<T & {
|
|
5
|
+
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(name: keyof K, schema: L | Array<Schema<any>>): SchemaValidator<T & {
|
|
6
6
|
[key in keyof K]: L;
|
|
7
7
|
}>;
|
|
8
8
|
get schemas(): {
|
package/dist/schemaValidator.js
CHANGED
|
@@ -40,13 +40,18 @@ class SchemaValidator {
|
|
|
40
40
|
addSchemaType(name, schema) {
|
|
41
41
|
if (typeof name !== 'string' || !name)
|
|
42
42
|
throw new Error('Name is required');
|
|
43
|
-
if (typeof schema !== 'object')
|
|
44
|
-
throw new Error('Object is required');
|
|
45
43
|
if (isDefaultType(name.toString()))
|
|
46
44
|
throw new Error(`You can't add a schema named "${name}" because it's a name of a default schema, please consider another name to be used`);
|
|
47
45
|
if (this._schemasMap.has(name.toString())) {
|
|
48
46
|
throw new Error(`Schema "${name}" already exists`);
|
|
49
47
|
}
|
|
48
|
+
if (Array.isArray(schema)) {
|
|
49
|
+
this._schemasMap.set(name.toString(), schema);
|
|
50
|
+
this._schemasCache = null;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
if (typeof schema !== 'object')
|
|
54
|
+
throw new Error('Array or Object is required');
|
|
50
55
|
this._schemasMap.set(name.toString(), {
|
|
51
56
|
...schema,
|
|
52
57
|
type: 'object'
|
|
@@ -123,6 +128,10 @@ class SchemaValidator {
|
|
|
123
128
|
return await this.validateDefaultType(schema, obj);
|
|
124
129
|
}
|
|
125
130
|
else if (typeof this.schemas[schema] !== 'undefined') {
|
|
131
|
+
if (Array.isArray(this.schemas[schema].schema)) {
|
|
132
|
+
return this.validate(this.schemas[schema]
|
|
133
|
+
.schema, obj);
|
|
134
|
+
}
|
|
126
135
|
const objSchema = (0, deep_1.deepExtend)(defaultSchemas.object, this.schemas[schema].schema);
|
|
127
136
|
const res = await (0, validateObject_1.validateObject)(obj, objSchema, this);
|
|
128
137
|
if (!res.valid)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleverbrush/schema",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"object schema validator",
|
|
6
6
|
"schema",
|
|
@@ -10,12 +10,16 @@
|
|
|
10
10
|
"license": "BSD",
|
|
11
11
|
"main": "./dist/index.js",
|
|
12
12
|
"readme": "https://github.com/cleverbrush/libs/tree/master/libs/schema#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "github:cleverbrush/libs"
|
|
16
|
+
},
|
|
13
17
|
"scripts": {
|
|
14
18
|
"watch": "tsc --watch",
|
|
15
19
|
"build": "tsc"
|
|
16
20
|
},
|
|
17
21
|
"dependencies": {
|
|
18
|
-
"@cleverbrush/deep": "0.0.
|
|
22
|
+
"@cleverbrush/deep": "0.0.7"
|
|
19
23
|
},
|
|
20
24
|
"types": "./dist/index.d.ts"
|
|
21
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -124,8 +124,8 @@ export interface ISchemasProvider<T = Record<string, never>> {
|
|
|
124
124
|
export interface ISchemaValidator<T = Record<string, never>> {
|
|
125
125
|
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(
|
|
126
126
|
name: keyof K,
|
|
127
|
-
schema: L
|
|
128
|
-
): SchemaValidator<T & { [key in keyof K]:
|
|
127
|
+
schema: L | Array<Schema<any>>
|
|
128
|
+
): SchemaValidator<T & { [key in keyof K]: typeof schema }>;
|
|
129
129
|
|
|
130
130
|
validate(
|
|
131
131
|
schema: keyof T | DefaultSchemaType | Schema<any>,
|
|
@@ -97,39 +97,39 @@ test('Schemas property is updated after adding a new schema', () => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
test('Trows when trying to add a schema with name = "number"', () => {
|
|
100
|
-
|
|
100
|
+
const validator = new SchemaValidator();
|
|
101
101
|
expect(() => validator.addSchemaType('number', {})).toThrow();
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
test('Trows when trying to add a schema with name = "array"', () => {
|
|
105
|
-
|
|
105
|
+
const validator = new SchemaValidator();
|
|
106
106
|
expect(() => validator.addSchemaType('array', {})).toThrow();
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
test('Trows when trying to add a schema with name = "date"', () => {
|
|
110
|
-
|
|
110
|
+
const validator = new SchemaValidator();
|
|
111
111
|
expect(() => validator.addSchemaType('date', {})).toThrow();
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
test('Trows when trying to add a schema with name = "string"', () => {
|
|
115
|
-
|
|
115
|
+
const validator = new SchemaValidator();
|
|
116
116
|
expect(() => validator.addSchemaType('string', {})).toThrow();
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
test('Throws if schema name is empty', () => {
|
|
120
|
-
|
|
120
|
+
const validator = new SchemaValidator();
|
|
121
121
|
expect(() => validator.addSchemaType('', {})).toThrow();
|
|
122
122
|
});
|
|
123
123
|
|
|
124
124
|
test('Throws if schema name is not a string', () => {
|
|
125
|
-
|
|
125
|
+
const validator = new SchemaValidator();
|
|
126
126
|
expect(() =>
|
|
127
127
|
validator.addSchemaType(new Date() as any as string, {})
|
|
128
128
|
).toThrow();
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
test('Throws if schema is not an object', () => {
|
|
132
|
-
|
|
132
|
+
const validator = new SchemaValidator();
|
|
133
133
|
expect(() =>
|
|
134
134
|
validator.addSchemaType(
|
|
135
135
|
'string',
|
|
@@ -138,6 +138,33 @@ test('Throws if schema is not an object', () => {
|
|
|
138
138
|
).toThrow();
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
+
test('Array as schema type', async () => {
|
|
142
|
+
const validator = new SchemaValidator()
|
|
143
|
+
.addSchemaType('name', {
|
|
144
|
+
properties: {
|
|
145
|
+
name: 'string'
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
.addSchemaType('number_or_string', ['number', 'string', 'name']);
|
|
149
|
+
|
|
150
|
+
let result = await validator.schemas.number_or_string.validate(123);
|
|
151
|
+
|
|
152
|
+
expect(result).toHaveProperty('valid', true);
|
|
153
|
+
|
|
154
|
+
result = await validator.schemas.number_or_string.validate('some string');
|
|
155
|
+
|
|
156
|
+
expect(result).toHaveProperty('valid', true);
|
|
157
|
+
|
|
158
|
+
result = await validator.schemas.number_or_string.validate({});
|
|
159
|
+
|
|
160
|
+
expect(result).toHaveProperty('valid', false);
|
|
161
|
+
|
|
162
|
+
result = await validator.schemas.number_or_string.validate({
|
|
163
|
+
name: 'some name'
|
|
164
|
+
});
|
|
165
|
+
expect(result).toHaveProperty('valid', true);
|
|
166
|
+
});
|
|
167
|
+
|
|
141
168
|
test('Validate - no schema', async () => {
|
|
142
169
|
const validator = new SchemaValidator();
|
|
143
170
|
const cth = jest.fn();
|
|
@@ -741,7 +768,7 @@ test('Validate - array - ofType - 1', async () => {
|
|
|
741
768
|
});
|
|
742
769
|
|
|
743
770
|
test('Validate - object - 1', async () => {
|
|
744
|
-
|
|
771
|
+
const validator = new SchemaValidator().addSchemaType(
|
|
745
772
|
'user',
|
|
746
773
|
getUserSchema()
|
|
747
774
|
);
|
|
@@ -824,7 +851,7 @@ test('Validate - object - 1', async () => {
|
|
|
824
851
|
});
|
|
825
852
|
|
|
826
853
|
test('Validate - object - 2', async () => {
|
|
827
|
-
|
|
854
|
+
const validator = new SchemaValidator().addSchemaType(
|
|
828
855
|
'user',
|
|
829
856
|
deepExtend(getUserSchema(), {
|
|
830
857
|
validators: [
|
|
@@ -853,7 +880,7 @@ test('Validate - object - 2', async () => {
|
|
|
853
880
|
aliases: []
|
|
854
881
|
};
|
|
855
882
|
|
|
856
|
-
|
|
883
|
+
const result = await validator.validate('user', user);
|
|
857
884
|
expect(result).toHaveProperty('valid', false);
|
|
858
885
|
});
|
|
859
886
|
|
|
@@ -885,3 +912,211 @@ test('Validate - one of - 1', async () => {
|
|
|
885
912
|
);
|
|
886
913
|
expect(result).toHaveProperty('valid', true);
|
|
887
914
|
});
|
|
915
|
+
|
|
916
|
+
test('Validate schema', async () => {
|
|
917
|
+
const authorsReportSpecificationSchema = {
|
|
918
|
+
properties: {
|
|
919
|
+
type: {
|
|
920
|
+
type: 'string',
|
|
921
|
+
equals: 'author'
|
|
922
|
+
},
|
|
923
|
+
start: 'Date',
|
|
924
|
+
end: 'Date',
|
|
925
|
+
selectionStart: 'Date',
|
|
926
|
+
selectionEnd: 'Date',
|
|
927
|
+
granularity: ['day', 'week', 'month'],
|
|
928
|
+
metrics: {
|
|
929
|
+
type: 'array',
|
|
930
|
+
ofType: [
|
|
931
|
+
'articlesPublished',
|
|
932
|
+
'searchReferrers',
|
|
933
|
+
'socialReferrers',
|
|
934
|
+
'views',
|
|
935
|
+
'visitors',
|
|
936
|
+
'newVisitors'
|
|
937
|
+
]
|
|
938
|
+
},
|
|
939
|
+
filters: {
|
|
940
|
+
type: 'object',
|
|
941
|
+
properties: {
|
|
942
|
+
author: 'AuthorFilter'
|
|
943
|
+
// publication: 'PublicationFilter',
|
|
944
|
+
// article: 'ArticleFilter'
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
validators: [
|
|
949
|
+
(value) =>
|
|
950
|
+
value.start <= value.end &&
|
|
951
|
+
value.selectionStart <= value.selectionEnd &&
|
|
952
|
+
value.selectionStart >= value.start &&
|
|
953
|
+
value.selectionStart <= value.end &&
|
|
954
|
+
value.selectionEnd >= value.start &&
|
|
955
|
+
value.selectionEnd <= value.end
|
|
956
|
+
? {
|
|
957
|
+
valid: true
|
|
958
|
+
}
|
|
959
|
+
: {
|
|
960
|
+
valid: false,
|
|
961
|
+
errors: [
|
|
962
|
+
'selectionStart <=> selectionEnd should be inside the start <=> end interval'
|
|
963
|
+
]
|
|
964
|
+
}
|
|
965
|
+
]
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const validator = new SchemaValidator()
|
|
969
|
+
.addSchemaType('Date', {
|
|
970
|
+
validators: [
|
|
971
|
+
(value) =>
|
|
972
|
+
value instanceof Date && !Number.isNaN(value)
|
|
973
|
+
? {
|
|
974
|
+
valid: true
|
|
975
|
+
}
|
|
976
|
+
: {
|
|
977
|
+
valid: false,
|
|
978
|
+
errors: ['should be a valid Date object']
|
|
979
|
+
}
|
|
980
|
+
]
|
|
981
|
+
})
|
|
982
|
+
.addSchemaType('EqualsFilterCondition', {
|
|
983
|
+
properties: {
|
|
984
|
+
operation: {
|
|
985
|
+
type: 'string',
|
|
986
|
+
equals: 'equals'
|
|
987
|
+
},
|
|
988
|
+
value: ['object', 'string', 'number']
|
|
989
|
+
}
|
|
990
|
+
})
|
|
991
|
+
.addSchemaType('GreaterThanFilterCondition', {
|
|
992
|
+
properties: {
|
|
993
|
+
operation: {
|
|
994
|
+
type: 'string',
|
|
995
|
+
equals: 'greater_than'
|
|
996
|
+
},
|
|
997
|
+
value: 'number'
|
|
998
|
+
}
|
|
999
|
+
})
|
|
1000
|
+
.addSchemaType('LessThanFilterCondition', {
|
|
1001
|
+
properties: {
|
|
1002
|
+
operation: {
|
|
1003
|
+
type: 'string',
|
|
1004
|
+
equals: 'less_than'
|
|
1005
|
+
},
|
|
1006
|
+
value: 'number'
|
|
1007
|
+
}
|
|
1008
|
+
})
|
|
1009
|
+
.addSchemaType('ContainsFilterCondition', {
|
|
1010
|
+
properties: {
|
|
1011
|
+
operation: {
|
|
1012
|
+
type: 'string',
|
|
1013
|
+
equals: 'contains'
|
|
1014
|
+
},
|
|
1015
|
+
value: [
|
|
1016
|
+
'string',
|
|
1017
|
+
'number',
|
|
1018
|
+
{
|
|
1019
|
+
type: 'array',
|
|
1020
|
+
ofType: ['string', 'number']
|
|
1021
|
+
}
|
|
1022
|
+
]
|
|
1023
|
+
}
|
|
1024
|
+
})
|
|
1025
|
+
.addSchemaType('LikeFilterCondition', {
|
|
1026
|
+
properties: {
|
|
1027
|
+
operation: 'like',
|
|
1028
|
+
value: 'string'
|
|
1029
|
+
}
|
|
1030
|
+
})
|
|
1031
|
+
.addSchemaType('BetweenFilterCondition', {
|
|
1032
|
+
properties: {
|
|
1033
|
+
operation: 'between',
|
|
1034
|
+
from: 'number',
|
|
1035
|
+
to: 'number'
|
|
1036
|
+
},
|
|
1037
|
+
validators: [
|
|
1038
|
+
(value) =>
|
|
1039
|
+
value.from <= value.to
|
|
1040
|
+
? { valid: true }
|
|
1041
|
+
: { valid: false, error: ['from must be <= to'] }
|
|
1042
|
+
]
|
|
1043
|
+
})
|
|
1044
|
+
.addSchemaType('StringFilterCondition', [
|
|
1045
|
+
'EqualsFilterCondition',
|
|
1046
|
+
'LikeFilterCondition'
|
|
1047
|
+
])
|
|
1048
|
+
.addSchemaType('AuthorFilter', {
|
|
1049
|
+
properties: {
|
|
1050
|
+
fullName: 'StringFilterCondition'
|
|
1051
|
+
}
|
|
1052
|
+
})
|
|
1053
|
+
.addSchemaType(
|
|
1054
|
+
'AuthorsReportSpecification',
|
|
1055
|
+
authorsReportSpecificationSchema as ObjectSchemaDefinitionParam<any>
|
|
1056
|
+
);
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* @type {import('smg-iq/editorial-analytics.reports').AuthorsReportSpecification}
|
|
1060
|
+
*/
|
|
1061
|
+
let reportSpec = {
|
|
1062
|
+
type: 'author',
|
|
1063
|
+
start: new Date(2021, 0, 1),
|
|
1064
|
+
end: new Date(),
|
|
1065
|
+
selectionStart: new Date(2022, 0, 1),
|
|
1066
|
+
selectionEnd: new Date(2022, 2, 1),
|
|
1067
|
+
granularity: 'day',
|
|
1068
|
+
metrics: [
|
|
1069
|
+
'articlesPublished',
|
|
1070
|
+
'searchReferrers',
|
|
1071
|
+
'socialReferrers',
|
|
1072
|
+
'views',
|
|
1073
|
+
'visitors',
|
|
1074
|
+
'newVisitors'
|
|
1075
|
+
],
|
|
1076
|
+
filters: {
|
|
1077
|
+
author: {
|
|
1078
|
+
fullName: {
|
|
1079
|
+
operation: 'between',
|
|
1080
|
+
value: 'some name'
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
let result = await validator.schemas.AuthorsReportSpecification.validate(
|
|
1087
|
+
reportSpec
|
|
1088
|
+
);
|
|
1089
|
+
|
|
1090
|
+
expect(result).toHaveProperty('valid', false);
|
|
1091
|
+
|
|
1092
|
+
reportSpec = {
|
|
1093
|
+
type: 'author',
|
|
1094
|
+
start: new Date(2021, 0, 1),
|
|
1095
|
+
end: new Date(),
|
|
1096
|
+
selectionStart: new Date(2022, 0, 1),
|
|
1097
|
+
selectionEnd: new Date(2022, 2, 1),
|
|
1098
|
+
granularity: 'day',
|
|
1099
|
+
metrics: [
|
|
1100
|
+
'articlesPublished',
|
|
1101
|
+
'searchReferrers',
|
|
1102
|
+
'socialReferrers',
|
|
1103
|
+
'views',
|
|
1104
|
+
'visitors',
|
|
1105
|
+
'newVisitors'
|
|
1106
|
+
],
|
|
1107
|
+
filters: {
|
|
1108
|
+
author: {
|
|
1109
|
+
fullName: {
|
|
1110
|
+
operation: 'equals',
|
|
1111
|
+
value: 'some name'
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
result = await validator.schemas.AuthorsReportSpecification.validate(
|
|
1118
|
+
reportSpec
|
|
1119
|
+
);
|
|
1120
|
+
|
|
1121
|
+
expect(result).toHaveProperty('valid', true);
|
|
1122
|
+
});
|
package/src/schemaValidator.ts
CHANGED
|
@@ -73,11 +73,11 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
73
73
|
|
|
74
74
|
public addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(
|
|
75
75
|
name: keyof K,
|
|
76
|
-
schema: L
|
|
76
|
+
schema: L | Array<Schema<any>>
|
|
77
77
|
): SchemaValidator<T & { [key in keyof K]: L }> {
|
|
78
78
|
if (typeof name !== 'string' || !name)
|
|
79
79
|
throw new Error('Name is required');
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
if (isDefaultType(name.toString()))
|
|
82
82
|
throw new Error(
|
|
83
83
|
`You can't add a schema named "${name}" because it's a name of a default schema, please consider another name to be used`
|
|
@@ -86,6 +86,14 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
86
86
|
throw new Error(`Schema "${name}" already exists`);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
if (Array.isArray(schema)) {
|
|
90
|
+
this._schemasMap.set(name.toString(), schema as Schema<any>);
|
|
91
|
+
this._schemasCache = null;
|
|
92
|
+
return this as any as SchemaValidator<T & { [key in keyof K]: L }>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (typeof schema !== 'object')
|
|
96
|
+
throw new Error('Array or Object is required');
|
|
89
97
|
this._schemasMap.set(name.toString(), {
|
|
90
98
|
...schema,
|
|
91
99
|
type: 'object'
|
|
@@ -194,6 +202,13 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
194
202
|
obj
|
|
195
203
|
);
|
|
196
204
|
} else if (typeof this.schemas[schema as keyof T] !== 'undefined') {
|
|
205
|
+
if (Array.isArray(this.schemas[schema as keyof T].schema)) {
|
|
206
|
+
return this.validate(
|
|
207
|
+
this.schemas[schema as keyof T]
|
|
208
|
+
.schema as any as Schema<any>,
|
|
209
|
+
obj
|
|
210
|
+
);
|
|
211
|
+
}
|
|
197
212
|
const objSchema = deepExtend(
|
|
198
213
|
defaultSchemas.object,
|
|
199
214
|
this.schemas[schema as keyof T].schema
|