├── .eslintrc.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── commands ├── ajv.js ├── help.js ├── index.js ├── options.js ├── test.js ├── util.js └── validate.js ├── index.js ├── package.json └── test ├── .eslintrc.yml ├── cli.js ├── custom ├── invalid_custom.js ├── invalid_data.json ├── schema.json ├── typeof.js └── valid_data.json ├── data_with_additional.json ├── help.spec.js ├── invalid_data.json ├── invalid_data2.json ├── meta ├── invalid_data.json ├── invalid_schema.json ├── invalid_schema2.json ├── meta_schema.json ├── schema.json └── valid_data.json ├── schema.json ├── schema_with_ref.json ├── test.spec.js ├── valid_data.json ├── valid_data.yml ├── valid_data2.json └── validate.spec.js /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | env: 3 | node: true 4 | extends: 'eslint:recommended' 5 | rules: 6 | indent: [2, 4, { SwitchCase : 1 } ] 7 | no-trailing-spaces: 2 8 | quotes: [ 2, single, avoid-escape ] 9 | linebreak-style: [ 2, unix ] 10 | semi: [ 2, always ] 11 | valid-jsdoc: [ 2, { requireReturn: false } ] 12 | no-invalid-this: 2 13 | no-unused-vars: [ 2, { args: none } ] 14 | no-console: 0 15 | block-scoped-var: 2 16 | complexity: [ 2, 12 ] 17 | curly: [ 2, multi-or-nest, consistent ] 18 | dot-location: [ 2, property ] 19 | dot-notation: 2 20 | no-else-return: 2 21 | no-eq-null: 2 22 | no-fallthrough: 2 23 | no-return-assign: 2 24 | strict: [ 2, global ] 25 | no-shadow: 1 26 | no-use-before-define: [ 2, nofunc ] 27 | callback-return: 2 28 | no-path-concat: 2 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory 11 | coverage 12 | .nyc_output 13 | 14 | # Dependency directory 15 | node_modules 16 | 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | before_script: 3 | - npm install -g codeclimate-test-reporter 4 | node_js: 5 | - '6' 6 | - '7' 7 | after_script: 8 | - codeclimate-test-reporter < coverage/lcov.info 9 | - coveralls < coverage/lcov.info 10 | deploy: 11 | - provider: releases 12 | api_key: 13 | secure: >- 14 | aCtltCB91v1CCQapAR/3DQhlric25pguvg7OxemIOKXr8xQiG3JNJ9rrb3wp0Onw4Gugcceubka5IfozgX/OnhoKyc8W3TANy7MLMtoV8h0CgALqxAPbjlPU5EamrGIHHvryHq2tyCYygGeZVSs+NiDgQwR7DBw8MLuQiQ3+aNejro0R5UrSm44XTiA2wJmKQqYOldsF9GpVoR/LuNRBiaEQgLfdVxlXTiNw7UzWYbVnhvTdxcuyvcC5IiN2pANT+ZFXHfkqvszx9q4JB4qQoRl0WLmYN86SacziN/p+5MoTmyoJnXNOMJb0pDmbWXJfjsEZOMwJzsOUogVsV38sDN0cjaFBnRdQbo9iiLOZ1Ap8mHc3ErA8mXbA4jrXrIWv6PDhLvE+lavRWDFPjaQ24Y2gQPtDa+WUAjj36hOYQVRjM+7ugZgybPJ5hsWkKwFWad0t7Rb3SPeJO6JuI83iyauDmTR8ooQoDqC/Pb2TF9M8y1yVRc43B6zCVs097MbTNbcGB++YSEpbI6k1/c5YqyxetRGzDlW1e1WhUySYQeQZFktSVK1wGyr2jNQY8QdXHjsVacCN1X4JkajSX06JB+R5D/ta73F3tInO1rBl+o1/gujyu5v9gWSoxvrBb7f8N+4yS7N3rjKpyYi1qQ0YJy6d5yWkM9yIDG8nKlpxH4I= 15 | 'on': 16 | tags: true 17 | - provider: npm 18 | api_key: 19 | secure: >- 20 | GZPWd1vS6FdbLG3usxZnImymG+AS0WdOY5NYz4DNW1UYhmJzOIilSbF0PGqjL0OzbtpeFvRPVEuqvu3LYI623YwsTDg4faycRXMo0OyVjS5vBq4CFF/JBTfkauxUZNq6cpMSg6f55sNshGuS4gGpnKBKkK6Bx8IkQRnEFJ44yfLWmyjPWmaGVEo5aXe6ZStdgLjvXNqL+uIcD57ssn8/nPsezZEAC+S3Kn9fYTqgyt21AaQQ7Hy/QW8JsYNikUlV/3xvueQftd8ILupm8PQ0SYhAgR35aIUbhDk8rEHmezFXGKOovNxDmqnZCAoZyP8WOn81Ey0Z14INZCz/qPBbPgjhhs7Q4O5Pq7SbwCEjqFUkQQ97yvUog28YwsRajl21tC20DXLxknWN06nKtu5EHnmGL9FGwUQt9mVumuJXF2NGHv8KS0EGMGMn9LSdCS89CtMrR/MhkRxCnjikHWxLIxIsDlklyQLY+6Lxc0cXAcLOULTDlOJFLlRCUU8FNxiHJkocFbw+oeVrn4ZzwAhdgAPmWGA+/fxx7cnxs4DSHaEZ3CnAC6rL5vEsKbA0IOjWDER0KbcVTU2OKm6+vZ1W3HP2fl4xbdENYTl78kk2ZXdTi4JtGoBeYX7nNuy+iDcYpLG4Jltna3L3UfvOblTSrXu8dEeTBZOYhryN6eQUi50= 21 | email: vossad01@gmail.com 22 | 'on': 23 | tags: true 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Adam Voss 4 | Copyright (c) Jesse Collis 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Polyglottal JSON Schema Validator (Polyglottal Ajv) 2 | 3 | Command line interface for [ajv](https://github.com/epoberezkin/ajv) that utilizes [any-json](https://github.com/laktak/any-json/) to provide validation against many data formats. `pajv` can validate: **[CSON](https://github.com/bevry/cson), [Hjson](http://hjson.org/), [JSON](http://json.org/), [JSON5](http://json5.org/), [TOML](https://github.com/toml-lang/toml), and [YAML](http://yaml.org/)** files using JSON Schema. pajv is a fork of [ajv-cli](https://github.com/jessedc/ajv-cli). 4 | 5 | [![Build Status](https://travis-ci.org/json-schema-everywhere/pajv.svg?branch=master)](https://travis-ci.org/json-schema-everywhere/pajv) 6 | [![npm version](https://badge.fury.io/js/pajv.svg)](https://www.npmjs.com/package/pajv) 7 | [![Code Climate](https://codeclimate.com/github/json-schema-everywhere/pajv/badges/gpa.svg)](https://codeclimate.com/github/json-schema-everywhere/pajv) 8 | [![Coverage Status](https://coveralls.io/repos/github/json-schema-everywhere/pajv/badge.svg?branch=master)](https://coveralls.io/github/json-schema-everywhere/pajv?branch=master) 9 | 10 | ## Contents 11 | 12 | - [Installation](#installation) 13 | - Commands 14 | - [Help](#help) 15 | - [Validate data](#validate-data) 16 | - [Test validation result](#test-validation-result) 17 | - [Ajv options](#ajv-options) 18 | - [Version History, License](#version_history) 19 | 20 | 21 | ## Installation 22 | 23 | ```sh 24 | npm install -g pajv 25 | ``` 26 | 27 | 28 | ## Help 29 | 30 | ```sh 31 | pajv help 32 | pajv help validate 33 | pajv help test 34 | ``` 35 | 36 | 37 | ## Validate data 38 | 39 | This command validates data files against JSON-schema 40 | 41 | ```sh 42 | pajv validate -s test/schema.json -d test/valid_data.json 43 | pajv -s test/schema.json -d test/valid_data.json 44 | ``` 45 | 46 | You can omit `validate` command name. 47 | 48 | 49 | #### Parameters 50 | 51 | ##### `-s` - file name of JSON-schema 52 | 53 | Only one schema can be passed in this parameter 54 | 55 | 56 | ##### `-d` - JSON data 57 | 58 | Multiple data files can be passed, as in `-r` parameter: 59 | 60 | ```sh 61 | pajv -s test/schema.json -d "test/valid*.json" 62 | ``` 63 | 64 | If some file is invalid exit code will be 1. 65 | 66 | 67 | ##### `-r` - referenced schemas 68 | 69 | The schema in `-s` parameter can reference any of these schemas with `$ref` keyword. 70 | 71 | Multiple schemas can be passed both by using this parameter mupltiple times and with [glob patterns](https://github.com/isaacs/node-glob#glob-primer). Glob pattern should be quoted and extensions cannot be omitted. 72 | 73 | 74 | ##### `-m` - meta-schemas 75 | 76 | Schemas can use any of these schemas as a meta-schema (that is the schema used in `$schema` keyword - it is used to validate the schema itself). 77 | 78 | Multiple meta-schemas can be passed, as in `-r` parameter. 79 | 80 | 81 | ##### `-c` - custom keywords/formats definitions 82 | 83 | You can pass module(s) that define custom keywords/formats. The modules should export a function that accepts Ajv instance as a parameter. The file name should start with ".", it will be resolved relative to the current folder. The package name can also be passed - it will be used in require as is. 84 | 85 | For example, you can use `-c ajv-keywords` to add all keywords from [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package or `-c ajv-keywords/keywords/typeof` to add only typeof keyword. 86 | 87 | 88 | #### Options 89 | 90 | - `--errors=`: error reporting format. Possible values: 91 | - `js` (default): JavaScript object 92 | - `json`: JSON with indentation and line-breaks 93 | - `line`: JSON without indentaion/line-breaks (for easy parsing) 94 | - `text`: human readable error messages with data paths 95 | 96 | - `--changes=`: detect changes in data after validation.
97 | Data can be modifyed with [Ajv options](#ajv-options) `--remove-additional`, `--use-defaults` and `--coerce-types`).
98 | The changes are reported in JSON-patch format ([RFC6902](https://tools.ietf.org/html/rfc6902)).
99 | Possible values are `js` (default), `json` and `line` (see `--errors` option). 100 | 101 | 102 | ## Test validation result 103 | 104 | This command asserts that the result of the validation is as expected. 105 | 106 | ```sh 107 | pajv test -s test/schema.json -d test/valid_data.json --valid 108 | pajv test -s test/schema.json -d test/invalid_data.json --invalid 109 | ``` 110 | 111 | If the option `--valid` (`--invalid`) is used for the `test` to pass (exit code 0) the data file(s) should be valid (invalid). 112 | 113 | This command supports the same options and parameters as [validate](#validate-data) with the exception of `--changes`. 114 | 115 | 116 | ## Ajv options 117 | 118 | You can pass the following Ajv options: 119 | 120 | |Option|Description| 121 | |---|---| 122 | |`--data`|use [$data references](https://github.com/epoberezkin/ajv#data-reference)| 123 | |`--all-errors`|collect all errors| 124 | |`--unknown-formats=`|handling of unknown formats| 125 | |`--verbose`|include schema and data in errors| 126 | |`--json-pointers`|report data paths in errors using JSON-pointers| 127 | |`--unique-items=false`|do not validate uniqueItems keyword| 128 | |`--unicode=false`|count unicode pairs as 2 characters| 129 | |`--format=full`|format mode| 130 | |`--schema-id=`|keyword(s) to use as schema ID| 131 | |`--extend-refs=`|validation of other keywords when $ref is present in the schema| 132 | |`--missing-refs=`|handle missing referenced schemas (true/ignore/fail)| 133 | |`--inline-refs=`|referenced schemas compilation mode (true/false/\)| 134 | |`--remove-additional`|remove additional properties (true/all/failing)| 135 | |`--use-defaults`|replace missing properties/items with the values from default keyword| 136 | |`--coerce-types`|change type of data to match type keyword| 137 | |`--multiple-of-precision`|precision of multipleOf, pass integer number| 138 | |`--error-data-path=property`|data path in errors| 139 | |`--messages=false`|do not include text messages in errors| 140 | 141 | Options can be passed in either dash-case and camelCase. 142 | 143 | See [Ajv Options](https://github.com/epoberezkin/ajv#options) for more information. 144 | 145 | 146 | ## Version History 147 | 148 | See https://github.com/json-schema-everywhere/pajv/releases 149 | 150 | 151 | ## Licence 152 | 153 | MIT 154 | -------------------------------------------------------------------------------- /commands/ajv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Ajv = require('ajv'); 4 | var options = require('./options'); 5 | var util = require('./util'); 6 | var path = require('path'); 7 | 8 | 9 | module.exports = function (argv) { 10 | var opts = options.get(argv); 11 | if (argv.o) opts.sourceCode = true; 12 | var ajv = new Ajv(opts); 13 | var invalid; 14 | ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); 15 | addSchemas(argv.m, 'addMetaSchema', 'meta-schema'); 16 | addSchemas(argv.r, 'addSchema', 'schema'); 17 | customFormatsKeywords(argv.c); 18 | if (invalid) process.exit(1); 19 | return ajv; 20 | 21 | function addSchemas(args, method, fileType) { 22 | if (!args) return; 23 | var files = util.getFiles(args); 24 | files.forEach(function (file) { 25 | var schema = util.openFile(file, fileType); 26 | try { ajv[method](schema); } 27 | catch (err) { 28 | console.error(fileType, file, 'is invalid'); 29 | console.error('error:', err.message); 30 | invalid = true; 31 | } 32 | }); 33 | } 34 | 35 | function customFormatsKeywords(args) { 36 | if (!args) return; 37 | var files = util.getFiles(args); 38 | files.forEach(function (file) { 39 | if (file[0] == '.') file = path.resolve(process.cwd(), file); 40 | try { 41 | require(file)(ajv); 42 | } catch (err) { 43 | console.error('module', file, 'is invalid; it should export function'); 44 | console.error('error:', err.message); 45 | invalid = true; 46 | } 47 | }); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /commands/help.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | execute: execute, 5 | usage: usage, 6 | schema: { 7 | type: 'object', 8 | properties: { 9 | _: { maxItems: 2 } 10 | }, 11 | _ajvOptions: false 12 | } 13 | }; 14 | 15 | 16 | var commands = { 17 | validate: helpValidate, 18 | test: helpTest 19 | }; 20 | 21 | 22 | function execute(argv) { 23 | var command = argv._[1]; 24 | if (!command || command == 'help') { 25 | mainHelp(); 26 | return true; 27 | } 28 | 29 | var cmdHelp = commands[command]; 30 | 31 | if (cmdHelp) { 32 | cmdHelp(); 33 | return true; 34 | } 35 | 36 | console.error('Unknown command', command); 37 | usage(); 38 | } 39 | 40 | 41 | function usage() { 42 | console.error('\ 43 | usage:\n\ 44 | validate: pajv [validate] -s schema_file -d data_file\n\ 45 | test: pajv test -s schema_file -d data_file --[in]valid\n\ 46 | \n\ 47 | help: pajv help\n\ 48 | pajv help '); 49 | } 50 | 51 | 52 | function mainHelp() { 53 | _helpValidate(); 54 | _helpTest(); 55 | console.log('\ 56 | More information:\n\ 57 | pajv help validate\n\ 58 | pajv help test'); 59 | } 60 | 61 | 62 | function helpValidate() { 63 | _helpValidate(); 64 | console.log('\ 65 | parameters\n\ 66 | -s JSON schema to validate against (required, only one schema allowed)\n\ 67 | -d data file(s) to be validated (required)\n\ 68 | -r referenced schema(s)\n\ 69 | -m meta schema(s)\n\ 70 | -c custom keywords/formats definitions\n\ 71 | \n\ 72 | -d, -r, -m, -c can be globs and can be used multiple times\n\ 73 | glob should be enclosed in double quotes\n\ 74 | -c module(s) should export a function that accepts Ajv instance as parameter\n\ 75 | (file path should start with ".", otherwise used as require package)\n\ 76 | \n\ 77 | options:\n\ 78 | --errors= error reporting format ("js" by deafult)\n\ 79 | --changes= log changes in data after validation ("no" by default)\n\ 80 | js JavaScript object\n\ 81 | json JSON format\n\ 82 | line JSON single line\n\ 83 | text text message (only for --errors option)\n\ 84 | no don\'t log errors'); 85 | helpAjvOptions(); 86 | } 87 | 88 | 89 | function _helpValidate() { 90 | console.log('\ 91 | Validate data file(s) against schema\n\ 92 | pajv [validate] -s schema_file -d data_file\n\ 93 | pajv [validate] -s schema_file -d "data*.ext"\n'); 94 | } 95 | 96 | function helpTest() { 97 | _helpTest(); 98 | console.log('\ 99 | parameters\n\ 100 | -s JSON schema to validate against (required, only one schema allowed)\n\ 101 | -d data file(s) to be validated (required)\n\ 102 | -r referenced schema(s)\n\ 103 | -m meta schema(s)\n\ 104 | -c custom keywords/formats definitions\n\ 105 | --valid/--invalid data file(s) must be valid/invalid for this command to succeed\n\ 106 | \n\ 107 | -d, -r, -m, -c can be globs and can be used multiple times\n\ 108 | glob should be enclosed in double quotes\n\ 109 | -c module(s) should export a function that accepts Ajv instance as parameter\n\ 110 | (file path should start with ".", otherwise used as require package)\n\ 111 | --valid=false can be used instead of --invalid\n\ 112 | \n\ 113 | options:\n\ 114 | --errors= error reporting\n\ 115 | js JavaScript object (default)\n\ 116 | json JSON format\n\ 117 | line JSON single line\n\ 118 | text text message\n'); 119 | helpAjvOptions(); 120 | } 121 | 122 | 123 | function _helpTest() { 124 | console.log('\ 125 | Test data validation result\n\ 126 | pajv test -s schema_file -d data_file --valid\n\ 127 | pajv test -s schema_file -d data_file --invalid\n\ 128 | pajv test -s schema_file -d "data*.ext" --valid \n'); 129 | } 130 | 131 | 132 | function helpAjvOptions() { 133 | console.log('\ 134 | Ajv options (see https://github.com/epoberezkin/ajv#options):\n\ 135 | --data use $data references\n\ 136 | \n\ 137 | --all-errors collect all errors\n\ 138 | \n\ 139 | --unknown-formats= handling of unknown formats\n\ 140 | true throw exception during schema compilation (default)\n\ 141 | allowed unknown format name, multiple names can be used\n\ 142 | \n\ 143 | --json-pointers report data paths as JSON pointers\n\ 144 | \n\ 145 | --unique-items=false do not validate uniqueItems keyword\n\ 146 | \n\ 147 | --unicode=false count unicode pairs as 2 characters\n\ 148 | \n\ 149 | --format= format validation mode\n\ 150 | fast using regex (default)\n\ 151 | full using functions\n\ 152 | \n\ 153 | --schema-id= (by default both IDs will be used)\n\ 154 | $id use $id\n\ 155 | id use id\n\ 156 | \n\ 157 | --extend-refs= validation of other keywords when $ref is present in the schema\n\ 158 | ignore ignore other keywords (default)\n\ 159 | fail throw exception (recommended)\n\ 160 | true validate all keywords\n\ 161 | \n\ 162 | --missing-refs= handling missing referenced schemas\n\ 163 | true fail schema compilation (default)\n\ 164 | ignore log error and pass validation\n\ 165 | fail log error and fail validation if ref is used\n\ 166 | \n\ 167 | --remove-additional= remove additional properties\n\ 168 | all remove all additional properties\n\ 169 | true remove if additionalProperties is false\n\ 170 | failing also remove if fails validation of schema in additionalProperties\n\ 171 | \n\ 172 | --use-defaults replace missing properties/items with the values from default keyword\n\ 173 | \n\ 174 | --coerce-types change type of data to match type keyword\n\ 175 | \n\ 176 | --multiple-of-precision=N pass integer number\n\ 177 | \n\ 178 | --error-data-path= data path in errors of required, additionalProperties and dependencies\n\ 179 | object point to object (default)\n\ 180 | property point to property\n\ 181 | \n\ 182 | --messages=false do not include text messages in errors'); 183 | } 184 | -------------------------------------------------------------------------------- /commands/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | help: require('./help'), 5 | validate: require('./validate'), 6 | test: require('./test') 7 | }; 8 | -------------------------------------------------------------------------------- /commands/options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Ajv = require('ajv'); 4 | var glob = require('glob'); 5 | var ajv = new Ajv({ 6 | allErrors: true, 7 | coerceTypes: 'array', 8 | jsonPointers: true, 9 | formats: { 10 | notGlob: function(s) { return !glob.hasMagic(s); } 11 | } 12 | }); 13 | 14 | var AJV_OPTIONS = { 15 | 'data': { type: 'boolean' }, 16 | 'all-errors': { type: 'boolean' }, 17 | 'verbose': { type: 'boolean' }, 18 | 'json-pointers': { type: 'boolean' }, 19 | 'unique-items': { type: 'boolean' }, 20 | 'unicode': { type: 'boolean' }, 21 | 'format': { anyOf: [ 22 | { type: 'boolean' }, 23 | { enum: ['fast', 'full'] } 24 | ] }, 25 | 'unknown-formats': { anyOf: [ 26 | { type: 'boolean' }, 27 | { const: 'ignore' }, 28 | { type: 'array', items: { type: 'string' } } 29 | ] }, 30 | 'schema-id': { enum: ['$id', 'id'] }, 31 | 'extend-refs': { anyOf: [ 32 | { type: 'boolean' }, 33 | { enum: ['ignore', 'fail'] } 34 | ] }, 35 | 'missing-refs': { anyOf: [ 36 | { type: 'boolean' }, 37 | { enum: ['ignore', 'fail'] } 38 | ] }, 39 | 'inline-refs': { type: ['boolean', 'integer'], minimum: 0 }, 40 | 'multiple-of-precision': { type: 'integer' }, 41 | 'error-data-path': { enum: ['object', 'property'] }, 42 | 'messages': { type: 'boolean' }, 43 | // modifying options 44 | 'remove-additional': { anyOf: [ 45 | { type: 'boolean' }, 46 | { enum: ['all', 'failing'] } 47 | ] }, 48 | 'use-defaults': { type: 'boolean' }, 49 | 'coerce-types': { anyOf: [ 50 | { type: 'boolean' }, 51 | { enum: ['array'] } 52 | ] } 53 | }; 54 | 55 | 56 | module.exports = { 57 | check: checkOptions, 58 | get: getOptions 59 | }; 60 | 61 | 62 | var DEFINITIONS = { 63 | stringOrArray: { 64 | anyOf: [ 65 | { type: 'string' }, 66 | { 67 | type: 'array', 68 | items: { type: 'string' } 69 | } 70 | ] 71 | } 72 | }; 73 | 74 | function checkOptions(schema, argv) { 75 | schema.definitions = DEFINITIONS; 76 | if (schema._ajvOptions !== false) { 77 | for (var opt in AJV_OPTIONS) { 78 | var optSchema = AJV_OPTIONS[opt]; 79 | schema.properties[opt] = optSchema; 80 | schema.properties[toCamelCase(opt)] = optSchema; 81 | } 82 | } 83 | schema.properties._ = schema.properties._ || { maxItems: 1 }; 84 | schema.additionalProperties = false; 85 | 86 | var valid = ajv.validate(schema, argv); 87 | if (valid) return null; 88 | var errors = ''; 89 | ajv.errors.forEach(function (err) { 90 | errors += 'error: '; 91 | switch (err.keyword) { 92 | case 'required': 93 | errors += 'parameter ' + parameter(err.params.missingProperty) + ' is required'; 94 | break; 95 | case 'additionalProperties': 96 | errors += 'parameter ' + parameter(err.params.additionalProperty) + ' is unknown'; 97 | break; 98 | case 'maxItems': 99 | errors += 'invalid syntax (too many arguments)'; 100 | break; 101 | case 'format': 102 | if (err.params.format == 'notGlob') { 103 | errors += 'only one file is allowed in parameter ' + parameter(err.dataPath.slice(1)); 104 | break; 105 | } 106 | // falls through 107 | default: 108 | errors += 'parameter ' + parameter(err.dataPath.slice(1)) + ' ' + err.message; 109 | } 110 | errors += '\n'; 111 | }); 112 | 113 | return errors; 114 | } 115 | 116 | 117 | function parameter(str) { 118 | return (str.length == 1 ? '-' : '--') + str; 119 | } 120 | 121 | 122 | function getOptions(argv) { 123 | var options = {}; 124 | for (var opt in AJV_OPTIONS) { 125 | var optCC = toCamelCase(opt); 126 | var value = argv[opt] === undefined ? argv[optCC] : argv[opt]; 127 | if (value !== undefined) options[optCC] = value; 128 | } 129 | return options; 130 | } 131 | 132 | 133 | function toCamelCase(str) { 134 | return str.replace(/-[a-z]/g, function (s) { 135 | return s[1].toUpperCase(); 136 | }); 137 | } 138 | -------------------------------------------------------------------------------- /commands/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('./util'); 4 | var getAjv = require('./ajv'); 5 | 6 | 7 | module.exports = { 8 | execute: execute, 9 | schema: { 10 | type: 'object', 11 | required: ['s', 'd'], 12 | oneOf: [ 13 | { required: [ 'valid' ] }, 14 | { required: [ 'invalid' ] } 15 | ], 16 | properties: { 17 | s: { 18 | type: 'string', 19 | format: 'notGlob' 20 | }, 21 | d: { $ref: '#/definitions/stringOrArray' }, 22 | r: { $ref: '#/definitions/stringOrArray' }, 23 | m: { $ref: '#/definitions/stringOrArray' }, 24 | c: { $ref: '#/definitions/stringOrArray' }, 25 | valid: { type: 'boolean' }, 26 | invalid: { type: 'boolean', enum: [true] }, 27 | errors: { enum: ['json', 'line', 'text', 'js', 'no'] } 28 | } 29 | } 30 | }; 31 | 32 | 33 | function execute(argv) { 34 | var ajv = getAjv(argv); 35 | var validate = util.compile(ajv, argv.s); 36 | var shouldBeValid = !!argv.valid && argv.valid != 'false'; 37 | var allPassed = true; 38 | 39 | var dataFiles = util.getFiles(argv.d); 40 | dataFiles.forEach(testDataFile); 41 | 42 | return allPassed; 43 | 44 | 45 | function testDataFile(file) { 46 | var data = util.openFile(file, 'data file ' + file); 47 | var validData = validate(data); 48 | var errors; 49 | if (!validData) errors = util.logJSON(argv.errors, validate.errors, ajv); 50 | 51 | if (validData === shouldBeValid) { 52 | console.log(file, 'passed test'); 53 | if (errors) console.log(errors); 54 | } else { 55 | allPassed = false; 56 | console.error(file, 'failed test'); 57 | if (errors) console.error(errors); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /commands/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var anyjson = require('any-json'); 4 | var glob = require('glob'); 5 | var path = require('path'); 6 | var fs = require('fs'); 7 | var wait = require('deasync-promise'); 8 | 9 | 10 | module.exports = { 11 | getFiles: getFiles, 12 | openFile: openFile, 13 | logJSON: logJSON, 14 | compile: compile 15 | }; 16 | 17 | 18 | function getFiles(args) { 19 | var files = []; 20 | if (Array.isArray(args)) args.forEach(_getFiles); 21 | else _getFiles(args); 22 | return files; 23 | 24 | function _getFiles(fileOrPattern) { 25 | if (glob.hasMagic(fileOrPattern)) { 26 | var dataFiles = glob.sync(fileOrPattern, { cwd: process.cwd() }); 27 | files = files.concat(dataFiles); 28 | } else { 29 | files.push(fileOrPattern); 30 | } 31 | } 32 | } 33 | 34 | function getFormatFromFileName(filename) { 35 | var format = path.extname(filename).substr(1).toLowerCase(); 36 | return format === 'yml' ? 'yaml' : format; 37 | } 38 | 39 | 40 | function openFile(filename, suffix){ 41 | var file = path.resolve(process.cwd(), filename); 42 | try { 43 | var contents = fs.readFileSync(file).toString(); 44 | var format = getFormatFromFileName(filename); 45 | return wait(anyjson.decode(contents, format)); 46 | } catch(err) { 47 | console.error('error: ' + err.message.replace(' module', ' ' + suffix)); 48 | process.exit(2); 49 | } 50 | } 51 | 52 | 53 | function logJSON(mode, data, ajv) { 54 | switch (mode) { 55 | case 'json': data = JSON.stringify(data, null, ' '); break; 56 | case 'line': data = JSON.stringify(data); break; 57 | case 'no': data = ''; break; 58 | case 'text': if (ajv) data = ajv.errorsText(data); 59 | } 60 | return data; 61 | } 62 | 63 | 64 | function compile(ajv, schemaFile) { 65 | var schema = openFile(schemaFile, 'schema'); 66 | try { return ajv.compile(schema); } 67 | catch (err) { 68 | console.error('schema', schemaFile, 'is invalid'); 69 | console.error('error:', err.message); 70 | process.exit(1); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /commands/validate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('./util'); 4 | var getAjv = require('./ajv'); 5 | var jsonPatch = require('fast-json-patch'); 6 | 7 | 8 | module.exports = { 9 | execute: execute, 10 | schema: { 11 | type: 'object', 12 | required: ['s', 'd'], 13 | properties: { 14 | s: { 15 | type: 'string', 16 | format: 'notGlob' 17 | }, 18 | d: { $ref: '#/definitions/stringOrArray' }, 19 | r: { $ref: '#/definitions/stringOrArray' }, 20 | m: { $ref: '#/definitions/stringOrArray' }, 21 | c: { $ref: '#/definitions/stringOrArray' }, 22 | errors: { enum: ['json', 'line', 'text', 'js', 'no'] }, 23 | changes: { enum: [ true, 'json', 'line', 'js' ] } 24 | } 25 | } 26 | }; 27 | 28 | 29 | function execute(argv) { 30 | var ajv = getAjv(argv); 31 | var validate = util.compile(ajv, argv.s); 32 | var allValid = true; 33 | 34 | var dataFiles = util.getFiles(argv.d); 35 | dataFiles.forEach(validateDataFile); 36 | 37 | return allValid; 38 | 39 | 40 | function validateDataFile(file) { 41 | var data = util.openFile(file, 'data file ' + file); 42 | var original; 43 | if (argv.changes) original = JSON.parse(JSON.stringify(data)); 44 | var validData = validate(data); 45 | 46 | if (validData) { 47 | console.log(file, 'valid'); 48 | if (argv.changes) { 49 | var patch = jsonPatch.compare(original, data); 50 | if (patch.length == 0) { 51 | console.log('no changes'); 52 | } else { 53 | console.log('changes:'); 54 | console.log(util.logJSON(argv.changes, patch)); 55 | } 56 | } 57 | } else { 58 | allValid = false; 59 | console.error(file, 'invalid'); 60 | console.error(util.logJSON(argv.errors, validate.errors, ajv)); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 'use strict'; 3 | 4 | var argv = require('minimist')(process.argv.slice(2)); 5 | var commands = require('./commands'); 6 | var options = require('./commands/options'); 7 | 8 | var command = argv._[0] || 'validate'; 9 | var cmd = commands[command]; 10 | 11 | if (cmd) { 12 | var errors = options.check(cmd.schema, argv); 13 | if (errors) { 14 | console.error(errors); 15 | commands.help.usage(); 16 | process.exit(2); 17 | } else { 18 | var ok = cmd.execute(argv); 19 | process.exit(ok ? 0 : 1); 20 | } 21 | } else { 22 | console.error('Unknown command', command); 23 | commands.help.usage(); 24 | process.exit(2); 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pajv", 3 | "displayName": "Polyglottal JSON Schema Validator", 4 | "version": "1.2.0", 5 | "description": "A command line JSON Schema validator that supports many file formats. Fork of jessedc/ajv-cli.", 6 | "scripts": { 7 | "eslint": "eslint index.js commands/*.js test/*.js test/**/*.js", 8 | "test-spec": "mocha test/*.spec.js -R spec", 9 | "test-cov": "nyc npm run test-spec", 10 | "test": "npm run eslint && npm run test-cov" 11 | }, 12 | "nyc": { 13 | "exclude": [ 14 | "test", 15 | "node_modules" 16 | ], 17 | "reporter": [ 18 | "lcov", 19 | "text-summary" 20 | ], 21 | "cache": true 22 | }, 23 | "bin": { 24 | "pajv": "index.js" 25 | }, 26 | "preferGlobal": true, 27 | "keywords": [ 28 | "JSON", 29 | "schema", 30 | "validator", 31 | "validation", 32 | "jsonschema", 33 | "json-schema", 34 | "json-schema-validator", 35 | "json-schema-validation", 36 | "cson", 37 | "hjson", 38 | "ini", 39 | "json5", 40 | "toml", 41 | "xml", 42 | "yaml" 43 | ], 44 | "author": "Adam Voss", 45 | "contributors": [ 46 | { 47 | "name": "Jesse Collis", 48 | "email": "jesse@jcmultimedia.com.au" 49 | } 50 | ], 51 | "license": "MIT", 52 | "repository": { 53 | "type": "git", 54 | "url": "https://github.com/json-schema-everywhere/pajv" 55 | }, 56 | "dependencies": { 57 | "ajv": "^5.0.0", 58 | "any-json": "^3.0.0", 59 | "deasync": "^0.1.10", 60 | "deasync-promise": "^1.0.1", 61 | "fast-json-patch": "^0.5.6", 62 | "glob": "^7.0.3", 63 | "minimist": "^1.2.0", 64 | "util.promisify": "^1.0.0" 65 | }, 66 | "devDependencies": { 67 | "ajv-keywords": "^2.0.0", 68 | "coveralls": "^2.11.8", 69 | "eslint": "^2.4.0", 70 | "mocha": "^2.4.5", 71 | "nyc": "^8.3.0", 72 | "pre-commit": "^1.1.2" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | indent: [2, 2, { SwitchCase : 1 } ] 4 | no-invalid-this: 0 5 | no-empty: [2, allowEmptyCatch: true] 6 | 7 | globals: 8 | describe: false 9 | it: false 10 | beforeEach: false 11 | -------------------------------------------------------------------------------- /test/cli.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exec = require('child_process').exec; 4 | var path = require('path'); 5 | var CWD = path.join(__dirname, '..'); 6 | 7 | 8 | module.exports = function cli(params, callback) { 9 | exec('node index ' + params, { cwd: CWD }, callback); 10 | }; 11 | -------------------------------------------------------------------------------- /test/custom/invalid_custom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /test/custom/invalid_data.json: -------------------------------------------------------------------------------- 1 | "foo" -------------------------------------------------------------------------------- /test/custom/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "id": "schema.json", 4 | "typeof": "number" 5 | } 6 | -------------------------------------------------------------------------------- /test/custom/typeof.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ajvKeywords = require('ajv-keywords'); 4 | 5 | module.exports = function (ajv) { 6 | ajvKeywords(ajv, 'typeof'); 7 | }; 8 | -------------------------------------------------------------------------------- /test/custom/valid_data.json: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /test/data_with_additional.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 2, 4 | "name": "An ice sculpture", 5 | "price": 12.50, 6 | "tags": ["cold", "ice"], 7 | "dimensions": { 8 | "length": 7.0, 9 | "width": 12.0, 10 | "height": 9.5 11 | }, 12 | "warehouseLocation": { 13 | "latitude": -78.75, 14 | "longitude": 20.4 15 | }, 16 | "additionalInfo": "Store in the cold" 17 | }, 18 | { 19 | "id": 3, 20 | "name": "A blue mouse", 21 | "price": 25.50, 22 | "dimensions": { 23 | "length": 3.1, 24 | "width": 1.0, 25 | "height": 1.0 26 | }, 27 | "warehouseLocation": { 28 | "latitude": 54.4, 29 | "longitude": -32.7 30 | }, 31 | "additionalInfo": "Feed every hour" 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /test/help.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cli = require('./cli'); 4 | var assert = require('assert'); 5 | 6 | 7 | describe('help', function() { 8 | this.timeout(10000); 9 | 10 | it('should print help', function (done) { 11 | cli('help', function (error, stdout, stderr) { 12 | assert.strictEqual(error, null); 13 | assert(/Validate/.test(stdout)); 14 | assert(/Test/.test(stdout)); 15 | assert.equal(stderr, ''); 16 | done(); 17 | }); 18 | }); 19 | 20 | it('should print help for validate', function (done) { 21 | cli('help validate', function (error, stdout, stderr) { 22 | assert.strictEqual(error, null); 23 | assert(/Validate/.test(stdout)); 24 | assert(/options/.test(stdout)); 25 | assert.equal(stderr, ''); 26 | done(); 27 | }); 28 | }); 29 | 30 | it('should print help for test', function (done) { 31 | cli('help test', function (error, stdout, stderr) { 32 | assert.strictEqual(error, null); 33 | assert(/Test/.test(stdout)); 34 | assert(/options/.test(stdout)); 35 | assert.equal(stderr, ''); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should print usage if unknown command is used', function (done) { 41 | cli('unknown', function (error, stdout, stderr) { 42 | assert(error instanceof Error); 43 | assert.equal(stdout, ''); 44 | assert(/command/.test(stderr)); 45 | assert(/unknown/.test(stderr)); 46 | assert(/usage/.test(stderr)); 47 | done(); 48 | }); 49 | }); 50 | 51 | it('should print usage if help command is unknown', function (done) { 52 | cli('help unknown', function (error, stdout, stderr) { 53 | assert(error instanceof Error); 54 | assert.equal(stdout, ''); 55 | assert(/command/.test(stderr)); 56 | assert(/unknown/.test(stderr)); 57 | assert(/usage/.test(stderr)); 58 | done(); 59 | }); 60 | }); 61 | 62 | it('should print usage if syntax is invalid', function (done) { 63 | cli('help -s test/schema.json', function (error, stdout, stderr) { 64 | assert(error instanceof Error); 65 | assert.equal(stdout, ''); 66 | assert(/usage/.test(stderr)); 67 | assert(/parameter/.test(stderr)); 68 | assert(/unknown/.test(stderr)); 69 | done(); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/invalid_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "A blue mouse", 5 | "price": 25.50, 6 | "dimensions": { 7 | "length": 3.1, 8 | "width": 1.0 9 | }, 10 | "warehouseLocation": { 11 | "latitude": 54.4, 12 | "longitude": -32.7 13 | } 14 | }, 15 | { 16 | "id": 2, 17 | "name": "An ice sculpture", 18 | "price": 12.50, 19 | "tags": ["cold", "ice"], 20 | "dimensions": { 21 | "length": 7.0, 22 | "width": 12.0, 23 | "height": "9.5" 24 | }, 25 | "warehouseLocation": { 26 | "latitude": -78.75, 27 | "longitude": 20.4 28 | } 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /test/invalid_data2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "A blue mouse", 5 | "price": 25.50, 6 | "dimensions": { 7 | "length": 3.1, 8 | "width": 1.0 9 | }, 10 | "warehouseLocation": { 11 | "latitude": 54.4, 12 | "longitude": -32.7 13 | } 14 | }, 15 | { 16 | "id": 2, 17 | "name": "An ice sculpture", 18 | "price": 12.50, 19 | "tags": ["cold", "ice"], 20 | "dimensions": { 21 | "length": 7.0, 22 | "width": 12.0, 23 | "height": "9.5" 24 | }, 25 | "warehouseLocation": { 26 | "latitude": -78.75, 27 | "longitude": 20.4 28 | } 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /test/meta/invalid_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar" 3 | } 4 | -------------------------------------------------------------------------------- /test/meta/invalid_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://example.com/my_meta_schema#", 3 | "type": "object", 4 | "properties": { 5 | "foo": { 6 | "my_keyword": 1, 7 | "type": "integer" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/meta/invalid_schema2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": false 3 | } 4 | -------------------------------------------------------------------------------- /test/meta/meta_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "$id": "http://example.com/my_meta_schema#", 4 | "title": "Core schema meta-schema", 5 | "definitions": { 6 | "schemaArray": { 7 | "type": "array", 8 | "minItems": 1, 9 | "items": { "$ref": "#" } 10 | }, 11 | "nonNegativeInteger": { 12 | "type": "integer", 13 | "minimum": 0 14 | }, 15 | "nonNegativeIntegerDefault0": { 16 | "allOf": [ 17 | { "$ref": "#/definitions/nonNegativeInteger" }, 18 | { "default": 0 } 19 | ] 20 | }, 21 | "simpleTypes": { 22 | "enum": [ 23 | "array", 24 | "boolean", 25 | "integer", 26 | "null", 27 | "number", 28 | "object", 29 | "string" 30 | ] 31 | }, 32 | "stringArray": { 33 | "type": "array", 34 | "items": { "type": "string" }, 35 | "uniqueItems": true, 36 | "default": [] 37 | } 38 | }, 39 | "type": ["object", "boolean"], 40 | "properties": { 41 | "$id": { 42 | "type": "string", 43 | "format": "uri-reference" 44 | }, 45 | "$schema": { 46 | "type": "string", 47 | "format": "uri" 48 | }, 49 | "$ref": { 50 | "type": "string", 51 | "format": "uri-reference" 52 | }, 53 | "title": { 54 | "type": "string" 55 | }, 56 | "description": { 57 | "type": "string" 58 | }, 59 | "default": {}, 60 | "multipleOf": { 61 | "type": "number", 62 | "exclusiveMinimum": 0 63 | }, 64 | "maximum": { 65 | "type": "number" 66 | }, 67 | "exclusiveMaximum": { 68 | "type": "number" 69 | }, 70 | "minimum": { 71 | "type": "number" 72 | }, 73 | "exclusiveMinimum": { 74 | "type": "number" 75 | }, 76 | "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, 77 | "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, 78 | "pattern": { 79 | "type": "string", 80 | "format": "regex" 81 | }, 82 | "additionalItems": { "$ref": "#" }, 83 | "items": { 84 | "anyOf": [ 85 | { "$ref": "#" }, 86 | { "$ref": "#/definitions/schemaArray" } 87 | ], 88 | "default": {} 89 | }, 90 | "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, 91 | "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, 92 | "uniqueItems": { 93 | "type": "boolean", 94 | "default": false 95 | }, 96 | "contains": { "$ref": "#" }, 97 | "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, 98 | "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, 99 | "required": { "$ref": "#/definitions/stringArray" }, 100 | "additionalProperties": { "$ref": "#" }, 101 | "definitions": { 102 | "type": "object", 103 | "additionalProperties": { "$ref": "#" }, 104 | "default": {} 105 | }, 106 | "properties": { 107 | "type": "object", 108 | "additionalProperties": { "$ref": "#" }, 109 | "default": {} 110 | }, 111 | "patternProperties": { 112 | "type": "object", 113 | "additionalProperties": { "$ref": "#" }, 114 | "default": {} 115 | }, 116 | "dependencies": { 117 | "type": "object", 118 | "additionalProperties": { 119 | "anyOf": [ 120 | { "$ref": "#" }, 121 | { "$ref": "#/definitions/stringArray" } 122 | ] 123 | } 124 | }, 125 | "propertyNames": { "$ref": "#" }, 126 | "const": {}, 127 | "enum": { 128 | "type": "array", 129 | "minItems": 1, 130 | "uniqueItems": true 131 | }, 132 | "type": { 133 | "anyOf": [ 134 | { "$ref": "#/definitions/simpleTypes" }, 135 | { 136 | "type": "array", 137 | "items": { "$ref": "#/definitions/simpleTypes" }, 138 | "minItems": 1, 139 | "uniqueItems": true 140 | } 141 | ] 142 | }, 143 | "format": { "type": "string" }, 144 | "allOf": { "$ref": "#/definitions/schemaArray" }, 145 | "anyOf": { "$ref": "#/definitions/schemaArray" }, 146 | "oneOf": { "$ref": "#/definitions/schemaArray" }, 147 | "not": { "$ref": "#" }, 148 | "my_keyword": { "type": "boolean" } 149 | }, 150 | "default": {} 151 | } 152 | -------------------------------------------------------------------------------- /test/meta/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://example.com/my_meta_schema#", 3 | "type": "object", 4 | "properties": { 5 | "foo": { 6 | "my_keyword": true, 7 | "type": "integer" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/meta/valid_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": 1 3 | } 4 | -------------------------------------------------------------------------------- /test/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "$id": "schema.json", 4 | "description": "basic schema from z-schema benchmark (https://github.com/zaggino/z-schema)", 5 | "title": "Product set", 6 | "type": "array", 7 | "items": { 8 | "title": "Product", 9 | "type": "object", 10 | "additionalProperties": false, 11 | "properties": { 12 | "id": { 13 | "description": "The unique identifier for a product", 14 | "type": "number" 15 | }, 16 | "name": { 17 | "type": "string" 18 | }, 19 | "price": { 20 | "type": "number", 21 | "exclusiveMinimum": 0 22 | }, 23 | "tags": { 24 | "type": "array", 25 | "items": { 26 | "type": "string" 27 | }, 28 | "minItems": 1, 29 | "uniqueItems": true 30 | }, 31 | "dimensions": { 32 | "type": "object", 33 | "properties": { 34 | "length": {"type": "number"}, 35 | "width": {"type": "number"}, 36 | "height": {"type": "number"} 37 | }, 38 | "required": ["length", "width", "height"] 39 | }, 40 | "warehouseLocation": { 41 | "description": "Coordinates of the warehouse with the product" 42 | } 43 | }, 44 | "required": ["id", "name", "price"] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/schema_with_ref.json: -------------------------------------------------------------------------------- 1 | { 2 | "$ref": "schema.json" 3 | } 4 | -------------------------------------------------------------------------------- /test/test.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cli = require('./cli'); 4 | var assert = require('assert'); 5 | 6 | 7 | describe('test', function() { 8 | this.timeout(10000); 9 | 10 | describe('test valid data', function() { 11 | it('should pass if expected result is valid', function (done) { 12 | cli('test -s test/schema.json -d test/valid_data.json --valid', function (error, stdout, stderr) { 13 | assert.strictEqual(error, null); 14 | assertNoErrors(stdout, 1, /\spassed/); 15 | assert.equal(stderr, ''); 16 | done(); 17 | }); 18 | }); 19 | 20 | it('should pass multiple files if expected result is valid', function (done) { 21 | cli('test -s test/schema.json -d "test/valid*.json" --valid', function (error, stdout, stderr) { 22 | assert.strictEqual(error, null); 23 | assertNoErrors(stdout, 2, /\spassed/); 24 | assert.equal(stderr, ''); 25 | done(); 26 | }); 27 | }); 28 | 29 | it('should fail if expected result is invalid', function (done) { 30 | cli('test -s test/schema.json -d test/valid_data.json --invalid', function (error, stdout, stderr) { 31 | assert(error instanceof Error); 32 | assertNoErrors(stderr, 1, /\sfailed/); 33 | assert.equal(stdout, ''); 34 | done(); 35 | }); 36 | }); 37 | 38 | it('should fail multiple files if expected result is invalid', function (done) { 39 | cli('test -s test/schema.json -d "test/valid*.json" --invalid', function (error, stdout, stderr) { 40 | assert(error instanceof Error); 41 | assertNoErrors(stderr, 2, /\sfailed/); 42 | assert.equal(stdout, ''); 43 | done(); 44 | }); 45 | }); 46 | }); 47 | 48 | 49 | describe('test invalid data', function() { 50 | it('should pass if expected result is invalid', function (done) { 51 | cli('test -s test/schema.json -d test/invalid_data.json --invalid --errors=line', function (error, stdout, stderr) { 52 | assert.strictEqual(error, null); 53 | assertRequiredErrors(stdout, 1, /\spassed/); 54 | assert.equal(stderr, ''); 55 | done(); 56 | }); 57 | }); 58 | 59 | it('should pass if expected result is invalid (valid=false)', function (done) { 60 | cli('test -s test/schema.json -d test/invalid_data.json --valid=false --errors=line', function (error, stdout, stderr) { 61 | assert.strictEqual(error, null); 62 | assertRequiredErrors(stdout, 1, /\spassed/); 63 | assert.equal(stderr, ''); 64 | done(); 65 | }); 66 | }); 67 | 68 | 69 | it('should pass multiple files if expected result is invalid', function (done) { 70 | cli('test -s test/schema.json -d "test/invalid*.json" --invalid --errors=line', function (error, stdout, stderr) { 71 | assert.strictEqual(error, null); 72 | assertRequiredErrors(stdout, 2, /\spassed/); 73 | assert.equal(stderr, ''); 74 | done(); 75 | }); 76 | }); 77 | 78 | it('should fail if expected result is valid', function (done) { 79 | cli('test -s test/schema.json -d test/invalid_data.json --valid --errors=line', function (error, stdout, stderr) { 80 | assert(error instanceof Error); 81 | assertRequiredErrors(stderr, 1, /\sfailed/); 82 | assert.equal(stdout, ''); 83 | done(); 84 | }); 85 | }); 86 | 87 | it('should fail multiple files if expected result is valid', function (done) { 88 | cli('test -s test/schema.json -d "test/invalid*.json" --valid --errors=line', function (error, stdout, stderr) { 89 | assert(error instanceof Error); 90 | assertRequiredErrors(stderr, 2, /\sfailed/); 91 | assert.equal(stdout, ''); 92 | done(); 93 | }); 94 | }); 95 | }); 96 | 97 | 98 | describe('test valid and invalid data', function() { 99 | it('should pass valid, fail invalid and return error if expected result is valid', function (done) { 100 | cli('test -s test/schema.json -d test/valid_data.json -d test/invalid_data.json --valid --errors=line', function (error, stdout, stderr) { 101 | assert(error instanceof Error); 102 | assertNoErrors(stdout, 1, /\spassed/); 103 | assertRequiredErrors(stderr, 1, /\sfailed/); 104 | done(); 105 | }); 106 | }); 107 | 108 | it('should fail valid, pass invalid and return error if expected result is invalid', function (done) { 109 | cli('test -s test/schema.json -d test/valid_data.json -d test/invalid_data.json --invalid --errors=line', function (error, stdout, stderr) { 110 | assert(error instanceof Error); 111 | assertNoErrors(stderr, 1, /\sfailed/); 112 | assertRequiredErrors(stdout, 1, /\spassed/); 113 | done(); 114 | }); 115 | }); 116 | }); 117 | }); 118 | 119 | 120 | function assertNoErrors(out, count, regexp) { 121 | var lines = out.split('\n'); 122 | assert.equal(lines.length, count + 1); 123 | for (var i=0; i