Prmd
Prmd は JSON Schema の scaffold, 正当性チェック, ドキュメント生成を行うツールです。
インストール
$ gem install prmd
サンプル
名前と年齢を保持する Person を定義してバリデーションしてみます。
init
テンプレートを生成します
$ prmd init --yaml person -o person.yml
- 生成されたファイル
person.yml
---
"$schema": http://json-schema.org/draft-04/hyper-schema
title: FIXME - Person
description: FIXME
stability: prototype
strictProperties: true
type:
- object
definitions:
id:
description: unique identifier of person
readOnly: true
format: uuid
type:
- string
name:
description: unique name of person
readOnly: true
type:
- string
identity:
anyOf:
- "$ref": "/schemata/person#/definitions/id"
- "$ref": "/schemata/person#/definitions/name"
created_at:
description: when person was created
format: date-time
type:
- string
updated_at:
description: when person was updated
format: date-time
type:
- string
links:
- description: Create a new person.
href: "/persons"
method: POST
rel: create
schema:
properties: {}
type:
- object
title: Create
- description: Delete an existing person.
href: "/persons/{(%2Fschemata%2Fperson%23%2Fdefinitions%2Fidentity)}"
method: DELETE
rel: destroy
title: Delete
- description: Info for existing person.
href: "/persons/{(%2Fschemata%2Fperson%23%2Fdefinitions%2Fidentity)}"
method: GET
rel: self
title: Info
- description: List existing persons.
href: "/persons"
method: GET
rel: instances
title: List
- description: Update an existing person.
href: "/persons/{(%2Fschemata%2Fperson%23%2Fdefinitions%2Fidentity)}"
method: PATCH
rel: update
schema:
properties: {}
type:
- object
title: Update
properties:
created_at:
"$ref": "/schemata/person#/definitions/created_at"
id:
"$ref": "/schemata/person#/definitions/id"
name:
"$ref": "/schemata/person#/definitions/name"
updated_at:
"$ref": "/schemata/person#/definitions/updated_at"
id: schemata/person
生成したテンプレートを編集
person.yml
---
id: schemata/person
"$schema": http://json-schema.org/draft-04/hyper-schema
title: Person
description: 人
stability: prototype
strictProperties: true
type:
- object
definitions:
name:
description: name of person
type:
- string
age:
description: age of person
pattern: ^[0-9]{1,3}$
type:
- string
- "null"
identity:
"$ref": "/schemata/person#/definitions/name"
required: ["name"]
properties:
name:
"$ref": "/schemata/person#/definitions/name"
age:
"$ref": "/schemata/person#/definitions/age"
METAファイルを作成
meta.yml
---
"$schema": http://json-schema.org/draft-04/hyper-schema
title: Sample API JSON Schema
description: sample schema
id: sample
links:
- href: https://example.com
rel: self
結合する
YAMLを結合します
$ prmd combine --meta meta.yml person.yml > schema.json
- 出力結果を確認
schema.json
{
"$schema": "http://json-schema.org/draft-04/hyper-schema",
"type": [
"object"
],
"definitions": {
"person": {
"$schema": "http://json-schema.org/draft-04/hyper-schema",
"title": "Person",
"description": "人",
"stability": "prototype",
"strictProperties": true,
"type": [
"object"
],
"definitions": {
"name": {
"description": "name of person",
"type": [
"string"
]
},
"age": {
"description": "age of person",
"pattern": "^[0-9]{1,3}$",
"type": [
"string",
"null"
]
},
"identity": {
"$ref": "#/definitions/person/definitions/name"
}
},
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/person/definitions/name"
},
"age": {
"$ref": "#/definitions/person/definitions/age"
}
}
}
},
"properties": {
"person": {
"$ref": "#/definitions/person"
}
},
"title": "Sample API JSON Schema",
"description": "sample schema",
"id": "sample",
"links": [
{
"href": "https://example.com",
"rel": "self"
}
]
}
ドキュメントを生成
$ prmd doc schema.json > schema.md
- 出力結果を確認
schema.md
## <a name="resource-person">Person</a>
Stability: `prototype`
人
### Attributes
| Name | Type | Description | Example |
| ------- | ------- | ------- | ------- |
| **age** | *string* | age of person<br/> **pattern:** `^[0-9]{1,3}$` | `"example"` |
| **name** | *string* | name of person | `"example"` |
JSON Schema としての正当性をチェックする
$ prmd verify schema.json
わざとエラーを発生させてみます。
age:
description: age of person
pattern: ^\[0-9]{1,3}$
type:
# number を不正な値 invalid に変更する
- invalid
$ prmd verify schema.json
schema.json: #/definitions/person/definitions/age/type: failed schema #/properties/type: No subschema in "anyOf" matched.
schema.json: #: failed schema #: Not all subschemas of "allOf" matched.
schema.json: #/definitions/person/definitions/age/type: failed schema #/properties/type: No subschema in "anyOf" matched.
schema.json: #/definitions/person: failed schema #/properties/definitions/additionalProperties: Not all subschemas of "allOf" matched.
schema.json: #/definitions/person/definitions/age/type: failed schema #/properties/type: No subschema in "anyOf" matched.
schema.json: #/definitions/person/definitions/age: failed schema #/properties/definitions/additionalProperties: Not all subschemas of "allOf" matched.
json-schema gem でバリデーションを実行する
- テストデータ1 : 正常系
person1.yml
person:
name: tanaka1
age: "34"
- テストデータ2 : ageのパターンエラー
person2.yml
person:
name: tanaka
age: "1000"
- テストデータ3 : name の null エラー
person3.yml
person:
name:
age: "20"
- テストデータ4 : 正常系(age は null 許容)
person4.yml
person:
name: suzuki
age:
- テストデータ5 : name の必須エラー
person5.yml
person:
age:
- テストコード
test.rb
require "json-schema"
require 'pp'
require 'yaml'
require 'json'
schema = JSON.parse(File.read('schema.json'))
jsons = (1..5).map{|e|YAML.load_file("person#{e}.yml").to_json}
jsons.each do |json|
begin
pp json
pp JSON::Validator.validate!(schema, json)
rescue JSON::Schema::ValidationError => e
pp e.message
end
end
- 実行結果
$ ruby test.rb
"{\"person\":{\"name\":\"tanaka1\",\"age\":\"34\"}}"
true
"{\"person\":{\"name\":\"tanaka\",\"age\":\"1000\"}}"
"The property '#/person/age' value \"1000\" did not match the regex '^[0-9]{1,3}$'"
"{\"person\":{\"name\":null,\"age\":\"20\"}}"
"The property '#/person/name' of type NilClass did not match one or more of the following types: string"
"{\"person\":{\"name\":\"suzuki\",\"age\":null}}"
true
"{\"person\":{\"age\":null}}"
"The property '#/person' did not contain a required property of 'name'"