@zohodesk/library-platform 1.2.2-exp.1 → 1.2.2-exp.2
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/es/cc/table-connected/SdkContract.js +3 -28
- package/es/cc/table-list/Properties.js +8 -9
- package/es/cc/table-list/row/Properties.js +6 -19
- package/es/library/custom-component/frameworks/json-schema-validator/Validator.js +1 -177
- package/es/library/custom-component/frameworks/ui/DependencyFactory.js +1 -17
- package/es/library/json-schema-validator/Validator.js +338 -0
- package/es/library/json-schema-validator/__tests__/Validator.test.js +753 -0
- package/es/library/json-schema-validator/index.js +1 -0
- package/es/platform/client-actions/cc/action-event-mediator/Properties.js +4 -18
- package/es/platform/sdk/application/interfaces/gateways/AbstractResource.js +1 -1
- package/package.json +3 -6
- package/es/library/custom-component/frameworks/json-schema-validator/__generated__/registry.d.js +0 -1
- package/es/library/custom-component/frameworks/json-schema-validator/__generated__/registry.js +0 -92
- package/es/library/custom-component/frameworks/json-schema-validator/__generated__/validators.js +0 -1
- package/es/library/custom-component/frameworks/json-schema-validator/__tests__/Validator.test.js +0 -478
|
@@ -1,30 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type: 'object',
|
|
4
|
-
properties: {
|
|
5
|
-
conditions: {
|
|
6
|
-
type: 'array',
|
|
7
|
-
items: {
|
|
8
|
-
type: 'object',
|
|
9
|
-
properties: {
|
|
10
|
-
fieldName: {
|
|
11
|
-
type: 'string'
|
|
12
|
-
},
|
|
13
|
-
condition: {
|
|
14
|
-
type: 'string',
|
|
15
|
-
enum: ['is empty', 'is not empty', 'is', "isn't", 'starts with', 'ends with', 'contains', "doesn't contain", '=', '<>', '<', '<=', '>', '>=', 'is after', 'is before', 'between', 'not between']
|
|
16
|
-
},
|
|
17
|
-
value: {}
|
|
18
|
-
},
|
|
19
|
-
required: ['fieldName', 'condition', 'value']
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
pattern: {
|
|
23
|
-
type: 'string'
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
required: ['conditions', 'pattern']
|
|
27
|
-
};
|
|
1
|
+
import { SortOrder } from "../../bc/zlist/Types";
|
|
2
|
+
import { filterConfigSchema } from "../../bc/zrecord/Schemas";
|
|
28
3
|
export const SdkContracts = {
|
|
29
4
|
getRecordInputMeta: {
|
|
30
5
|
type: 'object',
|
|
@@ -192,7 +167,7 @@ export const SdkContracts = {
|
|
|
192
167
|
},
|
|
193
168
|
order: {
|
|
194
169
|
type: 'string',
|
|
195
|
-
enum: [
|
|
170
|
+
enum: [SortOrder.ASC, SortOrder.DES, SortOrder.NONE]
|
|
196
171
|
}
|
|
197
172
|
}
|
|
198
173
|
},
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { Alignment, Width, SortedBy } from "./data-types/Header";
|
|
2
|
+
import RowCursor from "./data-types/RowCursor";
|
|
1
3
|
import SelectionConfigProperties from "../../bc/list-selection/Properties";
|
|
2
|
-
|
|
3
|
-
type: 'array',
|
|
4
|
-
minItems: 0
|
|
5
|
-
};
|
|
4
|
+
import { actionsSchema } from "../../platform/client-actions/cc/action-event-mediator/Properties";
|
|
6
5
|
const TableListProperties = {
|
|
7
6
|
isReOrderLoading: {
|
|
8
7
|
required: false,
|
|
@@ -48,7 +47,7 @@ const TableListProperties = {
|
|
|
48
47
|
properties: {
|
|
49
48
|
order: {
|
|
50
49
|
type: 'string',
|
|
51
|
-
enum:
|
|
50
|
+
enum: Object.values(SortedBy)
|
|
52
51
|
},
|
|
53
52
|
name: {
|
|
54
53
|
type: 'string'
|
|
@@ -111,7 +110,7 @@ const TableListProperties = {
|
|
|
111
110
|
required: false,
|
|
112
111
|
defaultValue: {
|
|
113
112
|
hasActions: false,
|
|
114
|
-
columnWidth:
|
|
113
|
+
columnWidth: Width.XSmall
|
|
115
114
|
},
|
|
116
115
|
typeMetadata: {
|
|
117
116
|
schema: {
|
|
@@ -143,7 +142,7 @@ const TableListProperties = {
|
|
|
143
142
|
typeMetadata: {
|
|
144
143
|
schema: {
|
|
145
144
|
type: 'string',
|
|
146
|
-
enum:
|
|
145
|
+
enum: Object.values(RowCursor)
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
},
|
|
@@ -176,11 +175,11 @@ const TableListProperties = {
|
|
|
176
175
|
},
|
|
177
176
|
alignment: {
|
|
178
177
|
type: 'string',
|
|
179
|
-
enum:
|
|
178
|
+
enum: Object.values(Alignment)
|
|
180
179
|
},
|
|
181
180
|
sortOrder: {
|
|
182
181
|
type: 'string',
|
|
183
|
-
enum:
|
|
182
|
+
enum: Object.values(SortedBy)
|
|
184
183
|
}
|
|
185
184
|
},
|
|
186
185
|
required: ['id', 'name', 'text']
|
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
minItems: 0,
|
|
5
|
-
items: {
|
|
6
|
-
type: 'object',
|
|
7
|
-
properties: {
|
|
8
|
-
targetEventName: {
|
|
9
|
-
type: 'string'
|
|
10
|
-
},
|
|
11
|
-
clientScript: {
|
|
12
|
-
type: 'string'
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
};
|
|
1
|
+
import { Width } from "../data-types/Header";
|
|
2
|
+
import RowCursor from "../data-types/RowCursor";
|
|
3
|
+
import { clientScriptsSchema } from "../../../platform/client-scripts/cc/zclient-scripts-execution/clientScriptsSchema";
|
|
17
4
|
const TableRowProperties = {
|
|
18
5
|
isReorderEnabled: {
|
|
19
6
|
required: false,
|
|
@@ -102,7 +89,7 @@ const TableRowProperties = {
|
|
|
102
89
|
typeMetadata: {
|
|
103
90
|
schema: {
|
|
104
91
|
type: 'string',
|
|
105
|
-
enum:
|
|
92
|
+
enum: Object.values(RowCursor)
|
|
106
93
|
}
|
|
107
94
|
}
|
|
108
95
|
},
|
|
@@ -135,11 +122,11 @@ const TableRowProperties = {
|
|
|
135
122
|
},
|
|
136
123
|
rowActionsColumnWidth: {
|
|
137
124
|
required: false,
|
|
138
|
-
defaultValue:
|
|
125
|
+
defaultValue: Width.XSmall,
|
|
139
126
|
typeMetadata: {
|
|
140
127
|
schema: {
|
|
141
128
|
type: 'number',
|
|
142
|
-
enum:
|
|
129
|
+
enum: Object.values(Width)
|
|
143
130
|
}
|
|
144
131
|
}
|
|
145
132
|
},
|
|
@@ -1,177 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// Map from schema object reference to precompiled validator function
|
|
3
|
-
const precompiledSchemaMap = new WeakMap(); // Precompiled registry: JSON-stringified schema → validator function
|
|
4
|
-
|
|
5
|
-
let precompiledByHash = null;
|
|
6
|
-
|
|
7
|
-
class Validator {
|
|
8
|
-
/**
|
|
9
|
-
* Register precompiled validators from the build-time generated registry.
|
|
10
|
-
* Builds a hash-based lookup so validate() can auto-match schemas.
|
|
11
|
-
* Called once at app startup.
|
|
12
|
-
*/
|
|
13
|
-
static registerPrecompiled(registry) {
|
|
14
|
-
precompiledByHash = new Map(); // Each precompiled validator has its schema embedded — extract and hash it
|
|
15
|
-
|
|
16
|
-
for (const [, validate] of Object.entries(registry)) {
|
|
17
|
-
if (typeof validate === 'function' && validate.schema) {
|
|
18
|
-
const hash = JSON.stringify(validate.schema);
|
|
19
|
-
precompiledByHash.set(hash, validate);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
static normalizeErrors(ajvErrors) {
|
|
25
|
-
return ajvErrors.map(e => ({ ...e,
|
|
26
|
-
dataPath: e.instancePath || e.dataPath || ''
|
|
27
|
-
}));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static validate(schema, value) {
|
|
31
|
-
if (schema.schema) {
|
|
32
|
-
return Validator.validatePropertySchema(schema, value);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let errors = [];
|
|
36
|
-
Validator.checkFunctionsRecursively(schema, value, '', '#', errors); // Try precompiled validator first (cached by schema reference)
|
|
37
|
-
|
|
38
|
-
let precompiled = precompiledSchemaMap.get(schema); // On first encounter, try to match against precompiled registry by schema hash
|
|
39
|
-
|
|
40
|
-
if (!precompiled && precompiledByHash) {
|
|
41
|
-
const ajvSchema = JSON.parse(JSON.stringify(schema, (key, val) => val === 'function' ? undefined : val));
|
|
42
|
-
const hash = JSON.stringify(ajvSchema);
|
|
43
|
-
precompiled = precompiledByHash.get(hash);
|
|
44
|
-
|
|
45
|
-
if (precompiled) {
|
|
46
|
-
// Cache for subsequent calls — O(1) lookup from now on
|
|
47
|
-
precompiledSchemaMap.set(schema, precompiled);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (precompiled) {
|
|
52
|
-
const isValid = precompiled(value);
|
|
53
|
-
|
|
54
|
-
if (!isValid && precompiled.errors) {
|
|
55
|
-
errors.push(...Validator.normalizeErrors(precompiled.errors));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
isValid: isValid && errors.length === 0,
|
|
60
|
-
errors,
|
|
61
|
-
warnings: []
|
|
62
|
-
};
|
|
63
|
-
} // No precompiled validator found — skip AJV validation, return function-check errors only
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (process.env.NODE_ENV === 'development') {
|
|
67
|
-
console.warn('[Validator] No precompiled validator found for schema. Skipping AJV validation.', schema);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
isValid: errors.length === 0,
|
|
72
|
-
errors,
|
|
73
|
-
warnings: []
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
static checkFunctionsRecursively(schema, value) {
|
|
78
|
-
let path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
|
|
79
|
-
let schemaPath = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '#';
|
|
80
|
-
let errors = arguments.length > 4 ? arguments[4] : undefined;
|
|
81
|
-
|
|
82
|
-
if (schema.type === 'function') {
|
|
83
|
-
if (typeof value !== 'function') {
|
|
84
|
-
errors.push({
|
|
85
|
-
keyword: 'type',
|
|
86
|
-
dataPath: path ? `.${path}` : '',
|
|
87
|
-
schemaPath: `${schemaPath}/type`,
|
|
88
|
-
params: {
|
|
89
|
-
type: 'function'
|
|
90
|
-
},
|
|
91
|
-
message: 'should be function'
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (schema.type === 'object' && schema.properties && typeof value === 'object' && value !== null) {
|
|
99
|
-
for (const key in schema.properties) {
|
|
100
|
-
Validator.checkFunctionsRecursively(schema.properties[key], value[key], path ? `${path}.${key}` : key, `${schemaPath}/properties/${key}`, errors);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (schema.type === 'array' && Array.isArray(value) && schema.items) {
|
|
105
|
-
value.forEach((item, index) => {
|
|
106
|
-
Validator.checkFunctionsRecursively(schema.items, item, `${path}[${index}]`, `${schemaPath}/items`, errors);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
static validatePropertySchema(propertySchema, value) {
|
|
112
|
-
const {
|
|
113
|
-
schema
|
|
114
|
-
} = propertySchema;
|
|
115
|
-
const result = Validator.validateRequired(propertySchema, value);
|
|
116
|
-
const isProvided = propertySchema.isValueProvided;
|
|
117
|
-
|
|
118
|
-
if (result.isValid === false) {
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (!isProvided) {
|
|
123
|
-
return {
|
|
124
|
-
isValid: true,
|
|
125
|
-
errors: [],
|
|
126
|
-
warnings: result.warnings
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const {
|
|
131
|
-
isValid,
|
|
132
|
-
errors,
|
|
133
|
-
warnings
|
|
134
|
-
} = Validator.validate(schema, value);
|
|
135
|
-
return {
|
|
136
|
-
key: propertySchema.name,
|
|
137
|
-
isValid,
|
|
138
|
-
errors,
|
|
139
|
-
warnings: [...result.warnings, ...warnings]
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
static validateRequired(propertySchema, value) {
|
|
144
|
-
const errors = [];
|
|
145
|
-
const warnings = [];
|
|
146
|
-
const isRequired = propertySchema.required === true;
|
|
147
|
-
const hasDefault = Object.prototype.hasOwnProperty.call(propertySchema, 'defaultValue');
|
|
148
|
-
const isProvided = propertySchema.isValueProvided;
|
|
149
|
-
|
|
150
|
-
if (!isProvided && isRequired) {
|
|
151
|
-
if (hasDefault) {
|
|
152
|
-
if (propertySchema.defaultValue === undefined) {
|
|
153
|
-
errors.push({
|
|
154
|
-
message: `is required but missing. So, default value is undefined.`
|
|
155
|
-
});
|
|
156
|
-
} else {
|
|
157
|
-
warnings.push({
|
|
158
|
-
message: `is required but missing. So, Default value will be used.`
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
} else {
|
|
162
|
-
errors.push({
|
|
163
|
-
message: `is required but missing. And, neither a value nor a default value is present.`
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
isValid: errors.length === 0,
|
|
170
|
-
warnings,
|
|
171
|
-
errors
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export default Validator;
|
|
1
|
+
export { default } from "../../../json-schema-validator/Validator";
|
|
@@ -2,30 +2,14 @@ import Presenter from "../../adapters/presenters/Presenter";
|
|
|
2
2
|
import Service from "../../adapters/gateways/service/Service";
|
|
3
3
|
import Controller from "../../adapters/controllers/Controller";
|
|
4
4
|
import Repository from "../../adapters/gateways/repository/Repository";
|
|
5
|
-
import Validator from "
|
|
5
|
+
import { Validator } from "../../../json-schema-validator";
|
|
6
6
|
import EventManager from "../../adapters/gateways/event-manager/EventManager";
|
|
7
|
-
// One-time registration of precompiled validators
|
|
8
|
-
let precompiledRegistered = false;
|
|
9
|
-
|
|
10
|
-
function ensurePrecompiledRegistered() {
|
|
11
|
-
if (precompiledRegistered) return;
|
|
12
|
-
precompiledRegistered = true;
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
|
-
const registry = require("../json-schema-validator/__generated__/registry");
|
|
17
|
-
|
|
18
|
-
Validator.registerPrecompiled(registry.default || registry);
|
|
19
|
-
} catch (e) {// __generated__ not available (e.g., first build) — runtime fallback
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
7
|
|
|
23
8
|
class DependencyFactory {
|
|
24
9
|
static create(_ref, input) {
|
|
25
10
|
let {
|
|
26
11
|
updateState
|
|
27
12
|
} = _ref;
|
|
28
|
-
ensurePrecompiledRegistered();
|
|
29
13
|
const repository = new Repository();
|
|
30
14
|
const presenter = new Presenter({
|
|
31
15
|
updateState
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/* eslint-disable complexity */
|
|
2
|
+
export class Validator {
|
|
3
|
+
static validate(schema, value) {
|
|
4
|
+
if (schema.schema) {
|
|
5
|
+
return Validator.validatePropertySchema(schema, value);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const errors = [];
|
|
9
|
+
Validator.validateNode(schema, value, '', errors, schema.definitions || schema.$defs || {});
|
|
10
|
+
return {
|
|
11
|
+
isValid: errors.length === 0,
|
|
12
|
+
errors,
|
|
13
|
+
warnings: []
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static validateNode(schema, value, dataPath, errors, rootDefinitions) {
|
|
18
|
+
if (!schema || typeof schema !== 'object') return; // undefined is not a JSON type — skip validation (matches AJV behavior)
|
|
19
|
+
|
|
20
|
+
if (value === undefined) return; // $ref resolution
|
|
21
|
+
|
|
22
|
+
if (schema.$ref) {
|
|
23
|
+
const resolved = Validator.resolveRef(schema.$ref, rootDefinitions);
|
|
24
|
+
|
|
25
|
+
if (resolved) {
|
|
26
|
+
Validator.validateNode(resolved, value, dataPath, errors, rootDefinitions);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return;
|
|
30
|
+
} // Hoist definitions from nested schema to root
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
const definitions = schema.definitions || schema.$defs ? { ...rootDefinitions,
|
|
34
|
+
...(schema.definitions || schema.$defs)
|
|
35
|
+
} : rootDefinitions; // anyOf
|
|
36
|
+
|
|
37
|
+
if (schema.anyOf) {
|
|
38
|
+
const anyOfValid = schema.anyOf.some(subSchema => {
|
|
39
|
+
const subErrors = [];
|
|
40
|
+
Validator.validateNode(subSchema, value, dataPath, subErrors, definitions);
|
|
41
|
+
return subErrors.length === 0;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!anyOfValid) {
|
|
45
|
+
errors.push({
|
|
46
|
+
keyword: 'anyOf',
|
|
47
|
+
dataPath,
|
|
48
|
+
message: 'should match at least one schema in anyOf'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return;
|
|
53
|
+
} // oneOf
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if (schema.oneOf) {
|
|
57
|
+
const matchCount = schema.oneOf.filter(subSchema => {
|
|
58
|
+
const subErrors = [];
|
|
59
|
+
Validator.validateNode(subSchema, value, dataPath, subErrors, definitions);
|
|
60
|
+
return subErrors.length === 0;
|
|
61
|
+
}).length;
|
|
62
|
+
|
|
63
|
+
if (matchCount !== 1) {
|
|
64
|
+
errors.push({
|
|
65
|
+
keyword: 'oneOf',
|
|
66
|
+
dataPath,
|
|
67
|
+
message: 'should match exactly one schema in oneOf'
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return;
|
|
72
|
+
} // not
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if (schema.not) {
|
|
76
|
+
const notErrors = [];
|
|
77
|
+
Validator.validateNode(schema.not, value, dataPath, notErrors, definitions);
|
|
78
|
+
|
|
79
|
+
if (notErrors.length === 0) {
|
|
80
|
+
errors.push({
|
|
81
|
+
keyword: 'not',
|
|
82
|
+
dataPath,
|
|
83
|
+
message: 'should NOT match schema in not'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return;
|
|
88
|
+
} // type validation
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if (schema.type) {
|
|
92
|
+
if (!Validator.checkType(schema.type, value)) {
|
|
93
|
+
const typeLabel = Array.isArray(schema.type) ? schema.type.join(',') : schema.type;
|
|
94
|
+
errors.push({
|
|
95
|
+
keyword: 'type',
|
|
96
|
+
dataPath,
|
|
97
|
+
message: `should be ${typeLabel}`
|
|
98
|
+
});
|
|
99
|
+
return; // Type mismatch — skip further checks
|
|
100
|
+
}
|
|
101
|
+
} // enum
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if (schema.enum) {
|
|
105
|
+
if (!schema.enum.includes(value)) {
|
|
106
|
+
errors.push({
|
|
107
|
+
keyword: 'enum',
|
|
108
|
+
dataPath,
|
|
109
|
+
message: `should be one of ${JSON.stringify(schema.enum)}`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
} // String constraints
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if (typeof value === 'string') {
|
|
116
|
+
if (schema.minLength !== undefined && value.length < schema.minLength) {
|
|
117
|
+
errors.push({
|
|
118
|
+
keyword: 'minLength',
|
|
119
|
+
dataPath,
|
|
120
|
+
message: `should have at least ${schema.minLength} characters`
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (schema.maxLength !== undefined && value.length > schema.maxLength) {
|
|
125
|
+
errors.push({
|
|
126
|
+
keyword: 'maxLength',
|
|
127
|
+
dataPath,
|
|
128
|
+
message: `should have at most ${schema.maxLength} characters`
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (schema.pattern !== undefined) {
|
|
133
|
+
const regex = new RegExp(schema.pattern);
|
|
134
|
+
|
|
135
|
+
if (!regex.test(value)) {
|
|
136
|
+
errors.push({
|
|
137
|
+
keyword: 'pattern',
|
|
138
|
+
dataPath,
|
|
139
|
+
message: `should match pattern "${schema.pattern}"`
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} // Array constraints
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
if (Array.isArray(value)) {
|
|
147
|
+
if (schema.minItems !== undefined && value.length < schema.minItems) {
|
|
148
|
+
errors.push({
|
|
149
|
+
keyword: 'minItems',
|
|
150
|
+
dataPath,
|
|
151
|
+
message: `should have at least ${schema.minItems} items`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (schema.uniqueItems === true) {
|
|
156
|
+
const seen = new Set();
|
|
157
|
+
const hasDuplicates = value.some(item => {
|
|
158
|
+
const key = typeof item === 'object' ? JSON.stringify(item) : item;
|
|
159
|
+
if (seen.has(key)) return true;
|
|
160
|
+
seen.add(key);
|
|
161
|
+
return false;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (hasDuplicates) {
|
|
165
|
+
errors.push({
|
|
166
|
+
keyword: 'uniqueItems',
|
|
167
|
+
dataPath,
|
|
168
|
+
message: 'should have unique items'
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (schema.items) {
|
|
174
|
+
value.forEach((item, index) => {
|
|
175
|
+
Validator.validateNode(schema.items, item, `${dataPath}[${index}]`, errors, definitions);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
} // Object constraints
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
182
|
+
if (schema.required) {
|
|
183
|
+
schema.required.forEach(key => {
|
|
184
|
+
if (!(key in value)) {
|
|
185
|
+
const propPath = dataPath ? `${dataPath}.${key}` : `.${key}`;
|
|
186
|
+
errors.push({
|
|
187
|
+
keyword: 'required',
|
|
188
|
+
dataPath: propPath,
|
|
189
|
+
message: `should have required property '${key}'`
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (schema.properties) {
|
|
196
|
+
for (const key in schema.properties) {
|
|
197
|
+
if (key in value) {
|
|
198
|
+
const propPath = dataPath ? `${dataPath}.${key}` : `.${key}`;
|
|
199
|
+
Validator.validateNode(schema.properties[key], value[key], propPath, errors, definitions);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (schema.additionalProperties !== undefined && schema.additionalProperties !== true && schema.properties) {
|
|
205
|
+
const allowed = new Set(Object.keys(schema.properties));
|
|
206
|
+
|
|
207
|
+
for (const key in value) {
|
|
208
|
+
if (!allowed.has(key)) {
|
|
209
|
+
if (schema.additionalProperties === false) {
|
|
210
|
+
const propPath = dataPath ? `${dataPath}.${key}` : `.${key}`;
|
|
211
|
+
errors.push({
|
|
212
|
+
keyword: 'additionalProperties',
|
|
213
|
+
dataPath: propPath,
|
|
214
|
+
message: `should NOT have additional property '${key}'`
|
|
215
|
+
});
|
|
216
|
+
} else if (typeof schema.additionalProperties === 'object') {
|
|
217
|
+
const propPath = dataPath ? `${dataPath}.${key}` : `.${key}`;
|
|
218
|
+
Validator.validateNode(schema.additionalProperties, value[key], propPath, errors, definitions);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static checkType(type, value) {
|
|
227
|
+
if (Array.isArray(type)) {
|
|
228
|
+
return type.some(t => Validator.checkSingleType(t, value));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return Validator.checkSingleType(type, value);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static checkSingleType(type, value) {
|
|
235
|
+
switch (type) {
|
|
236
|
+
case 'string':
|
|
237
|
+
return typeof value === 'string';
|
|
238
|
+
|
|
239
|
+
case 'number':
|
|
240
|
+
return typeof value === 'number';
|
|
241
|
+
|
|
242
|
+
case 'boolean':
|
|
243
|
+
return typeof value === 'boolean';
|
|
244
|
+
|
|
245
|
+
case 'object':
|
|
246
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
247
|
+
|
|
248
|
+
case 'array':
|
|
249
|
+
return Array.isArray(value);
|
|
250
|
+
|
|
251
|
+
case 'null':
|
|
252
|
+
return value === null;
|
|
253
|
+
|
|
254
|
+
case 'function':
|
|
255
|
+
return typeof value === 'function';
|
|
256
|
+
|
|
257
|
+
default:
|
|
258
|
+
return true;
|
|
259
|
+
// 'any' or unknown types — accept anything
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static resolveRef(ref, definitions) {
|
|
264
|
+
// Supports: #/definitions/Name and #/$defs/Name
|
|
265
|
+
const match = ref.match(/^#\/(definitions|\$defs)\/(.+)$/);
|
|
266
|
+
|
|
267
|
+
if (match && definitions[match[2]]) {
|
|
268
|
+
return definitions[match[2]];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
static validatePropertySchema(propertySchema, value) {
|
|
275
|
+
const {
|
|
276
|
+
schema
|
|
277
|
+
} = propertySchema;
|
|
278
|
+
const result = Validator.validateRequired(propertySchema, value);
|
|
279
|
+
const isProvided = propertySchema.isValueProvided;
|
|
280
|
+
|
|
281
|
+
if (result.isValid === false) {
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!isProvided) {
|
|
286
|
+
return {
|
|
287
|
+
isValid: true,
|
|
288
|
+
errors: [],
|
|
289
|
+
warnings: result.warnings
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const {
|
|
294
|
+
isValid,
|
|
295
|
+
errors,
|
|
296
|
+
warnings
|
|
297
|
+
} = Validator.validate(schema, value);
|
|
298
|
+
return {
|
|
299
|
+
key: propertySchema.name,
|
|
300
|
+
isValid,
|
|
301
|
+
errors,
|
|
302
|
+
warnings: [...result.warnings, ...warnings]
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static validateRequired(propertySchema, value) {
|
|
307
|
+
const errors = [];
|
|
308
|
+
const warnings = [];
|
|
309
|
+
const isRequired = propertySchema.required === true;
|
|
310
|
+
const hasDefault = Object.prototype.hasOwnProperty.call(propertySchema, 'defaultValue');
|
|
311
|
+
const isProvided = propertySchema.isValueProvided;
|
|
312
|
+
|
|
313
|
+
if (!isProvided && isRequired) {
|
|
314
|
+
if (hasDefault) {
|
|
315
|
+
if (propertySchema.defaultValue === undefined) {
|
|
316
|
+
errors.push({
|
|
317
|
+
message: `is required but missing. So, default value is undefined.`
|
|
318
|
+
});
|
|
319
|
+
} else {
|
|
320
|
+
warnings.push({
|
|
321
|
+
message: `is required but missing. So, Default value will be used.`
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
errors.push({
|
|
326
|
+
message: `is required but missing. And, neither a value nor a default value is present.`
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
isValid: errors.length === 0,
|
|
333
|
+
warnings,
|
|
334
|
+
errors
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
}
|