pax_global_header00006660000000000000000000000064134032764460014523gustar00rootroot0000000000000052 comment=5d134394acb6429840438cc262c36c76d91e356c ajv-errors-1.0.1/000077500000000000000000000000001340327644600136145ustar00rootroot00000000000000ajv-errors-1.0.1/.eslintrc.yml000066400000000000000000000012711340327644600162410ustar00rootroot00000000000000env: node: true extends: 'eslint:recommended' rules: indent: [ 2, 2, { SwitchCase: 1 } ] no-trailing-spaces: 2 quotes: [ 2, single, avoid-escape ] linebreak-style: [ 2, unix ] semi: [ 2, always ] valid-jsdoc: [ 2, { requireReturn: false } ] no-invalid-this: 2 no-unused-vars: [ 2, { args: none } ] no-console: [ 2, { allow: [ warn, error ] } ] block-scoped-var: 2 complexity: [ 2, 8 ] curly: [ 2, multi-or-nest, consistent ] dot-location: [ 2, property ] dot-notation: 2 no-else-return: 2 no-eq-null: 2 no-fallthrough: 2 no-return-assign: 2 strict: [ 2, global ] no-shadow: 1 no-use-before-define: [ 2, nofunc ] callback-return: 2 no-path-concat: 2 ajv-errors-1.0.1/.gitignore000066400000000000000000000011621340327644600156040ustar00rootroot00000000000000# Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules jspm_packages # Optional npm cache directory .npm # Optional REPL history .node_repl_history # Compiled templates lib/dotjs/*.js .DS_Store ajv-errors-1.0.1/.travis.yml000066400000000000000000000001551340327644600157260ustar00rootroot00000000000000language: node_js node_js: - "6" - "8" - "9" - "10" after_script: - coveralls < coverage/lcov.info ajv-errors-1.0.1/LICENSE000066400000000000000000000020621340327644600146210ustar00rootroot00000000000000MIT License Copyright (c) 2017 Evgeny Poberezkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ajv-errors-1.0.1/README.md000066400000000000000000000175511340327644600151040ustar00rootroot00000000000000# ajv-errors Custom error messages in JSON-Schema for Ajv validator [![Build Status](https://travis-ci.org/epoberezkin/ajv-errors.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv-errors) [![npm version](https://badge.fury.io/js/ajv-errors.svg)](http://badge.fury.io/js/ajv-errors) [![Coverage Status](https://coveralls.io/repos/github/epoberezkin/ajv-errors/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/ajv-errors?branch=master) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) ## Contents - [Install](#install) - [Usage](#usage) - [Single message](#single-message) - [Messages for keywords](#messages-for-keywords) - [Messages for properties and items](#messages-for-properties-and-items) - [Default message](#default-message) - [Templates](#templates) - [Options](#options) - [License](#license) ## Install ``` npm install ajv-errors ``` ## Usage Add the keyword `errorMessages` to Ajv instance: ```javascript var Ajv = require('ajv'); var ajv = new Ajv({allErrors: true, jsonPointers: true}); // Ajv options allErrors and jsonPointers are required require('ajv-errors')(ajv /*, {singleError: true} */); ``` See [Options](#options) below. ### Single message Replace all errors in the current schema and subschemas with a single message: ```javascript var schema = { type: 'object', required: ['foo'], properties: { foo: { type: 'integer' } }, additionalProperties: false, errorMessage: 'should be an object with an integer property foo only' }; var validate = ajv.compile(schema); console.log(validate({foo: 'a', bar: 2})); // false console.log(validate.errors); // processed errors ``` Processed errors: ```javascript [ { keyword: 'errorMessage', message: 'should be an object with an integer property foo only', // ... params: { errors: [ { keyword: 'additionalProperties', dataPath: '' /* , ... */ }, { keyword: 'type', dataPath: '.foo' /* , ... */ } ] } } ] ``` ### Messages for keywords Replace errors for certain keywords in the current schema only: ```javascript var schema = { type: 'object', required: ['foo'], properties: { foo: { type: 'integer' } }, additionalProperties: false, errorMessage: { type: 'should be an object', // will not replace internal "type" error for the property "foo" required: 'should have property foo', additionalProperties: 'should not have properties other than foo' } }; var validate = ajv.compile(schema); console.log(validate({foo: 'a', bar: 2})); // false console.log(validate.errors); // processed errors ``` Processed errors: ```javascript [ { // original error keyword: type, dataPath: '/foo', // ... message: 'should be integer' }, { // generated error keyword: 'errorMessage', message: 'should not have properties other than foo', // ... params: { errors: [ { keyword: 'additionalProperties' /* , ... */ } ] }, } ] ``` For keywords "required" and "dependencies" it is possible to specify different messages for different properties: ```javascript var schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'integer' }, bar: { type: 'string' } }, errorMessage: { type: 'should be an object', // will not replace internal "type" error for the property "foo" required: { foo: 'should have an integer property "foo"', bar: 'should have a string property "bar"' } } }; ``` ### Messages for properties and items Replace errors for properties / items (and deeper), regardless where in schema they were created: ```javascript var schema = { type: 'object', required: ['foo', 'bar'], allOf: [{ properties: { foo: { type: 'integer', minimum: 2 }, bar: { type: 'string', minLength: 2 } }, additionalProperties: false }], errorMessage: { properties: { foo: 'data.foo should be integer >= 2', bar: 'data.bar should be string with length >= 2' } } }; var validate = ajv.compile(schema); console.log(validate({foo: 1, bar: 'a'})); // false console.log(validate.errors); // processed errors ``` Processed errors: ```javascript [ { keyword: 'errorMessage', message: 'data.foo should be integer >= 2', dataPath: '/foo', // ... params: { errors: [ { keyword: 'minimum' /* , ... */ } ] }, }, { keyword: 'errorMessage', message: 'data.bar should be string with length >= 2', dataPath: '/bar', // ... params: { errors: [ { keyword: 'minLength' /* , ... */ } ] }, } ] ``` ### Default message When the value of keyword `errorMessage` is an object you can specify a message that will be used if any error appears that is not specified by keywords/properties/items: ```javascript var schema = { type: 'object', required: ['foo', 'bar'], allOf: [{ properties: { foo: { type: 'integer', minimum: 2 }, bar: { type: 'string', minLength: 2 } }, additionalProperties: false }], errorMessage: { type: 'data should be an object', properties: { foo: 'data.foo should be integer >= 2', bar: 'data.bar should be string with length >= 2' }, _: 'data should have properties "foo" and "bar" only' } }; var validate = ajv.compile(schema); console.log(validate({})); // false console.log(validate.errors); // processed errors ``` Processed errors: ```javascript [ { keyword: 'errorMessage', message: 'data should be an object with properties "foo" and "bar" only', dataPath: '', // ... params: { errors: [ { keyword: 'required' /* , ... */ }, { keyword: 'required' /* , ... */ } ] }, } ] ``` The message in property `_` of `errorMessage` replaces the same errors that would have been replaced if `errorMessage` were a string. ## Templates Custom error messages used in `errorMessage` keyword can be templates using [JSON-pointers](https://tools.ietf.org/html/rfc6901) or [relative JSON-pointers](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00) to data being validated, in which case the value will be interpolated. Also see [examples](https://gist.github.com/geraintluff/5911303) of relative JSON-pointers. The syntax to interpolate a value is `${}`. The values used in messages will be JSON-stringified: - to differentiate between `false` and `"false"`, etc. - to support structured values. Example: ```json { "type": "object", "properties": { "size": { "type": "number", "minimum": 4 } }, "errorMessage": { "properties": { "size": "size should be a number bigger or equal to 4, current value is ${/size}" } } } ``` ## Options Defaults: ```javascript { keepErrors: false, singleError: false } ``` - _keepErrors_: keep original errors. Default is to remove matched errors (they will still be available in `params.errors` property of generated error). If an error was matched and included in the error generated by `errorMessage` keyword it will have property `emUsed: true`. - _singleError_: create one error for all keywords used in `errorMessage` keyword (error messages defined for properties and items are not merged because they have different dataPaths). Multiple error messages are concatenated. Option values: - `false` (default): create multiple errors, one for each message - `true`: create single error, messages are concatenated using `"; "` - non-empty string: this string is used as a separator to concatenate messages ## Supporters [](https://www.linkedin.com/in/rogerkepler/) [Roger Kepler](https://www.linkedin.com/in/rogerkepler/) ## License [MIT](https://github.com/epoberezkin/ajv-errors/blob/master/LICENSE) ajv-errors-1.0.1/index.js000066400000000000000000000025021340327644600152600ustar00rootroot00000000000000'use strict'; module.exports = function (ajv, options) { if (!ajv._opts.allErrors) throw new Error('ajv-errors: Ajv option allErrors must be true'); if (!ajv._opts.jsonPointers) { console.warn('ajv-errors: Ajv option jsonPointers changed to true'); ajv._opts.jsonPointers = true; } ajv.addKeyword('errorMessage', { inline: require('./lib/dotjs/errorMessage'), statements: true, valid: true, errors: 'full', config: { KEYWORD_PROPERTY_PARAMS: { required: 'missingProperty', dependencies: 'property' }, options: options || {} }, metaSchema: { 'type': ['string', 'object'], properties: { properties: {$ref: '#/definitions/stringMap'}, items: {$ref: '#/definitions/stringList'}, required: {$ref: '#/definitions/stringOrMap'}, dependencies: {$ref: '#/definitions/stringOrMap'} }, additionalProperties: {'type': 'string'}, definitions: { stringMap: { 'type': ['object'], additionalProperties: {'type': 'string'} }, stringOrMap: { 'type': ['string', 'object'], additionalProperties: {'type': 'string'} }, stringList: { 'type': ['array'], items: {'type': 'string'} } } } }); return ajv; }; ajv-errors-1.0.1/lib/000077500000000000000000000000001340327644600143625ustar00rootroot00000000000000ajv-errors-1.0.1/lib/dot/000077500000000000000000000000001340327644600151505ustar00rootroot00000000000000ajv-errors-1.0.1/lib/dot/errorMessage.jst000066400000000000000000000307041340327644600203340ustar00rootroot00000000000000{{# def.definitions }} {{# def.errors }} {{# def.setupKeyword }} {{## def.em_errorMatch: {{# def._em_commonErrorMatch }} && ({{=$err}}.dataPath == {{=$dataPath}} || ({{=$err}}.dataPath.indexOf({{=$dataPath}}) == 0 && {{=$err}}.dataPath[{{=$dataPath}}.length] == '/')) && {{=$err}}.schemaPath.indexOf({{=$errSchemaPathString}}) == 0 && {{=$err}}.schemaPath[{{=it.errSchemaPath.length}}] == '/' #}} {{## def.em_keywordErrorMatch: {{# def._em_commonErrorMatch }} && {{=$err}}.keyword in {{=$errors}} && {{=$err}}.dataPath == {{=$dataPath}} && {{=$err}}.schemaPath.indexOf({{=$errSchemaPathString}}) == 0 && /^\/[^\/]*$/.test({{=$err}}.schemaPath.slice({{=it.errSchemaPath.length}})) #}} {{## def.em_childErrorMatch: {{# def._em_commonErrorMatch }} && {{=$err}}.dataPath.indexOf({{=$dataPath}}) == 0 && ({{=$matches}} = {{=$err}}.dataPath.slice({{=$dataPath}}.length).match(/^\/([^\/]*)(?:\/|$)/), {{=$child}} = {{=$matches}} && {{=$matches}}[1].replace(/~1/g, '/').replace(/~0/g, '~') ) !== undefined && {{=$child}} in {{=$errors}} #}} {{## def._em_commonErrorMatch: {{=$err}}.keyword != '{{=$keyword}}' {{? $config.options.keepErrors }} && !{{=$err}}.emUsed {{?}} #}} {{## def.em_useError: {{? $config.options.keepErrors }} {{=$err}}.emUsed = true; {{??}} vErrors.splice({{=$i}}, 1); errors--; {{?}} #}} {{## def.em_compileTemplates: _keysArray: var {{=$templates}} = { {{ var $comma = false; }} {{~ _keysArray:$k }} {{? INTERPOLATION.test($schema[$k]) }} {{?$comma}},{{?}}{{= it.util.toQuotedString($k) }}: {{= templateFunc($schema[$k]) }} {{ $comma = true; }} {{?}} {{~}} }; #}} {{## def.em_compilePropsTemplates: _keywordProps: var {{=$templates}} = { {{ var $comma = false; }} {{~ Object.keys(_keywordProps):$k }} {{ var $keywordMsgs = $schema[$k]; }} {{?$comma}},{{?}}{{= it.util.toQuotedString($k) }}: { {{ $comma = true; var $innerComma = false; }} {{~ Object.keys($keywordMsgs):$prop }} {{? INTERPOLATION.test($keywordMsgs[$prop]) }} {{?$innerComma}},{{?}}{{= it.util.toQuotedString($prop) }}: {{= templateFunc($keywordMsgs[$prop]) }} {{ $innerComma = true; }} {{?}} {{~}} } {{~}} }; #}} {{## def.em_compileChildTemplates: _children: {{ var _keysArray = Object.keys($childErrors._children); }} var {{=$templates}} = { {{ var $comma = false; }} {{~ _keysArray:$k }} {{? INTERPOLATION.test($schema._children[$k]) }} {{?$comma}},{{?}}{{= it.util.toQuotedString($k) }}: {{= templateFunc($schema._children[$k]) }} {{ $comma = true; }} {{?}} {{~}} }; #}} {{## def.em_errorMessage: {{=$key}} in {{=$templates}} ? {{=$templates}}[{{=$key}}] () : validate.schema{{=$schemaPath}}[{{=$key}}] #}} {{## def.em_keywordError: var err = { keyword: '{{=$keyword}}' , dataPath: {{=$dataPath}} , schemaPath: {{=$errSchemaPathString}} + '/{{=$keyword}}' , params: { errors: {{=$paramsErrors}} } , message: {{=$message}} {{? it.opts.verbose }} , schema: validate.schema{{=$schemaPath}} , parentSchema: validate.schema{{=it.schemaPath}} , data: {{=$data}} {{?}} }; {{# def._addError:'custom' }} #}} {{? it.createErrors !== false }} {{ var INTERPOLATION = /\$\{[^\}]+\}/; var INTERPOLATION_REPLACE = /\$\{([^\}]+)\}/g; var EMPTY_STR = /^\'\'\s*\+\s*|\s*\+\s*\'\'$/g; var $config = it.self.getKeyword($keyword).config , $dataPath = '_em_dataPath' + $lvl , $i = '_em_i' + $lvl , $key = '_em_key' + $lvl , $keyProp = '_em_keyProp' + $lvl , $err = '_em_err' + $lvl , $child = '_em_child' + $lvl , $childKeyword = '_em_childKeyword' + $lvl , $matches = '_em_matches' + $lvl , $isArray = '_em_isArray' + $lvl , $errors = '_em_errors' + $lvl , $message = '_em_message' + $lvl , $paramsErrors = '_em_paramsErrors' + $lvl , $propParam = '_em_propParam' + $lvl , $keywordPropParams = '_em_keywordPropParams' + $lvl , $templates = '_em_templates' + $lvl , $errSchemaPathString = it.util.toQuotedString(it.errSchemaPath); }} if (errors > 0) { var {{=$dataPath}} = (dataPath || '') + {{= it.errorPath }}; var {{=$i}}, {{=$err}}, {{=$errors}}; {{? typeof $schema == 'object' }} {{ var $keywordErrors = {} , $keywordPropErrors = {} , $childErrors = { properties: {}, items: {} } , $hasKeywordProps = false , $hasProperties = false , $hasItems = false; for (var $k in $schema) { switch ($k) { case 'properties': for (var $prop in $schema.properties) { $hasProperties = true; $childErrors.properties[$prop] = []; } break; case 'items': for (var $item=0; $item<$schema.items.length; $item++) { $hasItems = true; $childErrors.items[$item] = []; } break; default: if (typeof $schema[$k] == 'object') { $hasKeywordProps = true; $keywordPropErrors[$k] = {}; for (var $prop in $schema[$k]) { $keywordPropErrors[$k][$prop] = []; } } else { $keywordErrors[$k] = []; } } } }} {{ var $keywordErrorsArr = Object.keys($keywordErrors); }} {{? $keywordErrorsArr.length }} {{=$i}} = 0; {{=$errors}} = {{= JSON.stringify($keywordErrors) }}; {{# def.em_compileTemplates:$keywordErrorsArr }} while ({{=$i}} < errors) { {{=$err}} = vErrors[{{=$i}}]; if ({{# def.em_keywordErrorMatch}}) { {{=$errors}}[{{=$err}}.keyword].push({{=$err}}); {{# def.em_useError }} } else { {{=$i}}++; } } {{? $config.options.singleError }} var {{=$message}} = ''; var {{=$paramsErrors}} = []; {{?}} for (var {{=$key}} in {{=$errors}}) { if ({{=$errors}}[{{=$key}}].length) { {{? $config.options.singleError }} if ({{=$message}}) { {{=$message}} += {{? typeof $config.options.singleError == 'string' }} {{= it.util.toQuotedString($config.options.singleError) }} {{??}} '; ' {{?}}; } {{=$message}} += {{# def.em_errorMessage }}; {{=$paramsErrors}} = {{=$paramsErrors}}.concat({{=$errors}}[{{=$key}}]); } } {{??}} var {{=$message}} = {{# def.em_errorMessage }}; var {{=$paramsErrors}} = {{=$errors}}[{{=$key}}]; {{?}} {{# def.em_keywordError}} {{? !$config.options.singleError }} } } {{?}} {{?}} /* $keywordErrorsArr */ {{? $hasKeywordProps }} {{=$i}} = 0; {{=$errors}} = {{= JSON.stringify($keywordPropErrors) }}; var {{=$paramsErrors}}, {{=$propParam}}; var {{=$keywordPropParams}} = {{= JSON.stringify($config.KEYWORD_PROPERTY_PARAMS) }}; {{# def.em_compilePropsTemplates:$keywordPropErrors }} while ({{=$i}} < errors) { {{=$err}} = vErrors[{{=$i}}]; if ({{# def.em_keywordErrorMatch}}) { {{=$propParam}} = {{=$keywordPropParams}}[{{=$err}}.keyword]; {{=$paramsErrors}} = {{=$errors}}[{{=$err}}.keyword][{{=$err}}.params[{{=$propParam}}]]; if ({{=$paramsErrors}}) { {{=$paramsErrors}}.push({{=$err}}); {{# def.em_useError }} } else { {{=$i}}++; } } else { {{=$i}}++; } } for (var {{=$key}} in {{=$errors}}) { for (var {{=$keyProp}} in {{=$errors}}[{{=$key}}]) { {{=$paramsErrors}} = {{=$errors}}[{{=$key}}][{{=$keyProp}}]; if ({{=$paramsErrors}}.length) { var {{=$message}} = {{=$key}} in {{=$templates}} && {{=$keyProp}} in {{=$templates}}[{{=$key}}] ? {{=$templates}}[{{=$key}}][{{=$keyProp}}] () : validate.schema{{=$schemaPath}}[{{=$key}}][{{=$keyProp}}]; {{# def.em_keywordError}} } } } {{?}} /* $hasKeywordProps */ {{? $hasProperties || $hasItems }} var {{=$isArray}} = Array.isArray({{=$data}}); if {{? $hasProperties && $hasItems }} (typeof {{=$data}} == 'object') { {{ var $childProp = '[' + $childKeyword + ']'; }} {{=$i}} = 0; if ({{=$isArray}}) { var {{=$childKeyword}} = 'items'; {{=$errors}} = {{= JSON.stringify($childErrors.items) }}; {{# def.em_compileChildTemplates: items }} } else { var {{=$childKeyword}} = 'properties'; {{=$errors}} = {{= JSON.stringify($childErrors.properties) }}; {{# def.em_compileChildTemplates: properties }} } {{?? $hasProperties }} (typeof {{=$data}} == 'object' && !{{=$isArray}}) { {{ var $childProp = '.properties'; }} {{=$i}} = 0; {{=$errors}} = {{= JSON.stringify($childErrors.properties) }}; {{# def.em_compileChildTemplates: properties }} {{??}} ({{=$isArray}}) { {{ var $childProp = '.items'; }} {{=$i}} = 0; {{=$errors}} = {{= JSON.stringify($childErrors.items) }}; {{# def.em_compileChildTemplates: items }} {{?}} var {{=$child}}, {{=$matches}}; while ({{=$i}} < errors) { {{=$err}} = vErrors[{{=$i}}]; if ({{# def.em_childErrorMatch}}) { {{=$errors}}[{{=$child}}].push({{=$err}}); {{# def.em_useError }} } else { {{=$i}}++; } } for (var {{=$key}} in {{=$errors}}) { if ({{=$errors}}[{{=$key}}].length) { var err = { keyword: '{{=$keyword}}' , dataPath: {{=$dataPath}} + '/' + {{=$key}}.replace(/~/g, '~0').replace(/\//g, '~1') , schemaPath: {{=$errSchemaPathString}} + '/{{=$keyword}}' , params: { errors: {{=$errors}}[{{=$key}}] } , message: {{=$key}} in {{=$templates}} ? {{=$templates}}[{{=$key}}] () : validate.schema{{=$schemaPath}}{{=$childProp}}[{{=$key}}] {{? it.opts.verbose }} , schema: validate.schema{{=$schemaPath}} , parentSchema: validate.schema{{=it.schemaPath}} , data: {{=$data}} {{?}} }; {{# def._addError:'custom' }} } } /* for */ } /* if */ {{?}} /* $hasProperties || $hasItems */ {{?}} /* $schema is object */ {{ var $schemaMessage = typeof $schema == 'string' ? $schema : $schema._; }} {{? $schemaMessage }} {{=$i}} = 0; {{=$errors}} = []; while ({{=$i}} < errors) { {{=$err}} = vErrors[{{=$i}}]; if ({{# def.em_errorMatch}}) { {{=$errors}}.push({{=$err}}); {{# def.em_useError }} } else { {{=$i}}++; } } if ({{=$errors}}.length) { var err = { keyword: '{{=$keyword}}' , dataPath: {{=$dataPath}} , schemaPath: {{=$errSchemaPathString}} + '/{{=$keyword}}' , params: { errors: {{=$errors}} } , message: {{=templateExpr($schemaMessage)}} {{? it.opts.verbose }} , schema: {{=it.util.toQuotedString($schemaMessage)}} , parentSchema: validate.schema{{=it.schemaPath}} , data: {{=$data}} {{?}} }; {{# def._addError:'custom' }} } {{?}} } {{?}} {{ function templateExpr(str) { str = it.util.escapeQuotes(str); if (!INTERPOLATION.test(str)) return "'" + str + "'"; var expr = "'" + str.replace(INTERPOLATION_REPLACE, function ($0, $1) { return "' + JSON.stringify(" + it.util.getData($1, $dataLvl, it.dataPathArr) + ") + '"; }) + "'"; return expr.replace(EMPTY_STR, ''); } function templateFunc(str) { return 'function() { return ' + templateExpr(str) + '; }'; } }} ajv-errors-1.0.1/lib/dotjs/000077500000000000000000000000001340327644600155055ustar00rootroot00000000000000ajv-errors-1.0.1/lib/dotjs/README.md000066400000000000000000000002361340327644600167650ustar00rootroot00000000000000These files are compiled dot templates from dot folder. Do NOT edit them directly, edit the templates and run `npm run build` from main ajv-keywords folder. ajv-errors-1.0.1/package.json000066400000000000000000000024561340327644600161110ustar00rootroot00000000000000{ "name": "ajv-errors", "version": "1.0.1", "description": "Custom error messages in JSON-Schema for Ajv validator", "main": "index.js", "files": [ "lib" ], "scripts": { "build": "node node_modules/ajv/scripts/compile-dots.js node_modules/ajv/lib lib", "eslint": "eslint *.js spec", "test-spec": "mocha spec/*.spec.js -R spec", "test-cov": "nyc npm run test-spec", "test": "npm run eslint && npm run build && npm run test-cov", "prepublish": "npm run build" }, "repository": { "type": "git", "url": "git+https://github.com/epoberezkin/ajv-errors.git" }, "keywords": [ "ajv", "json-schema", "validator", "error", "messages" ], "author": "", "license": "MIT", "bugs": { "url": "https://github.com/epoberezkin/ajv-errors/issues" }, "homepage": "https://github.com/epoberezkin/ajv-errors#readme", "peerDependencies": { "ajv": ">=5.0.0" }, "devDependencies": { "ajv": "^5.0.0", "coveralls": "^2.11.16", "dot": "^1.1.1", "eslint": "^3.17.0", "glob": "^7.1.1", "js-beautify": "^1.6.12", "mocha": "^3.2.0", "nyc": "^10.1.2", "pre-commit": "^1.2.2" }, "nyc": { "exclude": [ "**/spec/**", "node_modules" ], "reporter": [ "lcov", "text-summary" ] } } ajv-errors-1.0.1/spec/000077500000000000000000000000001340327644600145465ustar00rootroot00000000000000ajv-errors-1.0.1/spec/.eslintrc.yml000066400000000000000000000001471340327644600171740ustar00rootroot00000000000000rules: no-console: 0 no-invalid-this: 0 globals: beforeEach: false describe: false it: false ajv-errors-1.0.1/spec/index.spec.js000066400000000000000000000013311340327644600171420ustar00rootroot00000000000000'use strict'; var ajvErrors = require('../index'); var Ajv = require('ajv'); var assert = require('assert'); describe('ajv-errors', function() { it('should return ajv instance', function() { var ajv = new Ajv({allErrors: true, jsonPointers: true}); var _ajv = ajvErrors(ajv); assert.equal(_ajv, ajv); }); it('should throw if option allErrors is not set', function() { var ajv = new Ajv; assert.throws(function() { ajvErrors(ajv); }); }); it('should set option jsonPointers if not set', function() { var ajv = new Ajv({allErrors: true}); assert.strictEqual(ajv._opts.jsonPointers, undefined); ajvErrors(ajv); assert.strictEqual(ajv._opts.jsonPointers, true); }); }); ajv-errors-1.0.1/spec/object.spec.js000066400000000000000000000575461340327644600173240ustar00rootroot00000000000000'use strict'; var ajvErrors = require('../index'); var Ajv = require('ajv'); var assert = require('assert'); describe('errorMessage value is an object', function() { var ajvs; beforeEach(function() { ajvs = [ ajvErrors(new Ajv({allErrors: true, jsonPointers: true/*, sourceCode: true, processCode: require('js-beautify').js_beautify*/ })), ajvErrors(new Ajv({allErrors: true, jsonPointers: true, verbose: true/*, sourceCode: true, processCode: require('js-beautify').js_beautify*/ })) ]; }); describe('keywords', function() { it('should replace keyword errors with custom error messages', function() { var schema = { type: 'object', required: ['foo'], properties: { foo: { type: 'integer' } }, additionalProperties: false, errorMessage: { type: 'should be an object', required: 'should have property foo', additionalProperties: 'should not have properties other than foo' } }; ajvs.forEach(function (ajv) { var validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1}), true); testInvalid({}, [['required']]); testInvalid({bar: 2}, [['required'], ['additionalProperties']]); testInvalid({foo: 1, bar: 2}, [['additionalProperties']]); testInvalid({foo: 'a'}, ['type']); testInvalid({foo: 'a', bar: 2}, ['type', ['additionalProperties']]); testInvalid(1, [['type']]); function testInvalid(data, expectedErrors) { assert.strictEqual(validate(data), false); assert.strictEqual(validate.errors.length, expectedErrors.length); validate.errors.forEach(function (err, i) { var expectedErr = expectedErrors[i]; if (Array.isArray(expectedErr)) { // errorMessage error assert.strictEqual(err.keyword, 'errorMessage'); assert.strictEqual(err.message, schema.errorMessage[err.params.errors[0].keyword]); assert.strictEqual(err.dataPath, ''); assert.strictEqual(err.schemaPath, '#/errorMessage'); var replacedKeywords = err.params.errors.map(function (e) { return e.keyword; }); assert.deepEqual(replacedKeywords.sort(), expectedErr.sort()); } else { // original error assert.strictEqual(err.keyword, expectedErr); } }); } }); }); describe('keyword errors with interpolated error messages', function() { var schema, validate; it('should replace errors', function() { schema = { type: 'object', properties: { foo: { type: 'number', minimum: 5, maximum: 10, errorMessage: { type: 'property ${0#} should be number, it is ${0}', minimum: 'property ${0#} should be >= 5, it is ${0}', maximum: 'property foo should be <= 10' } } } }; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate({foo: 7}), true); testInvalid({foo: 'a'}, [['type']]); testInvalid({foo: ['a']}, [['type']]); testInvalid({foo: 4.5}, [['minimum']]); testInvalid({foo: 10.5}, [['maximum']]); }); }); it('should replace keyword errors with interpolated error messages with type integer', function() { schema = { type: 'object', properties: { foo: { type: 'integer', minimum: 5, maximum: 10, errorMessage: { type: 'property ${0#} should be integer, it is ${0}', minimum: 'property ${0#} should be >= 5, it is ${0}', maximum: 'property foo should be <= 10' } } } }; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate({foo: 7}), true); testInvalid({foo: 'a'}, [['type']]); testInvalid({foo: ['a']}, [['type']]); testInvalid({foo: 5.5}, [['type']]); testInvalid({foo: 4.5}, [['type'], ['minimum']]); testInvalid({foo: 4}, [['minimum']]); testInvalid({foo: 11}, [['maximum']]); }); }); function testInvalid(data, expectedErrors) { assert.strictEqual(validate(data), false); assert.strictEqual(validate.errors.length, expectedErrors.length); validate.errors.forEach(function (err, i) { var expectedErr = expectedErrors[i]; if (Array.isArray(expectedErr)) { // errorMessage error assert.strictEqual(err.keyword, 'errorMessage'); var expectedMessage = schema.properties.foo .errorMessage[err.params.errors[0].keyword] .replace('${0#}', '"foo"') .replace('${0}', JSON.stringify(data.foo)); assert.strictEqual(err.message, expectedMessage); assert.strictEqual(err.dataPath, '/foo'); assert.strictEqual(err.schemaPath, '#/properties/foo/errorMessage'); var replacedKeywords = err.params.errors.map(function (e) { return e.keyword; }); assert.deepEqual(replacedKeywords.sort(), expectedErr.sort()); } else { // original error assert.strictEqual(err.keyword, expectedErr); } }); } }); describe('"required" and "dependencies" keywords errors for specific properties', function() { var schema, validate; it('should replace required errors with messages', function() { schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'integer' }, bar: { type: 'string' } }, errorMessage: { type: 'should be an object', required: { foo: 'an integer property "foo" is required', bar: 'a string property "bar" is required' } } }; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1, bar: 'a'}), true); testInvalid({}, [{required: 'foo'}, {required: 'bar'}]); testInvalid({foo: 1}, [{required: 'bar'}]); testInvalid({foo: 'a'}, ['type', {required: 'bar'}]); testInvalid({bar: 'a'}, [{required: 'foo'}]); }); }); it('should replace required errors with messages only for specific properties', function() { schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'integer' }, bar: { type: 'string' } }, errorMessage: { type: 'should be an object', required: { foo: 'an integer property "foo" is required' } } }; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1, bar: 'a'}), true); testInvalid({}, ['required', {required: 'foo'}]); testInvalid({foo: 1}, ['required']); testInvalid({foo: 'a'}, ['type', 'required']); testInvalid({bar: 'a'}, [{required: 'foo'}]); }); }); it('should replace required and dependencies errors with messages', function() { schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'integer' }, bar: { type: 'string' } }, dependencies: { foo: ['quux'], bar: ['boo'] }, errorMessage: { type: 'should be an object', required: { foo: 'an integer property "foo" is required, "bar" is ${/bar}', bar: 'a string property "bar" is required, "foo" is ${/foo}' }, dependencies: { foo: '"quux" should be present when "foo" is present, "foo" is ${/foo}', bar: '"boo" should be present when "bar" is present, "bar" is ${/bar}' } } }; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1, bar: 'a', quux: 2, boo: 3}), true); testInvalid({}, [{required: 'foo'}, {required: 'bar'}]); testInvalid({foo: 1}, [{required: 'bar'}, {dependencies: 'foo'}]); testInvalid({foo: 'a'}, ['type', {required: 'bar'}, {dependencies: 'foo'}]); testInvalid({bar: 'a'}, [{required: 'foo'}, {dependencies: 'bar'}]); testInvalid({foo: 1, bar: 'a'}, [{dependencies: 'foo'}, {dependencies: 'bar'}]); testInvalid({foo: 1, bar: 'a', quux: 2}, [{dependencies: 'bar'}]); }); }); it('should replace required errors with interpolated messages', function() { schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'integer' }, bar: { type: 'string' } }, errorMessage: { type: 'should be an object', required: { foo: 'an integer property "foo" is required, "bar" is ${/bar}', bar: 'a string property "bar" is required, "foo" is ${/foo}' } } }; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1, bar: 'a'}), true); testInvalid({}, [{required: 'foo'}, {required: 'bar'}]); testInvalid({foo: 1}, [{required: 'bar'}]); testInvalid({foo: 'a'}, ['type', {required: 'bar'}]); testInvalid({bar: 'a'}, [{required: 'foo'}]); }); }); function testInvalid(data, expectedErrors) { assert.strictEqual(validate(data), false); assert.strictEqual(validate.errors.length, expectedErrors.length); validate.errors.forEach(function (err, i) { var expectedErr = expectedErrors[i]; if (typeof expectedErr == 'object') { // errorMessage error var errKeyword = Object.keys(expectedErr)[0]; var errProp = expectedErr[errKeyword]; assert.strictEqual(err.keyword, 'errorMessage'); var expectedMessage = schema.errorMessage[errKeyword][errProp] .replace('${/foo}', JSON.stringify(data.foo)) .replace('${/bar}', JSON.stringify(data.bar)); assert.strictEqual(err.message, expectedMessage); assert.strictEqual(err.dataPath, ''); assert.strictEqual(err.schemaPath, '#/errorMessage'); var replacedKeywords = err.params.errors.map(function (e) { return e.keyword; }); assert.deepEqual(replacedKeywords, Object.keys(expectedErr)); } else { // original error assert.strictEqual(err.keyword, expectedErr); } }); } }); }); describe('properties and items', function() { var schema, validate; describe('properties only', function() { beforeEach(function() { schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'object', required: ['baz'], properties: { baz: {type: 'integer', maximum: 2} } }, bar: { type: 'array', items: {type: 'string', maxLength: 3}, minItems: 1 } }, additionalProperties: false, errorMessage: 'will be replaced in each test' }; }); it('should replace errors for properties with custom error messages', function() { schema.errorMessage = { properties: { foo: 'data.foo should be an object with the integer property "baz" <= 2', bar: 'data.bar should be an array with at least one string item with length <= 3' } }; testProperties(); }); it('should replace errors for properties with interpolated error messages', function() { schema.errorMessage = { properties: { foo: 'data.foo should be an object with the integer property "baz" <= 2, "baz" is ${/foo/baz}', bar: 'data.bar should be an array with at least one string item with length <= 3, "bar" is ${/bar}' } }; testProperties(tmpl); function tmpl(str, data) { return str.replace('${/foo/baz}', JSON.stringify(data.foo && data.foo.baz)) .replace('${/bar}', JSON.stringify(data.bar)); } }); function testProperties(tmpl) { var validData = { foo: {baz: 1}, bar: ['abc'] }; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate(validData), true); testInvalid({}, ['required', 'required'], tmpl); testInvalid({foo: 1}, ['required', ['type']], tmpl); testInvalid({foo: 1, bar: 2}, [['type'], ['type']], tmpl); testInvalid({foo: {baz: 'a'}}, ['required', ['type']], tmpl); testInvalid({foo: {baz: 3}}, ['required', ['maximum']], tmpl); testInvalid({foo: {baz: 3}, bar: []}, [['maximum'], ['minItems']], tmpl); testInvalid({foo: {baz: 3}, bar: [1]}, [['maximum'], ['type']], tmpl); testInvalid({foo: {baz: 3}, bar: ['abcd']}, [['maximum'], ['maxLength']], tmpl); }); } }); describe('items only', function() { beforeEach(function() { schema = { type: 'array', items: [ { type: 'object', required: ['baz'], properties: { baz: {type: 'integer', maximum: 2} } }, { type: 'array', items: {type: 'string', maxLength: 3}, minItems: 1 } ], minItems: 2, additionalItems: false, errorMessage: 'will be replaced in each test' }; }); it('should replace errors for items with custom error messages', function() { schema.errorMessage = { items: [ 'data[0] should be an object with the integer property "baz" <= 2', 'data[1] should be an array with at least one string item with length <= 3' ] }; testItems(); }); it('should replace errors for items with interpolated error messages', function() { schema.errorMessage = { items: [ 'data[0] should be an object with the integer property "baz" <= 2, data[0].baz is ${/0/baz}', 'data[1] should be an array with at least one string item with length <= 3, data[1] is ${/1}' ] }; testItems(tmpl); function tmpl(str, data) { return str.replace('${/0/baz}', JSON.stringify(data[0] && data[0].baz)) .replace('${/1}', JSON.stringify(data[1])); } }); function testItems(tmpl) { var validData = [ {baz: 1}, ['abc'] ]; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate(validData), true); testInvalid([], ['minItems'], tmpl); testInvalid([1], ['minItems', ['type']], tmpl); testInvalid([1, 2], [['type'], ['type']], tmpl); testInvalid([{baz: 'a'}], ['minItems', ['type']], tmpl); testInvalid([{baz: 3}], ['minItems', ['maximum']], tmpl); testInvalid([{baz: 3}, []], [['maximum'], ['minItems']], tmpl); testInvalid([{baz: 3}, [1]], [['maximum'], ['type']], tmpl); testInvalid([{baz: 3}, ['abcd']], [['maximum'], ['maxLength']], tmpl); }); } }); describe('both properties and items', function() { beforeEach(function() { schema = { definitions: { foo: { type: 'object', required: ['baz'], properties: { baz: {type: 'integer', maximum: 2} } }, bar: { type: 'array', items: {type: 'string', maxLength: 3}, minItems: 1 } }, anyOf: [ { type: 'object', required: ['foo', 'bar'], properties: { foo: {$ref: '#/definitions/foo'}, bar: {$ref: '#/definitions/bar'} }, additionalProperties: false }, { type: 'array', items: [ {$ref: '#/definitions/foo'}, {$ref: '#/definitions/bar'} ], minItems: 2, additionalItems: false } ], errorMessage: 'will be replaced in each test' }; }); it('should replace errors for properties and items with custom error messages', function() { schema.errorMessage = { properties: { foo: 'data.foo should be an object with the integer property "baz" <= 2', bar: 'data.bar should be an array with at least one string item with length <= 3' }, items: [ 'data[0] should be an object with the integer property "baz" <= 2', 'data[1] should be an array with at least one string item with length <= 3' ] }; testPropsAndItems(); }); it('should replace errors for properties and items with interpolated error messages', function() { schema.errorMessage = { properties: { foo: 'data.foo should be an object with the integer property "baz" <= 2, "baz" is ${/foo/baz}', bar: 'data.bar should be an array with at least one string item with length <= 3, "bar" is ${/bar}' }, items: [ 'data[0] should be an object with the integer property "baz" <= 2, data[0].baz is ${/0/baz}', 'data[1] should be an array with at least one string item with length <= 3, data[1] is ${/1}' ] }; testPropsAndItems(tmpl); function tmpl(str, data) { return str.replace('${/foo/baz}', JSON.stringify(data.foo && data.foo.baz)) .replace('${/bar}', JSON.stringify(data.bar)) .replace('${/0/baz}', JSON.stringify(data[0] && data[0].baz)) .replace('${/1}', JSON.stringify(data[1])); } }); function testPropsAndItems(tmpl) { var validData1 = { foo: {baz: 1}, bar: ['abc'] }; var validData2 = [ {baz: 1}, ['abc'] ]; ajvs.forEach(function (ajv) { validate = ajv.compile(schema); assert.strictEqual(validate(validData1), true); assert.strictEqual(validate(validData2), true); testInvalid({}, ['required', 'required', 'type', 'anyOf'], tmpl); testInvalid({foo: 1}, ['required', 'type', 'anyOf', ['type']], tmpl); testInvalid({foo: 1, bar: 2}, ['type', 'anyOf', ['type'], ['type']], tmpl); testInvalid({foo: {baz: 'a'}}, ['required', 'type', 'anyOf', ['type']], tmpl); testInvalid({foo: {baz: 3}}, ['required', 'type', 'anyOf', ['maximum']], tmpl); testInvalid({foo: {baz: 3}, bar: []}, ['type', 'anyOf', ['maximum'], ['minItems']], tmpl); testInvalid({foo: {baz: 3}, bar: [1]}, ['type', 'anyOf', ['maximum'], ['type']], tmpl); testInvalid({foo: {baz: 3}, bar: ['abcd']}, ['type', 'anyOf', ['maximum'], ['maxLength']], tmpl); testInvalid([], ['type', 'minItems', 'anyOf'], tmpl); testInvalid([1], ['type', 'minItems', 'anyOf', ['type']], tmpl); testInvalid([1, 2], ['type', 'anyOf', ['type'], ['type']], tmpl); testInvalid([{baz: 'a'}], ['type', 'minItems', 'anyOf', ['type']], tmpl); testInvalid([{baz: 3}], ['type', 'minItems', 'anyOf', ['maximum']], tmpl); testInvalid([{baz: 3}, []], ['type', 'anyOf', ['maximum'], ['minItems']], tmpl); testInvalid([{baz: 3}, [1]], ['type', 'anyOf', ['maximum'], ['type']], tmpl); testInvalid([{baz: 3}, ['abcd']], ['type', 'anyOf', ['maximum'], ['maxLength']], tmpl); }); } }); function testInvalid(data, expectedErrors, interpolate) { assert.strictEqual(validate(data), false); assert.strictEqual(validate.errors.length, expectedErrors.length); validate.errors.forEach(function (err, i) { var expectedErr = expectedErrors[i]; if (Array.isArray(expectedErr)) { // errorMessage error assert.strictEqual(err.keyword, 'errorMessage'); var child = Array.isArray(data) ? 'items' : 'properties'; var expectedMessage = schema.errorMessage[child][err.dataPath.slice(1)]; if (interpolate) expectedMessage = interpolate(expectedMessage, data); assert.strictEqual(err.message, expectedMessage); assert((Array.isArray(data) ? /^\/(0|1)$/ : /^\/(foo|bar)$/).test(err.dataPath)); assert.strictEqual(err.schemaPath, '#/errorMessage'); var replacedKeywords = err.params.errors.map(function (e) { return e.keyword; }); assert.deepEqual(replacedKeywords.sort(), expectedErr.sort()); } else { // original error assert.strictEqual(err.keyword, expectedErr); } }); } }); describe('default message', function() { it('should replace all errors not replaced by keyword/properties/items messages', function() { var schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'integer' }, bar: { type: 'string' } }, errorMessage: { properties: { foo: 'data.foo should be integer', bar: 'data.bar should be integer' }, required: 'properties foo and bar are required', _: 'should be an object with properties "foo" (integer) and "bar" (string)' } }; ajvs.forEach(function (ajv) { var validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1, bar: 'a'}), true); testInvalid({foo: 1}, [['required']]); testInvalid({foo: 'a'}, [['required'], ['type']]); testInvalid(null, [['type']]); function testInvalid(data, expectedErrors) { assert.strictEqual(validate(data), false); assert.strictEqual(validate.errors.length, expectedErrors.length); validate.errors.forEach(function (err, i) { var expectedErr = expectedErrors[i]; if (Array.isArray(expectedErr)) { // errorMessage error assert.equal(err.keyword, 'errorMessage'); assert.equal(err.schemaPath, '#/errorMessage'); var expectedMessage = err.dataPath ? schema.errorMessage.properties.foo : schema.errorMessage[expectedErr[0] == 'required' ? 'required' : '_']; assert.equal(err.message, expectedMessage); var replacedKeywords = err.params.errors.map(function (e) { return e.keyword; }); assert.deepEqual(replacedKeywords.sort(), expectedErr.sort()); } else { // original error assert.strictEqual(err.keyword, expectedErr); } }); } }); }); }); }); ajv-errors-1.0.1/spec/options.spec.js000066400000000000000000000157071340327644600175420ustar00rootroot00000000000000'use strict'; var ajvErrors = require('../index'); var Ajv = require('ajv'); var assert = require('assert'); describe('options', function() { var ajv; beforeEach(function() { ajv = new Ajv({allErrors: true, jsonPointers: true}); }); describe('keepErrors = true', function() { beforeEach(function() { ajvErrors(ajv, {keepErrors: true}); }); describe('errorMessage is a string', function() { it('should keep matched errors and mark them with {emUsed: true} property', function() { var schema = { type: 'object', required: ['foo', 'bar'], properties: { foo: { type: 'object', properties: { baz: { type: 'integer' } }, errorMessage: 'should be an object with an integer property baz' }, bar: { type: 'integer' } } }; var validate = ajv.compile(schema); assert.strictEqual(validate({foo: {baz: 1}, bar: 2}), true); assert.strictEqual(validate({foo: 1}), false); assertErrors(validate, [ { keyword: 'type', dataPath: '/foo', emUsed: true }, { keyword: 'errorMessage', message: 'should be an object with an integer property baz', dataPath: '/foo', errors: ['type'] }, { keyword: 'required', dataPath: '' } ]); }); }); describe('errorMessage is an object with keywords', function() { it('should keep matched errors and mark them with {emUsed: true} property', function() { var schema = { type: 'number', minimum: 2, maximum: 10, multipleOf: 2, errorMessage: { type: 'should be number', minimum: 'should be >= 2', maximum: 'should be <= 10' } }; var validate = ajv.compile(schema); assert.strictEqual(validate(4), true); assert.strictEqual(validate(11), false); assertErrors(validate, [ { keyword: 'maximum', dataPath: '', emUsed: true }, { keyword: 'multipleOf', dataPath: '' }, { keyword: 'errorMessage', message: 'should be <= 10', dataPath: '', errors: ['maximum'] } ]); }); }); describe('errorMessage is an object with "required" keyword with properties', function() { it('should keep matched errors and mark them with {emUsed: true} property', function() { var schema = { type: 'object', required: ['foo', 'bar'], errorMessage: { type: 'should be object', required: { foo: 'should have property foo', bar: 'should have property bar', } } }; var validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1, bar: 2}), true); assert.strictEqual(validate({}), false); assertErrors(validate, [ { keyword: 'required', dataPath: '', emUsed: true }, { keyword: 'required', dataPath: '', emUsed: true }, { keyword: 'errorMessage', message: 'should have property foo', dataPath: '', errors: ['required'] }, { keyword: 'errorMessage', message: 'should have property bar', dataPath: '', errors: ['required'] } ]); }); }); describe('errorMessage is an object with properties/items', function() { it('should keep matched errors and mark them with {emUsed: true} property', function() { var schema = { type: 'object', properties: { foo: {type: 'number'}, bar: {type: 'string'} }, errorMessage: { properties: { foo: 'foo should be a number' } } }; var validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1, bar: 'a'}), true); assert.strictEqual(validate({foo: 'a', bar: 1}), false); assertErrors(validate, [ { keyword: 'type', dataPath: '/foo', emUsed: true }, { keyword: 'type', dataPath: '/bar', }, { keyword: 'errorMessage', message: 'foo should be a number', dataPath: '/foo', errors: ['type'] } ]); }); }); }); describe('singleError', function() { describe('= true', function() { it('should generate a single error for all keywords', function() { ajvErrors(ajv, {singleError: true}); testSingleErrors('; '); }); }); describe('= separator', function() { it('should generate a single error for all keywords using separator', function() { ajvErrors(ajv, {singleError: '\n'}); testSingleErrors('\n'); }); }); function testSingleErrors(separator) { var schema = { type: 'number', minimum: 2, maximum: 10, multipleOf: 2, errorMessage: { type: 'should be number', minimum: 'should be >= 2', maximum: 'should be <= 10', multipleOf: 'should be multipleOf 2' } }; var validate = ajv.compile(schema); assert.strictEqual(validate(4), true); assert.strictEqual(validate(11), false); var expectedKeywords = ['maximum', 'multipleOf']; var expectedMessage = expectedKeywords .map(function (keyword) { return schema.errorMessage[keyword]; }) .join(separator); assertErrors(validate, [ { keyword: 'errorMessage', message: expectedMessage, dataPath: '', errors: expectedKeywords } ]); } }); function assertErrors(validate, expectedErrors) { assert.strictEqual(validate.errors.length, expectedErrors.length); expectedErrors.forEach(function (expectedErr, i) { var err = validate.errors[i]; assert.strictEqual(err.keyword, expectedErr.keyword); assert.strictEqual(err.dataPath, expectedErr.dataPath); assert.strictEqual(err.emUsed, expectedErr.emUsed); if (expectedErr.keyword == 'errorMessage') { assert.strictEqual(err.params.errors.length, expectedErr.errors.length); expectedErr.errors.forEach(function (matchedKeyword, j) { assert.strictEqual(err.params.errors[j].keyword, matchedKeyword); }); } }); } }); ajv-errors-1.0.1/spec/string.spec.js000066400000000000000000000074651340327644600173570ustar00rootroot00000000000000'use strict'; var ajvErrors = require('../index'); var Ajv = require('ajv'); var assert = require('assert'); describe('errorMessage value is a string', function() { it('should replace all errors with custom error message', function() { var ajvs = [ ajvErrors(new Ajv({allErrors: true, jsonPointers: true})), ajvErrors(new Ajv({allErrors: true, jsonPointers: true, verbose: true})) ]; var schema = { type: 'object', required: ['foo'], properties: { foo: { type: 'integer' } }, additionalProperties: false, errorMessage: 'should be an object with an integer property foo only' }; ajvs.forEach(function (ajv) { var validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1}), true); testInvalid({}, ['required']); testInvalid({bar: 2}, ['required', 'additionalProperties']); testInvalid({foo: 1, bar: 2}, ['additionalProperties']); testInvalid({foo: 'a'}, ['type']); testInvalid({foo: 'a', bar: 2}, ['type', 'additionalProperties']); testInvalid(1, ['type']); function testInvalid(data, expectedReplacedKeywords) { assert.strictEqual(validate(data), false); assert.strictEqual(validate.errors.length, 1); var err = validate.errors[0]; assert.strictEqual(err.keyword, 'errorMessage'); assert.strictEqual(err.message, schema.errorMessage); assert.strictEqual(err.dataPath, ''); assert.strictEqual(err.schemaPath, '#/errorMessage'); var replacedKeywords = err.params.errors.map(function (e) { return e.keyword; }); assert.deepEqual(replacedKeywords.sort(), expectedReplacedKeywords.sort()); } }); }); it('should replace all errors with interpolated error message', function() { var ajvs = [ ajvErrors(new Ajv({allErrors: true, jsonPointers: true})), ajvErrors(new Ajv({allErrors: true, jsonPointers: true, verbose: true})) ]; var errorMessages = [ 'properties "foo" = ${/foo}, "bar" = ${/bar}, should be integer', '${/foo}, ${/bar} are the values of properties "foo", "bar", should be integer', 'properties "foo", "bar" should be integer, they are ${/foo}, ${/bar}' ]; var schema = { type: 'object', properties: { foo: { type: 'integer' }, bar: { type: 'integer' } }, errorMessage: 'will be replaced with one of the messages above' }; errorMessages.forEach(function (message) { ajvs.forEach(function (ajv) { schema = JSON.parse(JSON.stringify(schema)); schema.errorMessage = message; var validate = ajv.compile(schema); assert.strictEqual(validate({foo: 1}), true); testInvalid({foo: 1.2, bar: 2.3}, ['type', 'type']); testInvalid({foo: 'a', bar: 'b'}, ['type', 'type']); testInvalid({foo: false, bar: true}, ['type', 'type']); function testInvalid(data, expectedReplacedKeywords) { assert.strictEqual(validate(data), false); assert.strictEqual(validate.errors.length, 1); var err = validate.errors[0]; assert.strictEqual(err.keyword, 'errorMessage'); var expectedMessage = schema.errorMessage .replace('${/foo}', JSON.stringify(data.foo)) .replace('${/bar}', JSON.stringify(data.bar)); assert.strictEqual(err.message, expectedMessage); assert.strictEqual(err.dataPath, ''); assert.strictEqual(err.schemaPath, '#/errorMessage'); var replacedKeywords = err.params.errors.map(function (e) { return e.keyword; }); assert.deepEqual(replacedKeywords.sort(), expectedReplacedKeywords.sort()); } }); }); }); });