From fef8952d8ae12ac4c371fb7cb4ae548bc668f939 Mon Sep 17 00:00:00 2001 From: Adam Voss Date: Fri, 9 Jun 2017 14:48:21 -0500 Subject: [PATCH 1/6] Add exclude section to _config.yml --- _config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_config.yml b/_config.yml index ad96e174..26e01800 100644 --- a/_config.yml +++ b/_config.yml @@ -31,6 +31,10 @@ header_pages: - examples.md - implementations.md +exclude: +- README.md +- Gemfile + collections: docs: output: true From 36179c4a4d5f74c84229f3182c66a0f248a6bc0e Mon Sep 17 00:00:00 2001 From: Adam Voss Date: Fri, 9 Jun 2017 14:49:47 -0500 Subject: [PATCH 2/6] Remove unused collection from _config.yml --- _config.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/_config.yml b/_config.yml index 26e01800..80224782 100644 --- a/_config.yml +++ b/_config.yml @@ -35,10 +35,5 @@ exclude: - README.md - Gemfile -collections: - docs: - output: true - permalink: "/:title:output_ext" - gems: - jekyll-relative-links From 7349ef9a22593ac5178b86d22128b8a28129ad60 Mon Sep 17 00:00:00 2001 From: Adam Voss Date: Thu, 8 Jun 2017 21:15:29 -0500 Subject: [PATCH 3/6] Add tests validating example json files --- .gitignore | 1 + .travis.yml | 22 ++ _config.yml | 1 + _includes/example1/instance.json | 6 + _includes/example1/schema1.json | 6 + _includes/example1/schema2.json | 13 ++ _includes/example1/schema3.json | 17 ++ _includes/example1/schema4.json | 22 ++ _includes/example1/schema5.json | 30 +++ _includes/example1/set_instance.json | 31 +++ _includes/example1/set_schema.json | 45 ++++ _includes/example2/entry_schema1.json | 24 +++ _includes/example2/entry_schema2.json | 34 ++++ _includes/example2/entry_schema3.json | 83 ++++++++ _includes/example2/instance.json | 31 +++ _includes/example2/schema1.json | 12 ++ _includes/example2/schema2.json | 12 ++ _includes/example2/storage_schema1.json | 11 + _includes/example2/storage_schema2.json | 11 + _includes/example2/storage_schema3.json | 19 ++ _includes/example2/storage_schema4.json | 12 ++ _includes/person.json | 18 ++ example/geo.json | 2 +- example1.md | 179 +--------------- example2.md | 259 +----------------------- examples.md | 19 +- 26 files changed, 481 insertions(+), 439 deletions(-) create mode 100644 _includes/example1/instance.json create mode 100644 _includes/example1/schema1.json create mode 100644 _includes/example1/schema2.json create mode 100644 _includes/example1/schema3.json create mode 100644 _includes/example1/schema4.json create mode 100644 _includes/example1/schema5.json create mode 100644 _includes/example1/set_instance.json create mode 100644 _includes/example1/set_schema.json create mode 100644 _includes/example2/entry_schema1.json create mode 100644 _includes/example2/entry_schema2.json create mode 100644 _includes/example2/entry_schema3.json create mode 100644 _includes/example2/instance.json create mode 100644 _includes/example2/schema1.json create mode 100644 _includes/example2/schema2.json create mode 100644 _includes/example2/storage_schema1.json create mode 100644 _includes/example2/storage_schema2.json create mode 100644 _includes/example2/storage_schema3.json create mode 100644 _includes/example2/storage_schema4.json create mode 100644 _includes/person.json diff --git a/.gitignore b/.gitignore index 0f193c2e..582ed7b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Jekyll products .sass-cache/ _site/ +node_modules/ diff --git a/.travis.yml b/.travis.yml index c0bdbed8..e8f48e3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,27 @@ language: ruby dist: trusty sudo: false +node_js: +- node +before_script: +- npm install ajv-cli +- echo node ./node_modules/ajv-cli/index.js \"\$\@\" >> ajv +- chmod +x ajv +- PATH=".:$PATH" script: - bundle exec jekyll build +- ajv test -s schema -d "_includes/person.json" --valid +- ajv test -s schema -d "_includes/example1/schema*.json" --valid +- ajv test -s _includes/example1/schema1.json -d _includes/example1/instance.json --valid +- ajv test -s _includes/example1/schema2.json -d _includes/example1/instance.json --valid +- ajv test -s _includes/example1/schema3.json -d _includes/example1/instance.json --valid +- ajv test -s _includes/example1/schema4.json -d _includes/example1/instance.json --valid +- ajv test -s _includes/example1/schema5.json -d _includes/example1/instance.json --valid +- ajv test -s schema -d _includes/example1/set_schema.json --valid +- ajv test -s _includes/example1/set_schema.json -d _includes/example1/set_instance.json -r geo --valid +- ajv test -s schema -d "_includes/example2/schema*.json" --valid +- ajv test -s schema -d "_includes/example2/entry_schema*.json" --valid +- ajv test -s schema -d "_includes/example2/storage_schema*.json" --valid +- ajv test -s _includes/example2/schema1.json -d _includes/example2/instance.json --valid +- ajv test -s _includes/example2/schema2.json -d _includes/example2/instance.json -r _includes/example2/entry_schema3.json --valid +- ajv test -s schema -d address -d calendar -d card -d geo --valid \ No newline at end of file diff --git a/_config.yml b/_config.yml index 80224782..71a91b24 100644 --- a/_config.yml +++ b/_config.yml @@ -34,6 +34,7 @@ header_pages: exclude: - README.md - Gemfile +- node_modules gems: - jekyll-relative-links diff --git a/_includes/example1/instance.json b/_includes/example1/instance.json new file mode 100644 index 00000000..3cf40ec6 --- /dev/null +++ b/_includes/example1/instance.json @@ -0,0 +1,6 @@ +{ + "id": 1, + "name": "A green door", + "price": 12.50, + "tags": ["home", "green"] +} \ No newline at end of file diff --git a/_includes/example1/schema1.json b/_includes/example1/schema1.json new file mode 100644 index 00000000..7adcd1e3 --- /dev/null +++ b/_includes/example1/schema1.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Product", + "description": "A product from Acme's catalog", + "type": "object" +} \ No newline at end of file diff --git a/_includes/example1/schema2.json b/_includes/example1/schema2.json new file mode 100644 index 00000000..cb0bd446 --- /dev/null +++ b/_includes/example1/schema2.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Product", + "description": "A product from Acme's catalog", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a product", + "type": "integer" + } + }, + "required": ["id"] +} \ No newline at end of file diff --git a/_includes/example1/schema3.json b/_includes/example1/schema3.json new file mode 100644 index 00000000..c3aff9c0 --- /dev/null +++ b/_includes/example1/schema3.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Product", + "description": "A product from Acme's catalog", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a product", + "type": "integer" + }, + "name": { + "description": "Name of the product", + "type": "string" + } + }, + "required": ["id", "name"] +} \ No newline at end of file diff --git a/_includes/example1/schema4.json b/_includes/example1/schema4.json new file mode 100644 index 00000000..1a4473ac --- /dev/null +++ b/_includes/example1/schema4.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Product", + "description": "A product from Acme's catalog", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a product", + "type": "integer" + }, + "name": { + "description": "Name of the product", + "type": "string" + }, + "price": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + } + }, + "required": ["id", "name", "price"] +} \ No newline at end of file diff --git a/_includes/example1/schema5.json b/_includes/example1/schema5.json new file mode 100644 index 00000000..d217a00c --- /dev/null +++ b/_includes/example1/schema5.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Product", + "description": "A product from Acme's catalog", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a product", + "type": "integer" + }, + "name": { + "description": "Name of the product", + "type": "string" + }, + "price": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": ["id", "name", "price"] +} \ No newline at end of file diff --git a/_includes/example1/set_instance.json b/_includes/example1/set_instance.json new file mode 100644 index 00000000..b847cede --- /dev/null +++ b/_includes/example1/set_instance.json @@ -0,0 +1,31 @@ +[ + { + "id": 2, + "name": "An ice sculpture", + "price": 12.50, + "tags": ["cold", "ice"], + "dimensions": { + "length": 7.0, + "width": 12.0, + "height": 9.5 + }, + "warehouseLocation": { + "latitude": -78.75, + "longitude": 20.4 + } + }, + { + "id": 3, + "name": "A blue mouse", + "price": 25.50, + "dimensions": { + "length": 3.1, + "width": 1.0, + "height": 1.0 + }, + "warehouseLocation": { + "latitude": 54.4, + "longitude": -32.7 + } + } +] \ No newline at end of file diff --git a/_includes/example1/set_schema.json b/_includes/example1/set_schema.json new file mode 100644 index 00000000..d21b2176 --- /dev/null +++ b/_includes/example1/set_schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Product set", + "type": "array", + "items": { + "title": "Product", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a product", + "type": "number" + }, + "name": { + "type": "string" + }, + "price": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "dimensions": { + "type": "object", + "properties": { + "length": {"type": "number"}, + "width": {"type": "number"}, + "height": {"type": "number"} + }, + "required": ["length", "width", "height"] + }, + "warehouseLocation": { + "description": "Coordinates of the warehouse with the product", + "$ref": "http://json-schema.org/geo" + } + }, + "required": ["id", "name", "price"] + } +} \ No newline at end of file diff --git a/_includes/example2/entry_schema1.json b/_includes/example2/entry_schema1.json new file mode 100644 index 00000000..610e0e79 --- /dev/null +++ b/_includes/example2/entry_schema1.json @@ -0,0 +1,24 @@ +{ + "id": "http://some.site.somewhere/entry-schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "schema for an fstab entry", + "type": "object", + "required": [ "storage" ], + "properties": { + "storage": { + "type": "object", + "oneOf": [ + { "$ref": "#/definitions/diskDevice" }, + { "$ref": "#/definitions/diskUUID" }, + { "$ref": "#/definitions/nfs" }, + { "$ref": "#/definitions/tmpfs" } + ] + } + }, + "definitions": { + "diskDevice": {}, + "diskUUID": {}, + "nfs": {}, + "tmpfs": {} + } +} \ No newline at end of file diff --git a/_includes/example2/entry_schema2.json b/_includes/example2/entry_schema2.json new file mode 100644 index 00000000..30e8f4bb --- /dev/null +++ b/_includes/example2/entry_schema2.json @@ -0,0 +1,34 @@ +{ + "id": "http://some.site.somewhere/entry-schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "schema for an fstab entry", + "type": "object", + "required": [ "storage" ], + "properties": { + "storage": { + "type": "object", + "oneOf": [ + { "$ref": "#/definitions/diskDevice" }, + { "$ref": "#/definitions/diskUUID" }, + { "$ref": "#/definitions/nfs" }, + { "$ref": "#/definitions/tmpfs" } + ] + }, + "fstype": { + "enum": [ "ext3", "ext4", "btrfs" ] + }, + "options": { + "type": "array", + "minItems": 1, + "items": { "type": "string" }, + "uniqueItems": true + }, + "readonly": { "type": "boolean" } + }, + "definitions": { + "diskDevice": {}, + "diskUUID": {}, + "nfs": {}, + "tmpfs": {} + } +} \ No newline at end of file diff --git a/_includes/example2/entry_schema3.json b/_includes/example2/entry_schema3.json new file mode 100644 index 00000000..8dc7e828 --- /dev/null +++ b/_includes/example2/entry_schema3.json @@ -0,0 +1,83 @@ +{ + "id": "http://some.site.somewhere/entry-schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "schema for an fstab entry", + "type": "object", + "required": [ "storage" ], + "properties": { + "storage": { + "type": "object", + "oneOf": [ + { "$ref": "#/definitions/diskDevice" }, + { "$ref": "#/definitions/diskUUID" }, + { "$ref": "#/definitions/nfs" }, + { "$ref": "#/definitions/tmpfs" } + ] + }, + "fstype": { + "enum": [ "ext3", "ext4", "btrfs" ] + }, + "options": { + "type": "array", + "minItems": 1, + "items": { "type": "string" }, + "uniqueItems": true + }, + "readonly": { "type": "boolean" } + }, + "definitions": { + "diskDevice": { + "properties": { + "type": { "enum": [ "disk" ] }, + "device": { + "type": "string", + "pattern": "^/dev/[^/]+(/[^/]+)*$" + } + }, + "required": [ "type", "device" ], + "additionalProperties": false + }, + "diskUUID": { + "properties": { + "type": { "enum": [ "disk" ] }, + "label": { + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + } + }, + "required": [ "type", "label" ], + "additionalProperties": false + }, + "nfs": { + "properties": { + "type": { "enum": [ "nfs" ] }, + "remotePath": { + "type": "string", + "pattern": "^(/[^/]+)+$" + }, + "server": { + "type": "string", + "oneOf": [ + { "format": "host-name" }, + { "format": "ipv4" }, + { "format": "ipv6" } + ] + } + }, + "required": [ "type", "server", "remotePath" ], + "additionalProperties": false + }, + "tmpfs": { + "properties": { + "type": { "enum": [ "tmpfs" ] }, + "sizeInMB": { + "type": "integer", + "minimum": 16, + "maximum": 512 + } + }, + "required": [ "type", "sizeInMB" ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/_includes/example2/instance.json b/_includes/example2/instance.json new file mode 100644 index 00000000..acc5c4a8 --- /dev/null +++ b/_includes/example2/instance.json @@ -0,0 +1,31 @@ +{ + "/": { + "storage": { + "type": "disk", + "device": "/dev/sda1" + }, + "fstype": "btrfs", + "readonly": true + }, + "/var": { + "storage": { + "type": "disk", + "label": "8f3ba6f4-5c70-46ec-83af-0d5434953e5f" + }, + "fstype": "ext4", + "options": [ "nosuid" ] + }, + "/tmp": { + "storage": { + "type": "tmpfs", + "sizeInMB": 64 + } + }, + "/var/www": { + "storage": { + "type": "nfs", + "server": "my.nfs.server", + "remotePath": "/exports/mypath" + } + } +} \ No newline at end of file diff --git a/_includes/example2/schema1.json b/_includes/example2/schema1.json new file mode 100644 index 00000000..fcc95618 --- /dev/null +++ b/_includes/example2/schema1.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "/": {} + }, + "patternProperties": { + "^(/[^/]+)+$": {} + }, + "additionalProperties": false, + "required": [ "/" ] +} \ No newline at end of file diff --git a/_includes/example2/schema2.json b/_includes/example2/schema2.json new file mode 100644 index 00000000..e93af544 --- /dev/null +++ b/_includes/example2/schema2.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "/": { "$ref": "http://some.site.somewhere/entry-schema#" } + }, + "patternProperties": { + "^(/[^/]+)+$": { "$ref": "http://some.site.somewhere/entry-schema#" } + }, + "additionalProperties": false, + "required": [ "/" ] +} \ No newline at end of file diff --git a/_includes/example2/storage_schema1.json b/_includes/example2/storage_schema1.json new file mode 100644 index 00000000..7bf92b14 --- /dev/null +++ b/_includes/example2/storage_schema1.json @@ -0,0 +1,11 @@ +{ + "properties": { + "type": { "enum": [ "disk" ] }, + "device": { + "type": "string", + "pattern": "^/dev/[^/]+(/[^/]+)*$" + } + }, + "required": [ "type", "device" ], + "additionalProperties": false +} \ No newline at end of file diff --git a/_includes/example2/storage_schema2.json b/_includes/example2/storage_schema2.json new file mode 100644 index 00000000..2c77835b --- /dev/null +++ b/_includes/example2/storage_schema2.json @@ -0,0 +1,11 @@ +{ + "properties": { + "type": { "enum": [ "disk" ] }, + "label": { + "type": "string", + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + } + }, + "required": [ "type", "label" ], + "additionalProperties": false +} \ No newline at end of file diff --git a/_includes/example2/storage_schema3.json b/_includes/example2/storage_schema3.json new file mode 100644 index 00000000..164ee953 --- /dev/null +++ b/_includes/example2/storage_schema3.json @@ -0,0 +1,19 @@ +{ + "properties": { + "type": { "enum": [ "nfs" ] }, + "remotePath": { + "type": "string", + "pattern": "^(/[^/]+)+$" + }, + "server": { + "type": "string", + "oneOf": [ + { "format": "host-name" }, + { "format": "ipv4" }, + { "format": "ipv6" } + ] + } + }, + "required": [ "type", "server", "remotePath" ], + "additionalProperties": false +} \ No newline at end of file diff --git a/_includes/example2/storage_schema4.json b/_includes/example2/storage_schema4.json new file mode 100644 index 00000000..7caa3cd6 --- /dev/null +++ b/_includes/example2/storage_schema4.json @@ -0,0 +1,12 @@ +{ + "properties": { + "type": { "enum": [ "tmpfs" ] }, + "sizeInMB": { + "type": "integer", + "minimum": 16, + "maximum": 512 + } + }, + "required": [ "type", "sizeInMB" ], + "additionalProperties": false +} \ No newline at end of file diff --git a/_includes/person.json b/_includes/person.json new file mode 100644 index 00000000..6e128524 --- /dev/null +++ b/_includes/person.json @@ -0,0 +1,18 @@ +{ + "title": "Person", + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "age": { + "description": "Age in years", + "type": "integer", + "minimum": 0 + } + }, + "required": ["firstName", "lastName"] +} \ No newline at end of file diff --git a/example/geo.json b/example/geo.json index 2f335265..654d2ce7 100644 --- a/example/geo.json +++ b/example/geo.json @@ -6,4 +6,4 @@ "latitude": { "type": "number" }, "longitude": { "type": "number" } } -} +} \ No newline at end of file diff --git a/example1.md b/example1.md index 9c2451e3..977a38ef 100644 --- a/example1.md +++ b/example1.md @@ -10,12 +10,7 @@ Let's pretend we're interacting with a JSON based product catalog. This catalog An example product in this API is: ```json -{ - "id": 1, - "name": "A green door", - "price": 12.50, - "tags": ["home", "green"] -} +{% include example1/instance.json %} ``` While generally straightforward, that example leaves some open questions. For example, one may ask: @@ -33,12 +28,7 @@ Starting the schema To start a schema definition, let's begin with a basic JSON schema: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Product", - "description": "A product from Acme's catalog", - "type": "object" -} +{% include example1/schema1.json %} ``` The above schema has four properties called *keywords*. The *title* and *description* keywords are descriptive only, in that they do not add constraints to the data being validated. The intent of the schema is stated with these two keywords (that is, this schema describes a product). @@ -59,19 +49,7 @@ Next let's answer our previous questions about this API, starting with id. In JSON Schema terms, we can update our schema to: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Product", - "description": "A product from Acme's catalog", - "type": "object", - "properties": { - "id": { - "description": "The unique identifier for a product", - "type": "integer" - } - }, - "required": ["id"] -} +{% include example1/schema2.json %} ``` ### Is name required? @@ -79,23 +57,7 @@ In JSON Schema terms, we can update our schema to: *name* is a string value that describes a product. Since there isn't much to a product without a name, it also is required. Adding this gives us the schema: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Product", - "description": "A product from Acme's catalog", - "type": "object", - "properties": { - "id": { - "description": "The unique identifier for a product", - "type": "integer" - }, - "name": { - "description": "Name of the product", - "type": "string" - } - }, - "required": ["id", "name"] -} +{% include example1/schema3.json %} ``` ### Can price be 0? @@ -103,28 +65,7 @@ In JSON Schema terms, we can update our schema to: According to Acme's docs, there are no free products. In JSON schema a number can have a minimum. By default this minimum is inclusive, so we need to specify *exclusiveMinimum*. Therefore we can update our schema with *price*: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Product", - "description": "A product from Acme's catalog", - "type": "object", - "properties": { - "id": { - "description": "The unique identifier for a product", - "type": "integer" - }, - "name": { - "description": "Name of the product", - "type": "string" - }, - "price": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - } - }, - "required": ["id", "name", "price"] -} +{% include example1/schema4.json %} ``` ### Are all tags strings? @@ -139,36 +80,7 @@ However, Acme's docs add two constraints: The first constraint can be added with *minItems*, and the second one by specifying *uniqueItems* as being true: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Product", - "description": "A product from Acme's catalog", - "type": "object", - "properties": { - "id": { - "description": "The unique identifier for a product", - "type": "integer" - }, - "name": { - "description": "Name of the product", - "type": "string" - }, - "price": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - } - }, - "required": ["id", "name", "price"] -} +{% include example1/schema5.json %} ``` Summary @@ -183,86 +95,11 @@ And also, since JSON Schema defines a reference schema for a geographic location ### Set of products: ```json -[ - { - "id": 2, - "name": "An ice sculpture", - "price": 12.50, - "tags": ["cold", "ice"], - "dimensions": { - "length": 7.0, - "width": 12.0, - "height": 9.5 - }, - "warehouseLocation": { - "latitude": -78.75, - "longitude": 20.4 - } - }, - { - "id": 3, - "name": "A blue mouse", - "price": 25.50, - "dimensions": { - "length": 3.1, - "width": 1.0, - "height": 1.0 - }, - "warehouseLocation": { - "latitude": 54.4, - "longitude": -32.7 - } - } -] +{% include example1/set_instance.json %} ``` ### Set of products schema: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Product set", - "type": "array", - "items": { - "title": "Product", - "type": "object", - "properties": { - "id": { - "description": "The unique identifier for a product", - "type": "number" - }, - "name": { - "type": "string" - }, - "price": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "dimensions": { - "type": "object", - "properties": { - "length": {"type": "number"}, - "width": {"type": "number"}, - "height": {"type": "number"} - }, - "required": ["length", "width", "height"] - }, - "warehouseLocation": { - "description": "Coordinates of the warehouse with the product", - "$ref": "http://json-schema.org/geo" - } - }, - "required": ["id", "name", "price"] - } -} +{% include example1/set_schema.json %} ``` - diff --git a/example2.md b/example2.md index d0b1efa1..97b96723 100644 --- a/example2.md +++ b/example2.md @@ -8,37 +8,7 @@ This example shows a possible JSON representation of a hypothetical machine's mo An entry in the fstab file can have many different forms. Here is a possible representation of a full fstab: ```json -{ - "/": { - "storage": { - "type": "disk", - "device": "/dev/sda1" - }, - "fstype": "btrfs", - "readonly": true - }, - "/var": { - "storage": { - "type": "disk", - "label": "8f3ba6f4-5c70-46ec-83af-0d5434953e5f" - }, - "fstype": "ext4", - "options": [ "nosuid" ] - }, - "/tmp": { - "storage": { - "type": "tmpfs", - "sizeInMB": 64 - } - }, - "/var/www": { - "storage": { - "type": "nfs", - "server": "my.nfs.server", - "remotePath": "/exports/mypath" - } - } -} +{% include example2/instance.json %} ``` Not all constraints to an fstab file can be modeled using JSON Schema alone; however, it can already represent a good number of them. We will add constraints one after the other until we get to a satisfactory result. @@ -55,18 +25,7 @@ We will start with a base schema expressing the following constraints: We also want the schema to be regarded as a draft v4 schema, we must therefore specify *$schema*: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "/": {} - }, - "patternProperties": { - "^(/[^/]+)+$": {} - }, - "additionalProperties": false, - "required": [ "/" ] -} +{% include example2/schema1.json %} ``` Note how the valid paths constraint is enforced here: @@ -85,30 +44,7 @@ The entry schema - starting out Here again we will proceed step by step. We will start with the global structure of our schema, which will be as such: ```json -{ - "id": "http://some.site.somewhere/entry-schema#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "schema for an fstab entry", - "type": "object", - "required": [ "storage" ], - "properties": { - "storage": { - "type": "object", - "oneOf": [ - { "$ref": "#/definitions/diskDevice" }, - { "$ref": "#/definitions/diskUUID" }, - { "$ref": "#/definitions/nfs" }, - { "$ref": "#/definitions/tmpfs" } - ] - } - }, - "definitions": { - "diskDevice": {}, - "diskUUID": {}, - "nfs": {}, - "tmpfs": {} - } -} +{% include example2/entry_schema1.json %} ``` You should already be familiar with some of the constraints: @@ -138,40 +74,7 @@ Let's now extend this skeleton to add constraints to these three properties. Not With these added constraints, the schema now looks like this: ```json -{ - "id": "http://some.site.somewhere/entry-schema#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "schema for an fstab entry", - "type": "object", - "required": [ "storage" ], - "properties": { - "storage": { - "type": "object", - "oneOf": [ - { "$ref": "#/definitions/diskDevice" }, - { "$ref": "#/definitions/diskUUID" }, - { "$ref": "#/definitions/nfs" }, - { "$ref": "#/definitions/tmpfs" } - ] - }, - "fstype": { - "enum": [ "ext3", "ext4", "btrfs" ] - }, - "options": { - "type": "array", - "minItems": 1, - "items": { "type": "string" }, - "uniqueItems": true - }, - "readonly": { "type": "boolean" } - }, - "definitions": { - "diskDevice": {}, - "diskUUID": {}, - "nfs": {}, - "tmpfs": {} - } -} +{% include example2/entry_schema2.json %} ``` For now, all definitions are empty (an empty JSON Schema validates all instances). We will write schemas for individual definitions below, and fill these schemas into the entry schema. @@ -182,17 +85,7 @@ The *diskDevice* storage type This storage type has two required properties, *type* and *device*. The type can only be *disk*, and the device must be an absolute path starting with */dev*. No other properties are allowed: ```json -{ - "properties": { - "type": { "enum": [ "disk" ] }, - "device": { - "type": "string", - "pattern": "^/dev/[^/]+(/[^/]+)*$" - } - }, - "required": [ "type", "device" ], - "additionalProperties": false -} +{% include example2/storage_schema1.json %} ``` You will have noted that we need not specify that *type* must be a string: the constraint described by *enum* is enough. @@ -203,17 +96,7 @@ The *diskUUID* storage type This storage type has two required properties, *type* and *label*. The type can only be *disk*, and the label must be a valid UUID. No other properties are allowed: ```json -{ - "properties": { - "type": { "enum": [ "disk" ] }, - "label": { - "type": "string", - "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" - } - }, - "required": [ "type", "label" ], - "additionalProperties": false -} +{% include example2/storage_schema2.json %} ``` The *nfs* storage type @@ -224,25 +107,7 @@ This storage type has three required properties: *type*, *server* and *remotePat For the constraints on *server*, we use a new keyword: *format*. While it is not required that *format* be supported, we will suppose that it is here: ```json -{ - "properties": { - "type": { "enum": [ "nfs" ] }, - "remotePath": { - "type": "string", - "pattern": "^(/[^/]+)+$" - }, - "server": { - "type": "string", - "oneOf": [ - { "format": "host-name" }, - { "format": "ipv4" }, - { "format": "ipv6" } - ] - } - }, - "required": [ "type", "server", "remotePath" ], - "additionalProperties": false -} +{% include example2/storage_schema3.json %} ``` The *tmpfs* storage type @@ -251,18 +116,7 @@ The *tmpfs* storage type This storage type has two required properties: *type* and *sizeInMB*. The size can only be an integer. What is more, we will require that the size be between 16 and 512, inclusive: ```json -{ - "properties": { - "type": { "enum": [ "tmpfs" ] }, - "sizeInMB": { - "type": "integer", - "minimum": 16, - "maximum": 512 - } - }, - "required": [ "type", "sizeInMB" ], - "additionalProperties": false -} +{% include example2/storage_schema4.json %} ``` The full entry schema @@ -271,89 +125,7 @@ The full entry schema The resulting schema is quite large: ```json -{ - "id": "http://some.site.somewhere/entry-schema#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "schema for an fstab entry", - "type": "object", - "required": [ "storage" ], - "properties": { - "storage": { - "type": "object", - "oneOf": [ - { "$ref": "#/definitions/diskDevice" }, - { "$ref": "#/definitions/diskUUID" }, - { "$ref": "#/definitions/nfs" }, - { "$ref": "#/definitions/tmpfs" } - ] - }, - "fstype": { - "enum": [ "ext3", "ext4", "btrfs" ] - }, - "options": { - "type": "array", - "minItems": 1, - "items": { "type": "string" }, - "uniqueItems": true - }, - "readonly": { "type": "boolean" } - }, - "definitions": { - "diskDevice": { - "properties": { - "type": { "enum": [ "disk" ] }, - "device": { - "type": "string", - "pattern": "^/dev/[^/]+(/[^/]+)*$" - } - }, - "required": [ "type", "device" ], - "additionalProperties": false - }, - "diskUUID": { - "properties": { - "type": { "enum": [ "disk" ] }, - "label": { - "type": "string", - "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" - } - }, - "required": [ "type", "label" ], - "additionalProperties": false - }, - "nfs": { - "properties": { - "type": { "enum": [ "nfs" ] }, - "remotePath": { - "type": "string", - "pattern": "^(/[^/]+)+$" - }, - "server": { - "type": "string", - "oneOf": [ - { "format": "host-name" }, - { "format": "ipv4" }, - { "format": "ipv6" } - ] - } - }, - "required": [ "type", "server", "remotePath" ], - "additionalProperties": false - }, - "tmpfs": { - "properties": { - "type": { "enum": [ "tmpfs" ] }, - "sizeInMB": { - "type": "integer", - "minimum": 16, - "maximum": 512 - } - }, - "required": [ "type", "sizeInMB" ], - "additionalProperties": false - } - } -} +{% include example2/entry_schema3.json %} ``` Plugging this into our main schema @@ -362,18 +134,7 @@ Plugging this into our main schema Now that all possible entries have been described, we can refer to the entry schema from our main schema. We will, again, use a JSON Reference here: ```json -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "/": { "$ref": "http://some.site.somewhere/entry-schema#" } - }, - "patternProperties": { - "^(/[^/]+)+$": { "$ref": "http://some.site.somewhere/entry-schema#" } - }, - "additionalProperties": false, - "required": [ "/" ] -} +{% include example2/schema2.json %} ``` Wrapping up diff --git a/examples.md b/examples.md index 1c9dc474..f36ebfb4 100644 --- a/examples.md +++ b/examples.md @@ -6,24 +6,7 @@ title: Examples Here is a basic example of a JSON Schema: ```json -{ - "title": "Person", - "type": "object", - "properties": { - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "age": { - "description": "Age in years", - "type": "integer", - "minimum": 0 - } - }, - "required": ["firstName", "lastName"] -} +{% include person.json%} ``` Example schemas From c292a43d8a0bcd9f541be3a20796571c9742fc8b Mon Sep 17 00:00:00 2001 From: Adam Voss Date: Fri, 9 Jun 2017 17:47:10 -0500 Subject: [PATCH 4/6] Update all examples to be valid against Draft 6 --- _includes/example1/schema1.json | 2 +- _includes/example1/schema2.json | 2 +- _includes/example1/schema3.json | 2 +- _includes/example1/schema4.json | 5 ++--- _includes/example1/schema5.json | 5 ++--- _includes/example1/set_schema.json | 5 ++--- _includes/example2/entry_schema1.json | 2 +- _includes/example2/entry_schema2.json | 2 +- _includes/example2/entry_schema3.json | 4 ++-- _includes/example2/schema1.json | 2 +- _includes/example2/schema2.json | 2 +- _includes/example2/storage_schema3.json | 2 +- example/address.json | 2 +- example/calendar.json | 4 ++-- example/card.json | 2 +- example/geo.json | 3 ++- example1.md | 4 ++-- example2.md | 6 ++---- 18 files changed, 26 insertions(+), 30 deletions(-) diff --git a/_includes/example1/schema1.json b/_includes/example1/schema1.json index 7adcd1e3..bde74825 100644 --- a/_includes/example1/schema1.json +++ b/_includes/example1/schema1.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "title": "Product", "description": "A product from Acme's catalog", "type": "object" diff --git a/_includes/example1/schema2.json b/_includes/example1/schema2.json index cb0bd446..593db4ce 100644 --- a/_includes/example1/schema2.json +++ b/_includes/example1/schema2.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "title": "Product", "description": "A product from Acme's catalog", "type": "object", diff --git a/_includes/example1/schema3.json b/_includes/example1/schema3.json index c3aff9c0..cd8e32fd 100644 --- a/_includes/example1/schema3.json +++ b/_includes/example1/schema3.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "title": "Product", "description": "A product from Acme's catalog", "type": "object", diff --git a/_includes/example1/schema4.json b/_includes/example1/schema4.json index 1a4473ac..5d0a5490 100644 --- a/_includes/example1/schema4.json +++ b/_includes/example1/schema4.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "title": "Product", "description": "A product from Acme's catalog", "type": "object", @@ -14,8 +14,7 @@ }, "price": { "type": "number", - "minimum": 0, - "exclusiveMinimum": true + "exclusiveMinimum": 0 } }, "required": ["id", "name", "price"] diff --git a/_includes/example1/schema5.json b/_includes/example1/schema5.json index d217a00c..c17d9db2 100644 --- a/_includes/example1/schema5.json +++ b/_includes/example1/schema5.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "title": "Product", "description": "A product from Acme's catalog", "type": "object", @@ -14,8 +14,7 @@ }, "price": { "type": "number", - "minimum": 0, - "exclusiveMinimum": true + "exclusiveMinimum": 0 }, "tags": { "type": "array", diff --git a/_includes/example1/set_schema.json b/_includes/example1/set_schema.json index d21b2176..93366142 100644 --- a/_includes/example1/set_schema.json +++ b/_includes/example1/set_schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "title": "Product set", "type": "array", "items": { @@ -15,8 +15,7 @@ }, "price": { "type": "number", - "minimum": 0, - "exclusiveMinimum": true + "exclusiveMinimum": 0 }, "tags": { "type": "array", diff --git a/_includes/example2/entry_schema1.json b/_includes/example2/entry_schema1.json index 610e0e79..d6c7beff 100644 --- a/_includes/example2/entry_schema1.json +++ b/_includes/example2/entry_schema1.json @@ -1,6 +1,6 @@ { "id": "http://some.site.somewhere/entry-schema#", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "schema for an fstab entry", "type": "object", "required": [ "storage" ], diff --git a/_includes/example2/entry_schema2.json b/_includes/example2/entry_schema2.json index 30e8f4bb..50dfc582 100644 --- a/_includes/example2/entry_schema2.json +++ b/_includes/example2/entry_schema2.json @@ -1,6 +1,6 @@ { "id": "http://some.site.somewhere/entry-schema#", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "schema for an fstab entry", "type": "object", "required": [ "storage" ], diff --git a/_includes/example2/entry_schema3.json b/_includes/example2/entry_schema3.json index 8dc7e828..5ae25a98 100644 --- a/_includes/example2/entry_schema3.json +++ b/_includes/example2/entry_schema3.json @@ -1,6 +1,6 @@ { "id": "http://some.site.somewhere/entry-schema#", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "schema for an fstab entry", "type": "object", "required": [ "storage" ], @@ -58,7 +58,7 @@ "server": { "type": "string", "oneOf": [ - { "format": "host-name" }, + { "format": "hostname" }, { "format": "ipv4" }, { "format": "ipv6" } ] diff --git a/_includes/example2/schema1.json b/_includes/example2/schema1.json index fcc95618..616f1ac7 100644 --- a/_includes/example2/schema1.json +++ b/_includes/example2/schema1.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "type": "object", "properties": { "/": {} diff --git a/_includes/example2/schema2.json b/_includes/example2/schema2.json index e93af544..167a7710 100644 --- a/_includes/example2/schema2.json +++ b/_includes/example2/schema2.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "type": "object", "properties": { "/": { "$ref": "http://some.site.somewhere/entry-schema#" } diff --git a/_includes/example2/storage_schema3.json b/_includes/example2/storage_schema3.json index 164ee953..66aea6e1 100644 --- a/_includes/example2/storage_schema3.json +++ b/_includes/example2/storage_schema3.json @@ -8,7 +8,7 @@ "server": { "type": "string", "oneOf": [ - { "format": "host-name" }, + { "format": "hostname" }, { "format": "ipv4" }, { "format": "ipv6" } ] diff --git a/example/address.json b/example/address.json index 67f7a92e..dd124d65 100644 --- a/example/address.json +++ b/example/address.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "An Address following the convention of http://microformats.org/wiki/hcard", "type": "object", "properties": { diff --git a/example/calendar.json b/example/calendar.json index b1a6f112..b716e8b5 100644 --- a/example/calendar.json +++ b/example/calendar.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "A representation of an event", "type": "object", "required": [ "dtstart", "summary" ], @@ -33,6 +33,6 @@ }, "category": { "type": "string" }, "description": { "type": "string" }, - "geo": { "$ref": "http: //json-schema.org/geo" } + "geo": { "$ref": "http://json-schema.org/geo" } } } diff --git a/example/card.json b/example/card.json index acdc3d07..59dbbc93 100644 --- a/example/card.json +++ b/example/card.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "A representation of a person, company, organization, or place", "type": "object", "required": ["familyName", "givenName"], diff --git a/example/geo.json b/example/geo.json index 654d2ce7..4a0610ed 100644 --- a/example/geo.json +++ b/example/geo.json @@ -1,5 +1,6 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://json-schema.org/geo", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "A geographical coordinate", "type": "object", "properties": { diff --git a/example1.md b/example1.md index 977a38ef..fe2c297e 100644 --- a/example1.md +++ b/example1.md @@ -35,7 +35,7 @@ The above schema has four properties called *keywords*. The *title* and *descrip The *type* keyword defines the first constraint on our JSON data: it has to be a JSON Object. -Finally, the *$schema* keyword states that this schema is written according to the draft v4 specification. +Finally, the *$schema* keyword states that this schema is written according to the draft-06 specification. Defining the properties ----------------------- @@ -62,7 +62,7 @@ In JSON Schema terms, we can update our schema to: ### Can price be 0? -According to Acme's docs, there are no free products. In JSON schema a number can have a minimum. By default this minimum is inclusive, so we need to specify *exclusiveMinimum*. Therefore we can update our schema with *price*: +According to Acme's docs, there are no free products. So we need to specify *exclusiveMinimum*. If we wanted to include 0 as a valid price, we would have specified *minimum* instead. Therefore, we can update our schema with *price*: ```json {% include example1/schema4.json %} diff --git a/example2.md b/example2.md index 97b96723..a48f33bf 100644 --- a/example2.md +++ b/example2.md @@ -22,7 +22,7 @@ We will start with a base schema expressing the following constraints: - the member names (or property names) of this object must all be valid, absolute paths; - there must be an entry for the root filesystem (ie, `/`). -We also want the schema to be regarded as a draft v4 schema, we must therefore specify *$schema*: +We also want the schema to be regarded as a draft v6 schema, we must therefore specify *$schema*: ```json {% include example2/schema1.json %} @@ -163,6 +163,4 @@ While this is not a concern if you know that the schema you write will be used b - *format* support is optional, and as such other tools may ignore this keyword: this can lead to a different validation outcome for the same data; - it uses regular expressions: care should be taken not to use any advanced features (such as lookarounds), since they may not be supported at the other end; -- it uses *$schema* to express the need to use draft v4 syntax, but not all tools support draft v4 (in fact, most don't support it). - - +- it uses *$schema* to express the need to use draft v6 compliant processing, but not all tools support draft v6 (in fact, most don't support it). From a328ed1e1180fb88ac460d0a1efa90c12ee2e172 Mon Sep 17 00:00:00 2001 From: Adam Voss Date: Fri, 9 Jun 2017 17:51:47 -0500 Subject: [PATCH 5/6] Some minor improvements to the walkthroughs --- example1.md | 2 +- example2.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example1.md b/example1.md index fe2c297e..026afc0c 100644 --- a/example1.md +++ b/example1.md @@ -70,7 +70,7 @@ According to Acme's docs, there are no free products. So we need to specify *exc ### Are all tags strings? -Finally, we come to the *tags* property. Unlike the previous properties, tags have many values, and is represented as a JSON array. According to Acme's docs, all tags must be strings, but you aren't required to specify tags. We simply leave *tags* out of the list of required properties. +Finally, we come to the *tags* property. Unlike the previous properties, tags have many values, and is represented as a JSON array. If, according to Acme's docs, all tags must be strings, but you aren't required to specify tags; we would simply leave *tags* out of the list of required properties. However, Acme's docs add two constraints: diff --git a/example2.md b/example2.md index a48f33bf..af537a5e 100644 --- a/example2.md +++ b/example2.md @@ -5,13 +5,13 @@ title: Building a mount point schema This example shows a possible JSON representation of a hypothetical machine's mount points as represented in an `/etc/fstab` file. -An entry in the fstab file can have many different forms. Here is a possible representation of a full fstab: +An entry in an fstab file can have many different forms. Here is a possible representation of a full fstab: ```json {% include example2/instance.json %} ``` -Not all constraints to an fstab file can be modeled using JSON Schema alone; however, it can already represent a good number of them. We will add constraints one after the other until we get to a satisfactory result. +Not all constraints to an fstab file can be modeled using JSON Schema alone; however, it can represent a good number of them. We will add constraints one after the other until we get to a satisfactory result. Base schema ----------- @@ -149,7 +149,7 @@ This is only an example for learning purposes. Some additional constraints could - it makes no sense for `/` to be mounted on a tmpfs filesystem; - it makes no sense to specify the filesystem type if the storage is either NFS or tmpfs. -As an exercise, you can always try and add these constraints. It would probably require splitting the schema further. +As an exercise, you can always try to add these constraints. It would probably require splitting the schema further. ### Not all constraints can be expressed @@ -163,4 +163,4 @@ While this is not a concern if you know that the schema you write will be used b - *format* support is optional, and as such other tools may ignore this keyword: this can lead to a different validation outcome for the same data; - it uses regular expressions: care should be taken not to use any advanced features (such as lookarounds), since they may not be supported at the other end; -- it uses *$schema* to express the need to use draft v6 compliant processing, but not all tools support draft v6 (in fact, most don't support it). +- it uses *$schema* to express the need to use draft v6 compliant processing, but not all tools support draft v6. From fa5975fb41f29fb1613c20c1e2b9453b5d43f8d6 Mon Sep 17 00:00:00 2001 From: Adam Voss Date: Mon, 12 Jun 2017 17:52:46 -0500 Subject: [PATCH 6/6] Utilize a better way to access ajv-cli --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e8f48e3a..12841e3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,7 @@ node_js: - node before_script: - npm install ajv-cli -- echo node ./node_modules/ajv-cli/index.js \"\$\@\" >> ajv -- chmod +x ajv -- PATH=".:$PATH" +- PATH="./node_modules/.bin/:$PATH" script: - bundle exec jekyll build - ajv test -s schema -d "_includes/person.json" --valid