blob: 53d288f3aedbf0a8657d124c2e33f0e3000dfc07 [file] [log] [blame]
AviZi280f8012017-06-09 02:39:56 +03001/*!
Michael Landoefa037d2017-02-19 12:57:33 +02002 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
AviZi280f8012017-06-09 02:39:56 +03003 *
Michael Landoefa037d2017-02-19 12:57:33 +02004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
AviZi280f8012017-06-09 02:39:56 +03007 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
Michael Landoefa037d2017-02-19 12:57:33 +020010 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
AviZi280f8012017-06-09 02:39:56 +030012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 * or implied. See the License for the specific language governing
14 * permissions and limitations under the License.
Michael Landoefa037d2017-02-19 12:57:33 +020015 */
Michael Landoefa037d2017-02-19 12:57:33 +020016// import Ajv from 'ajv';
17import cloneDeep from 'lodash/cloneDeep.js';
18import JSONPointer from './JSONPointer.js';
19
20export default class JSONSchema {
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020021 setSchema(schema) {
22 this._schema = schema;
23 this._fragmentsCache = new Map();
24 // this._ajv = new Ajv({
25 // useDefaults: true,
26 // coerceTypes: true
27 // });
28 // this._validate = this._ajv.compile(schema);
29 }
Michael Landoefa037d2017-02-19 12:57:33 +020030
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020031 processData(data) {
32 data = cloneDeep(data);
33 // this._validate(data);
34 return data;
35 }
Michael Landoefa037d2017-02-19 12:57:33 +020036
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020037 // array of names of validation functions
38 setSupportedValidationFunctions(supportedValidationFunctions) {
39 this._supportedValidationFunctions = supportedValidationFunctions;
40 }
Michael Landoefa037d2017-02-19 12:57:33 +020041
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020042 /* FYI - I was going to support "required" but then found out that server never sends it in its schema (it was a business decision. so leaving the code commented for now */
43 flattenSchema(supportedValidationFunctions) {
44 if (supportedValidationFunctions) {
45 this.setSupportedValidationFunctions(supportedValidationFunctions);
46 }
47 let genericFieldInfo = {};
48 if (this._schema && this._schema.properties) {
49 this.travelProperties(
50 this._schema.properties,
51 genericFieldInfo /*, this._schema.required*/
52 );
53 }
54 return { genericFieldInfo };
55 }
AviZi280f8012017-06-09 02:39:56 +030056
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020057 extractGenericFieldInfo(item) {
58 let validationsArr = [];
59 let additionalInfo = { isValid: true, errorText: '' };
60 for (let value in item) {
61 if (this._supportedValidationFunctions.includes(value)) {
62 let validationItem = this.extractValidations(item, value);
63 validationsArr[validationsArr.length] = validationItem;
64 } else {
65 let enumResult = this.extractEnum(item, value);
66 if (enumResult !== null) {
67 additionalInfo.enum = enumResult;
68 } else {
69 additionalInfo[value] = item[value];
70 }
71 /*if (required.includes (property)) {
AviZi280f8012017-06-09 02:39:56 +030072 additionalInfo[value].isRequired = true ;
73 }*/
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020074 }
75 }
AviZi280f8012017-06-09 02:39:56 +030076
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020077 additionalInfo.validations = validationsArr;
78 return additionalInfo;
79 }
AviZi280f8012017-06-09 02:39:56 +030080
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020081 extractValidations(item, value) {
82 let validationItem;
83 let data = item[value];
84 if (value === 'maximum') {
85 if (item.exclusiveMaximum) {
86 value = 'maximumExclusive';
87 }
88 }
89 if (value === 'minimum') {
90 if (item.exclusiveMinimum) {
91 value = 'minimumExclusive';
92 }
93 }
94 validationItem = { type: value, data: data };
95 return validationItem;
96 }
AviZi280f8012017-06-09 02:39:56 +030097
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020098 extractEnum(item, value) {
99 let enumResult = null;
100 if (value === 'type' && item[value] === 'array') {
101 let items = item.items;
102 if (items && items.enum && items.enum.length > 0) {
103 let values = items.enum
104 .filter(value => value)
105 .map(value => ({ enum: value, title: value }));
106 enumResult = values;
107 }
108 } else if (value === 'enum') {
109 let items = item[value];
110 if (items && items.length > 0) {
111 let values = items
112 .filter(value => value)
113 .map(value => ({ enum: value, title: value }));
114 enumResult = values;
115 }
116 }
117 return enumResult;
118 }
AviZi280f8012017-06-09 02:39:56 +0300119
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200120 travelProperties(
121 properties,
122 genericFieldDefs,
123 /*required = [],*/ pointer = ''
124 ) {
125 let newPointer = pointer;
126 for (let property in properties) {
127 newPointer = newPointer ? newPointer + '/' + property : property;
128 if (properties[property].properties) {
129 this.travelProperties(
130 properties[property].properties,
131 genericFieldDefs /*, properties[property].required*/,
132 newPointer
133 );
134 } else if (properties[property].$ref) {
135 let fragment = this._getSchemaFragmentByRef(
136 properties[property].$ref
137 );
138 if (fragment.properties) {
139 this.travelProperties(
140 fragment.properties,
141 genericFieldDefs /*, properties[property].required*/,
142 newPointer
143 );
144 } else {
145 genericFieldDefs[newPointer] = this.extractGenericFieldInfo(
146 fragment.properties
147 );
148 }
149 } else {
150 genericFieldDefs[newPointer] = this.extractGenericFieldInfo(
151 properties[property]
152 );
153 }
154 newPointer = pointer;
155 }
156 }
AviZi280f8012017-06-09 02:39:56 +0300157
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200158 getTitle(pointer) {
159 return this._getSchemaFragment(pointer).title;
160 }
Michael Landoefa037d2017-02-19 12:57:33 +0200161
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200162 exists(pointer) {
163 const fragment = this._getSchemaFragment(pointer);
164 return !!fragment;
165 }
Michael Landoefa037d2017-02-19 12:57:33 +0200166
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200167 getDefault(pointer) {
168 const fragment = this._getSchemaFragment(pointer);
169 return fragment && fragment.default;
170 }
Michael Landoefa037d2017-02-19 12:57:33 +0200171
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200172 getEnum(pointer) {
173 const fragment = this._getSchemaFragment(pointer);
174 return (
175 fragment &&
176 (fragment.type === 'array' ? fragment.items.enum : fragment.enum)
177 );
178 }
Michael Landoefa037d2017-02-19 12:57:33 +0200179
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200180 isRequired(pointer) {
181 const parentPointer = JSONPointer.extractParentPointer(pointer);
182 const lastPart = JSONPointer.extractLastPart(pointer);
183 let parentFragment = this._getSchemaFragment(parentPointer);
184 return (
185 parentFragment &&
186 parentFragment.required &&
187 parentFragment.required.includes(lastPart)
188 );
189 }
Michael Landoefa037d2017-02-19 12:57:33 +0200190
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200191 isNumber(pointer) {
192 const fragment = this._getSchemaFragment(pointer);
193 return fragment && fragment.type === 'number';
194 }
Michael Landoefa037d2017-02-19 12:57:33 +0200195
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200196 getMaxValue(pointer) {
197 const fragment = this._getSchemaFragment(pointer);
198 return fragment && fragment.exclusiveMaximum
199 ? fragment.maximum - 1
200 : fragment.maximum;
201 }
Michael Landoefa037d2017-02-19 12:57:33 +0200202
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200203 getMinValue(pointer) {
204 const fragment = this._getSchemaFragment(pointer);
205 return fragment && fragment.exclusiveMinimum
206 ? fragment.minimum
207 : fragment.minimum;
208 }
Michael Landoefa037d2017-02-19 12:57:33 +0200209
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200210 isString(pointer) {
211 const fragment = this._getSchemaFragment(pointer);
212 return fragment && fragment.type === 'string';
213 }
Michael Landoefa037d2017-02-19 12:57:33 +0200214
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200215 getPattern(pointer) {
216 const fragment = this._getSchemaFragment(pointer);
217 return fragment && fragment.pattern;
218 }
Michael Landoefa037d2017-02-19 12:57:33 +0200219
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200220 getMaxLength(pointer) {
221 const fragment = this._getSchemaFragment(pointer);
222 return fragment && fragment.maxLength;
223 }
Michael Landoefa037d2017-02-19 12:57:33 +0200224
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200225 getMinLength(pointer) {
226 const fragment = this._getSchemaFragment(pointer);
227 return fragment && fragment.minLength;
228 }
Michael Landoefa037d2017-02-19 12:57:33 +0200229
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200230 isArray(pointer) {
231 const fragment = this._getSchemaFragment(pointer);
232 return fragment && fragment.type === 'array';
233 }
Michael Landoefa037d2017-02-19 12:57:33 +0200234
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200235 _getSchemaFragment(pointer) {
236 if (this._fragmentsCache.has(pointer)) {
237 return this._fragmentsCache.get(pointer);
238 }
Michael Landoefa037d2017-02-19 12:57:33 +0200239
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200240 let parts = JSONPointer.extractParts(pointer);
Michael Landoefa037d2017-02-19 12:57:33 +0200241
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200242 let fragment = parts.reduce((fragment, part) => {
243 if (fragment === undefined) {
244 return undefined;
245 }
Michael Landoefa037d2017-02-19 12:57:33 +0200246
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200247 if (fragment.$ref) {
248 fragment = this._getSchemaFragmentByRef(fragment.$ref);
249 }
Michael Landoefa037d2017-02-19 12:57:33 +0200250
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200251 switch (fragment.type) {
252 case 'object':
253 return fragment.properties && fragment.properties[part];
Michael Landoefa037d2017-02-19 12:57:33 +0200254
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200255 case 'array':
256 return fragment.enum && fragment.enum[part];
Michael Landoefa037d2017-02-19 12:57:33 +0200257
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200258 default:
259 // throw new Error(`Incorrect/unsupported JSONPointer "${pointer}" from "${part}"`);
260 return undefined;
261 }
262 }, this._schema);
Michael Landoefa037d2017-02-19 12:57:33 +0200263
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200264 while (fragment && fragment.$ref) {
265 fragment = this._getSchemaFragmentByRef(fragment.$ref);
266 }
Michael Landoefa037d2017-02-19 12:57:33 +0200267
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200268 this._fragmentsCache.set(pointer, fragment);
269 return fragment;
270 }
Michael Landoefa037d2017-02-19 12:57:33 +0200271
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200272 _getSchemaFragmentByRef($ref) {
273 let pointer = $ref.substr(1);
274 return JSONPointer.getValue(this._schema, pointer);
275 // let fragmentAjv = new Ajv();
276 // fragmentAjv.addSchema(this._schema);
277 // let compiledFragment = fragmentAjv.compile({$ref});
278 // let fragment = compiledFragment.refVal[compiledFragment.refs[$ref]];
279 // return fragment;
280 }
281}