1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 |
|
7 | var _util = require('util');
|
8 |
|
9 | var _util2 = _interopRequireDefault(_util);
|
10 |
|
11 | var _debug = require('debug');
|
12 |
|
13 | var _debug2 = _interopRequireDefault(_debug);
|
14 |
|
15 | var _Data = require('../Data');
|
16 |
|
17 | var _Data2 = _interopRequireDefault(_Data);
|
18 |
|
19 | var _Error = require('../Error');
|
20 |
|
21 | var _Error2 = _interopRequireDefault(_Error);
|
22 |
|
23 | var _Reference = require('../Reference');
|
24 |
|
25 | var _Reference2 = _interopRequireDefault(_Reference);
|
26 |
|
27 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
28 |
|
29 | class Schema {
|
30 |
|
31 | constructor(base) {
|
32 | if (base) this.base = base;
|
33 | this.debug = (0, _debug2.default)(`validator:${this.constructor.name.replace(/Schema/, '').toLowerCase()}`);
|
34 | this._title = this.constructor.name.replace(/(.)Schema/, '$1');
|
35 | this._detail = 'should be defined with';
|
36 | this._rules = {
|
37 | descriptor: [],
|
38 | check: [],
|
39 | validator: []
|
40 | };
|
41 | this._setting = {};
|
42 |
|
43 | this._rules.descriptor.push(this._emptyDescriptor, this._optionalDescriptor, this._rawDescriptor);
|
44 | this._rules.validator.push(this._emptyValidator, this._optionalValidator, this._rawValidator);
|
45 | }
|
46 |
|
47 | inspect(depth, options) {
|
48 | const newOptions = Object.assign({}, options, {
|
49 | depth: options.depth === null ? null : options.depth - 1
|
50 | });
|
51 | const base = this.base ? `base=${_util2.default.inspect(this.base)} ` : '';
|
52 | const padding = ' '.repeat(5);
|
53 | const inner = _util2.default.inspect(this._setting, newOptions).replace(/\n/g, `\n${padding}`);
|
54 | return `${options.stylize(this.constructor.name, 'class')} ${base}${inner} `;
|
55 | }
|
56 |
|
57 | title(title) {
|
58 | this._title = title;
|
59 | return this;
|
60 | }
|
61 | detail(detail) {
|
62 | this._detail = detail;
|
63 | return this;
|
64 | }
|
65 | schema(path) {
|
66 | let obj = this;
|
67 | var _iteratorNormalCompletion = true;
|
68 | var _didIteratorError = false;
|
69 | var _iteratorError = undefined;
|
70 |
|
71 | try {
|
72 | for (var _iterator = path.split(/\//)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
73 | const key = _step.value;
|
74 |
|
75 | if (obj instanceof Schema) obj = obj._setting[key];else if (obj instanceof Map) obj = obj.get(key);else obj = obj[key];
|
76 | }
|
77 | } catch (err) {
|
78 | _didIteratorError = true;
|
79 | _iteratorError = err;
|
80 | } finally {
|
81 | try {
|
82 | if (!_iteratorNormalCompletion && _iterator.return) {
|
83 | _iterator.return();
|
84 | }
|
85 | } finally {
|
86 | if (_didIteratorError) {
|
87 | throw _iteratorError;
|
88 | }
|
89 | }
|
90 | }
|
91 |
|
92 | if (!(obj instanceof Schema)) throw new Error(`No schema element under ${path} found`);
|
93 | return obj;
|
94 | }
|
95 |
|
96 | _setError(name, msg) {
|
97 | throw new Error(msg || `In ${this.constructor.name} you are not allowed to set
|
98 | ${name} manually`);
|
99 | }
|
100 | _setFlag(name, flag = true) {
|
101 | if (flag) this._setting[name] = flag;else delete this._setting[name];
|
102 | return this;
|
103 | }
|
104 | _setAny(name, value) {
|
105 | if (value) this._setting[name] = value;else delete this._setting[name];
|
106 | return this;
|
107 | }
|
108 | _isReference(name) {
|
109 | return this._setting[name] instanceof _Reference2.default;
|
110 | }
|
111 | _checkBoolean(name) {
|
112 | let value;
|
113 | switch (typeof this._check[name]) {
|
114 | case 'undefined':
|
115 | case 'boolean':
|
116 | break;
|
117 | case 'string':
|
118 | value = this._check[name].toLowerCase();
|
119 | if (value === undefined) this._check[name] = false;else if (Array.isArray(value)) this._check[name] = value.length > 0;else if (typeof value === 'object') this._check[name] = Object.keys(value).length > 0;else if (['yes', 1, '1', 'true', 't', '+'].includes(value)) this._check[name] = true;else if (['no', 0, '0', 'false', 'f', '', '-'].includes(value)) this._check[name] = false;
|
120 | break;
|
121 | default:
|
122 | throw new Error(`No boolean value for \`${name}\` setting given in \
|
123 | ${this._setting[name] && this._setting[name].description || this._setting[name]}`);
|
124 | }
|
125 | }
|
126 | _checkString(name) {
|
127 | const check = this._check;
|
128 | if (check[name] && typeof check[name] !== 'string') {
|
129 | throw new Error(`No string value for \`${name}\` setting given in \
|
130 | ${this._setting[name] && this._setting[name].description || this._setting[name]}`);
|
131 | }
|
132 | }
|
133 | _checkArrayString(name) {
|
134 | this._checkArray(name);
|
135 | const check = this._check;
|
136 | if (check[name]) {
|
137 | check[name].forEach(e => {
|
138 | if (typeof e !== 'string') {
|
139 | throw new Error(`No string value for \`${name}\` setting given in \
|
140 | ${this._setting[name] && this._setting[name].description || this._setting[name]}`);
|
141 | }
|
142 | });
|
143 | }
|
144 | }
|
145 | _checkNumber(name) {
|
146 | const check = this._check;
|
147 | if (check[name] && typeof check[name] !== 'number') {
|
148 | throw new Error(`No numerical value for \`${name}\` setting given in \
|
149 | ${this._setting[name] && this._setting[name].description || this._setting[name]}`);
|
150 | }
|
151 | }
|
152 | _checkArray(name) {
|
153 | const check = this._check;
|
154 | if (check[name]) {
|
155 | if (check[name] instanceof Set) check[name] = Array.from(check[name]);else if (!Array.isArray(check[name])) {
|
156 | if (typeof check[name] === 'object') check[name] = Object.keys(check[name]);else check[name] = [check[name]];
|
157 | }
|
158 | }
|
159 | }
|
160 | _checkObject(name) {
|
161 | const check = this._check;
|
162 | if (!check[name]) check[name] = {};else if (typeof check[name] !== 'object') {
|
163 | throw new Error(`No object for \`${name}\` setting given in \
|
164 | ${this._setting[name] && this._setting[name].description || this._setting[name]}`);
|
165 | }
|
166 | }
|
167 | _checkMatch(name) {
|
168 | const check = this._check;
|
169 | if (check[name] === undefined) return;
|
170 | if (check[name] instanceof RegExp) return;
|
171 | const e = check[name].toString();
|
172 | if (e.match(/^\/([^\\/]|\\.)+\/[gi]*$/)) {
|
173 | const parts = e.match(/([^\\/]|\\.)+/g);
|
174 | if (parts.length < 1 || parts.length > 2) {
|
175 | throw new Error(`Could not convert ${_util2.default.inspect(check[name])} to regular expression`);
|
176 | }
|
177 | check[name] = new RegExp(parts[0], parts[1]);
|
178 | }
|
179 | check[name] = check[name].toString();
|
180 | }
|
181 | _checkArrayMatch(name) {
|
182 | this._checkArray(name);
|
183 | const check = this._check;
|
184 | if (check[name] === undefined) return;
|
185 | check[name] = check[name].map(e => {
|
186 | if (e instanceof RegExp) return e;
|
187 | const el = e.toString();
|
188 | if (el.match(/^\/([^\\/]|\\.)+\/[gi]*$/)) {
|
189 | const parts = el.match(/([^\\/]|\\.)+/g);
|
190 | if (parts.length < 1 || parts.length > 2) {
|
191 | throw new Error(`Could not convert ${_util2.default.inspect(e)} to regular expression`);
|
192 | }
|
193 | return new RegExp(parts[0], parts[1]);
|
194 | }
|
195 | return el;
|
196 | });
|
197 | }
|
198 |
|
199 | stripEmpty(flag) {
|
200 | return this._setFlag('stripEmpty', flag);
|
201 | }
|
202 |
|
203 | _emptyDescriptor() {
|
204 | const set = this._setting;
|
205 | if (set.stripEmpty instanceof _Reference2.default) {
|
206 | return `Empty values are set to \`undefined\` depending on ${set.stripEmpty.description}.\n`;
|
207 | }
|
208 | return set.stripEmpty ? 'Empty values are set to `undefined`.\n' : '';
|
209 | }
|
210 |
|
211 | _emptyValidator(data) {
|
212 | const check = this._check;
|
213 | try {
|
214 | this._checkBoolean('stripEmpty');
|
215 | } catch (err) {
|
216 | return Promise.reject(new _Error2.default(this, data, err.message));
|
217 | }
|
218 | if (check.stripEmpty && (data.value === '' || data.value === null || Array.isArray(data.value) && !data.value.length || Object.keys(data.value).length === 0 && data.value.constructor === Object)) {
|
219 | data.value = undefined;
|
220 | }
|
221 | return Promise.resolve();
|
222 | }
|
223 |
|
224 | required(flag) {
|
225 | const set = this._setting;
|
226 | if (set.forbidden && !this._isReference('forbidden')) {
|
227 | throw new Error('This is already `forbidden` and can´t be also be `required`');
|
228 | }
|
229 | return this._setFlag('required', flag);
|
230 | }
|
231 | forbidden(flag) {
|
232 | const set = this._setting;
|
233 | if (set.required && !this._isReference('required')) {
|
234 | throw new Error('This is already `required` and can´t be also be `forbidden`');
|
235 | }
|
236 | return this._setFlag('forbidden', flag);
|
237 | }
|
238 | default(value) {
|
239 | return this._setAny('default', value);
|
240 | }
|
241 |
|
242 | _optionalDescriptor() {
|
243 | const set = this._setting;
|
244 | let msg = '';
|
245 | if (set.default) {
|
246 | const value = set.default instanceof _Reference2.default ? set.default.description : _util2.default.inspect(set.default);
|
247 | msg += `It will default to ${value} if not set. `;
|
248 | }
|
249 | if (set.required) {
|
250 | if (set.required instanceof _Reference2.default) {
|
251 | msg += `It is required depending on ${set.required.description}. `;
|
252 | } else msg += 'It is required and has to be set with a value. ';
|
253 | }
|
254 | if (set.forbidden) {
|
255 | if (set.forbidden instanceof _Reference2.default) {
|
256 | msg += `It is forbidden depending on ${set.forbidden.description}. `;
|
257 | } else msg += 'It is forbidden and could not contain a value. ';
|
258 | }
|
259 | return msg.replace(/ $/, '\n');
|
260 | }
|
261 |
|
262 | _optionalValidator(data) {
|
263 | const check = this._check;
|
264 | try {
|
265 | this._checkBoolean('required');
|
266 | this._checkBoolean('forbidden');
|
267 | } catch (err) {
|
268 | return Promise.reject(new _Error2.default(this, data, err.message));
|
269 | }
|
270 |
|
271 | if (data.value === undefined && check.default) data.value = check.default;
|
272 |
|
273 | if (check.required && data.value === undefined) {
|
274 | return Promise.reject(new _Error2.default(this, data, 'This element is mandatory!'));
|
275 | }
|
276 | if (check.forbidden && data.value !== undefined) {
|
277 | return Promise.reject(new _Error2.default(this, data, 'This element is forbidden!'));
|
278 | }
|
279 | if (data.value === undefined) return Promise.reject();
|
280 | return Promise.resolve();
|
281 | }
|
282 |
|
283 | raw(flag) {
|
284 | return this._setFlag('raw', flag);
|
285 | }
|
286 |
|
287 | _rawDescriptor() {
|
288 | const set = this._setting;
|
289 | if (set.raw instanceof _Reference2.default) {
|
290 | return `The original value is used depending on ${set.raw.description}.\n`;
|
291 | }
|
292 | return set.raw ? 'After validation the original value is used.\n' : '';
|
293 | }
|
294 |
|
295 | _rawValidator(data) {
|
296 | const check = this._check;
|
297 | try {
|
298 | this._checkBoolean('raw');
|
299 | } catch (err) {
|
300 | return Promise.reject(new _Error2.default(this, data, err.message));
|
301 | }
|
302 | if (check.raw) data.value = data.orig;
|
303 | return Promise.resolve();
|
304 | }
|
305 |
|
306 | get clone() {
|
307 | return Object.assign(Object.create(this), this);
|
308 | }
|
309 |
|
310 | get description() {
|
311 | let msg = '';
|
312 |
|
313 | if (this.base) {
|
314 | msg += `Use as base: ${_util2.default.inspect(this.base instanceof _Data2.default ? this.base.value : this.base).trim()}. `;
|
315 | }
|
316 |
|
317 | this._rules.descriptor.forEach(rule => {
|
318 | if (rule) msg += rule.call(this);
|
319 | });
|
320 | return msg.trim();
|
321 | }
|
322 |
|
323 | _validate(value, source, options) {
|
324 | if (this.debug.enabled) {
|
325 | this.debug(_util2.default.inspect(value));
|
326 | this.debug(` ${_util2.default.inspect(this)}`);
|
327 | }
|
328 | const data = value instanceof _Data2.default ? value : new _Data2.default(value, source, options);
|
329 | if (this.base) {
|
330 | data.value = this.base instanceof _Data2.default ? this.base.value : this.base;
|
331 | }
|
332 | let p = Promise.resolve();
|
333 |
|
334 | if (data.value instanceof _Reference2.default) {
|
335 | p = p.then(() => data.value.raw().resolve(data)).then(res => {
|
336 | data.value = res;
|
337 | if (this.debug.enabled) this.debug(` Use base: ${_util2.default.inspect(data)}`);
|
338 | });
|
339 | }
|
340 |
|
341 | const par = [];
|
342 | this._check = {};
|
343 | const set = this._setting;
|
344 | var _iteratorNormalCompletion2 = true;
|
345 | var _didIteratorError2 = false;
|
346 | var _iteratorError2 = undefined;
|
347 |
|
348 | try {
|
349 | for (var _iterator2 = Object.keys(set)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
350 | const key = _step2.value;
|
351 |
|
352 | let raw = set[key];
|
353 | if (raw instanceof Set) raw = Array.from(raw);
|
354 | if (raw instanceof _Reference2.default) {
|
355 | par.push(raw.resolve(data).then(res => {
|
356 | this._check[key] = res;
|
357 | }));
|
358 | } else if (Array.isArray(raw)) {
|
359 | this._check[key] = [];
|
360 | var _iteratorNormalCompletion3 = true;
|
361 | var _didIteratorError3 = false;
|
362 | var _iteratorError3 = undefined;
|
363 |
|
364 | try {
|
365 | for (var _iterator3 = raw.keys()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
366 | const i = _step3.value;
|
367 |
|
368 | const e = raw[i];
|
369 | if (e instanceof _Reference2.default) {
|
370 | this._check[key][i] = null;
|
371 | par.push(e.resolve(data).then(res => {
|
372 | this._check[key][i] = res;
|
373 | }));
|
374 | } else if (Array.isArray(e)) {
|
375 | this._check[key][i] = [];
|
376 | var _iteratorNormalCompletion4 = true;
|
377 | var _didIteratorError4 = false;
|
378 | var _iteratorError4 = undefined;
|
379 |
|
380 | try {
|
381 | for (var _iterator4 = e.keys()[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
382 | const j = _step4.value;
|
383 |
|
384 | const sub = e[j];
|
385 | if (sub instanceof _Reference2.default) {
|
386 | this._check[key][i][j] = null;
|
387 | par.push(sub.resolve(data).then(res => {
|
388 | this._check[key][i][j] = res;
|
389 | }));
|
390 | } else this._check[key][i][j] = sub;
|
391 | }
|
392 | } catch (err) {
|
393 | _didIteratorError4 = true;
|
394 | _iteratorError4 = err;
|
395 | } finally {
|
396 | try {
|
397 | if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
398 | _iterator4.return();
|
399 | }
|
400 | } finally {
|
401 | if (_didIteratorError4) {
|
402 | throw _iteratorError4;
|
403 | }
|
404 | }
|
405 | }
|
406 | } else this._check[key].push(e);
|
407 | }
|
408 | } catch (err) {
|
409 | _didIteratorError3 = true;
|
410 | _iteratorError3 = err;
|
411 | } finally {
|
412 | try {
|
413 | if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
414 | _iterator3.return();
|
415 | }
|
416 | } finally {
|
417 | if (_didIteratorError3) {
|
418 | throw _iteratorError3;
|
419 | }
|
420 | }
|
421 | }
|
422 | } else if (raw instanceof Map) {
|
423 | this._check[key] = {};
|
424 | var _iteratorNormalCompletion5 = true;
|
425 | var _didIteratorError5 = false;
|
426 | var _iteratorError5 = undefined;
|
427 |
|
428 | try {
|
429 | for (var _iterator5 = raw.keys()[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
430 | const k = _step5.value;
|
431 |
|
432 | const e = raw.get(k);
|
433 | if (e instanceof _Reference2.default) {
|
434 | this._check[key][k] = null;
|
435 | par.push(e.resolve(data).then(res => {
|
436 | this._check[key][k] = res;
|
437 | }));
|
438 | } else this._check[key][k] = e;
|
439 | }
|
440 | } catch (err) {
|
441 | _didIteratorError5 = true;
|
442 | _iteratorError5 = err;
|
443 | } finally {
|
444 | try {
|
445 | if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
446 | _iterator5.return();
|
447 | }
|
448 | } finally {
|
449 | if (_didIteratorError5) {
|
450 | throw _iteratorError5;
|
451 | }
|
452 | }
|
453 | }
|
454 | } else this._check[key] = raw;
|
455 | }
|
456 | } catch (err) {
|
457 | _didIteratorError2 = true;
|
458 | _iteratorError2 = err;
|
459 | } finally {
|
460 | try {
|
461 | if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
462 | _iterator2.return();
|
463 | }
|
464 | } finally {
|
465 | if (_didIteratorError2) {
|
466 | throw _iteratorError2;
|
467 | }
|
468 | }
|
469 | }
|
470 |
|
471 | p = p.then(() => Promise.all(par));
|
472 |
|
473 | this._rules.validator.forEach(rule => {
|
474 | p = p.then(() => rule.call(this, data));
|
475 | });
|
476 |
|
477 | return p.then(() => {
|
478 | if (this.debug.enabled) this.debug(`=> ${_util2.default.inspect(data)}`);
|
479 | data.done(data.value);
|
480 | return data;
|
481 | }).catch(err => {
|
482 | if (this.debug.enabled) {
|
483 | if (err) this.debug(`=> ${_util2.default.inspect(err)}`);else this.debug(`=> ${_util2.default.inspect(data)}`);
|
484 | }
|
485 | return err ? Promise.reject(err) : data;
|
486 | });
|
487 | }
|
488 |
|
489 | validate(value, source, options) {
|
490 | return this._validate(value, source, options).then(data => data.value);
|
491 | }
|
492 | }
|
493 |
|
494 | exports.default = Schema; |
\ | No newline at end of file |