UNPKG

objection

Version:
168 lines (128 loc) 3.78 kB
import _ from 'lodash'; import Ajv from 'ajv'; import Model from './Model'; import Validator from './Validator'; import ValidationError from './ValidationError'; export default class AjvValidator extends Validator { constructor(conf) { super(); this.ajv = new Ajv(_.defaults({}, conf.options, { useDefaults: true })); this.ajvNoDefaults = new Ajv(_.assign({}, conf.options, { useDefaults: false })); this.cache = Object.create(null); conf.onCreateAjv(this.ajv); conf.onCreateAjv(this.ajvNoDefaults); } beforeValidate({model, json, options, ctx}) { ctx.jsonSchema = model.constructor.getJsonSchema(); if (model.$beforeValidate !== Model.prototype.$beforeValidate) { ctx.jsonSchema = _.cloneDeep(ctx.jsonSchema); ctx.jsonSchema = model.$beforeValidate(ctx.jsonSchema, json, options); } } validate({model, json, options, ctx}) { if (!ctx.jsonSchema) { return json; } const validator = this.getJsonSchemaValidator(model.constructor, ctx.jsonSchema, !!options.patch); if (!options.patch && this.setsDefaultValues(ctx.jsonSchema)) { json = _.cloneDeep(json); } validator(json); if (validator.errors) { throw parseValidationError(validator.errors); } return json; } getJsonSchemaValidator(ModelClass, jsonSchema, skipRequired) { const key = jsonSchema === ModelClass.getJsonSchema() ? 'default' : JSON.stringify(jsonSchema); let validators = this.cache[key]; if (!validators) { validators = {}; this.cache[key] = validators; } let validator = validators[skipRequired]; if (!validator) { validator = this.compileJsonSchemaValidator(jsonSchema, skipRequired); validators[skipRequired] = validator; } return validator; } compileJsonSchemaValidator(jsonSchema, skipRequired) { let origRequired; try { if (skipRequired) { origRequired = jsonSchema.required; jsonSchema.required = []; return this.ajvNoDefaults.compile(jsonSchema); } else { return this.ajv.compile(jsonSchema); } } finally { if (skipRequired) { jsonSchema.required = origRequired; } } } setsDefaultValues(jsonSchema) { return jsonSchema && jsonSchema.properties && hasDefaults(jsonSchema.properties); } } function parseValidationError(errors) { const errorHash = {}; let index = 0; for (let i = 0; i < errors.length; ++i) { let error = errors[i]; let key = error.dataPath.substring(1); if (!key) { let match = /should have required property '(.+)'/.exec(error.message); if (match && match.length > 1) { key = match[1]; } } if (!key && error.params && error.params.additionalProperty) { key = error.params.additionalProperty; } if (!key) { key = (index++).toString(); } errorHash[key] = [{ message: error.message, keyword: error.keyword, params: error.params }]; } return new ValidationError(errorHash); } function hasDefaults(obj) { return Array.isArray(obj) ? arrayHasDefaults(obj) : objectHasDefaults(obj); } function arrayHasDefaults(arr) { for (let i = 0, l = arr.length; i < l; ++i) { const val = arr[i]; if (val && typeof val === 'object' && hasDefaults(val)) { return true; } } return false; } function objectHasDefaults(obj) { const keys = Object.keys(obj); for (let i = 0, l = keys.length; i < l; ++i) { const key = keys[i]; if (key === 'default') { return true; } else { const val = obj[key]; if (val && typeof val === 'object' && hasDefaults(val)) { return true; } } } return false; }