json_api_conformant 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.travis.yml +1 -9
- data/CODE_OF_CONDUCT.md +74 -0
- data/LICENSE.txt +1 -1
- data/README.md +24 -21
- data/json_api_conformant.gemspec +4 -4
- data/lib/json_api_conformant/conformant.rb +7 -7
- data/lib/json_api_conformant/version.rb +1 -1
- data/schemas/1.0.json +370 -0
- data/spec/fixtures/invalid.json +39 -17
- data/spec/fixtures/valid.json +67 -48
- data/spec/json_api_conformant/conformant_spec.rb +52 -4
- data/spec/json_api_conformant/matchers/rspec_spec.rb +21 -3
- metadata +26 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc3bcb951985dd2a665cdb84f7b1c6ce2c539b86
|
4
|
+
data.tar.gz: 88e96d8554af56e995f8b3dca3161473209e31c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7808472e832d55a99c7daade9c44a370a66f443bf353b775d60a53781e86e41ea6f764473f91c288c359403f16eb90e2d819adfc3ab3364e7dc8e84a737e3b9b
|
7
|
+
data.tar.gz: 3749fa0fc26839e391630fc0d61e4e506036b1441dfa2f42474f64cf039d5a8b34961d0595eb0d519b0a688aed9e3c6d7a176f7ffb3f89b57ffe0116c5a750b1
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
CHANGED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, sexual identity and
|
10
|
+
orientation, or sexual proclivities between consenting adults.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project maintainer at [sebasoga@gmail.com]. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4.1,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4/1][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
[](https://travis-ci.org/sebasoga/json_api_conformant)
|
3
3
|
[](https://codeclimate.com/github/sebasoga/json_api_conformant)
|
4
4
|
|
5
|
-
When building an API in JSON it is important to follow conventions
|
6
|
-
|
7
|
-
|
5
|
+
When building an API in JSON it is important to follow conventions so that the
|
6
|
+
clients know how what to expect in the response when operating on the available
|
7
|
+
resources.
|
8
8
|
|
9
|
-
JSON API Conformant provides a narrow interface for validating JSON objects
|
10
|
-
against
|
11
|
-
[
|
9
|
+
JSON API Conformant provides a narrow interface for validating JSON objects
|
10
|
+
against the [JSON API specification](http://jsonapi.org/) using
|
11
|
+
[the official schema](http://jsonapi.org/schema) and provides a custom RSpec
|
12
12
|
matcher for use in your tests.
|
13
13
|
|
14
|
-
**Important:**
|
15
|
-
document. The fact that a JSON document
|
16
|
-
not necessarily mean it is a valid JSON
|
17
|
-
JSON API for a base level sanity check.
|
14
|
+
**Important:** Currently v1.0 specification is used as the default base schema.
|
15
|
+
The base schema is not a perfect document. The fact that a JSON document
|
16
|
+
validates against this schema, it does not necessarily mean it is a valid JSON
|
17
|
+
API document. The schema is provided by JSON API for a base level sanity check.
|
18
18
|
|
19
19
|
## Installation
|
20
20
|
|
@@ -36,8 +36,8 @@ Or install it yourself as:
|
|
36
36
|
|
37
37
|
Validate if your JSON object is JSON API conformant.
|
38
38
|
```ruby
|
39
|
-
valid_schema = {"
|
40
|
-
invalid_schema = {"
|
39
|
+
valid_schema = { "data" => [{ "type" => "posts", "id" => "1" }] }
|
40
|
+
invalid_schema = { "data" => [{ "posts" => "1" }] }
|
41
41
|
|
42
42
|
JSON::API::Conformant.valid?(valid_schema) # => true
|
43
43
|
JSON::API::Conformant.valid?(invalid_schema) # => false
|
@@ -45,30 +45,30 @@ JSON::API::Conformant.valid?(invalid_schema) # => false
|
|
45
45
|
|
46
46
|
Get errors when your JSON object is JSON API not conformant.
|
47
47
|
```ruby
|
48
|
-
valid_schema = {"
|
49
|
-
invalid_schema = {"
|
48
|
+
valid_schema = { "data" => [{ "type" => "posts", "id" => "1" }] }
|
49
|
+
invalid_schema = { "data" => [{ "posts" => "1" }] }
|
50
50
|
|
51
51
|
JSON::API::Conformant.validate(valid_schema) # => []
|
52
|
-
JSON::API::Conformant.validate(invalid_schema) # => ["The property '#/
|
52
|
+
JSON::API::Conformant.validate(invalid_schema) # => ["The property '#/' of type object did not match any of the..."]
|
53
53
|
```
|
54
54
|
|
55
|
-
JSON API Conformant wraps [json-schema](https://github.com/hoxworth/json-schema),
|
55
|
+
JSON API Conformant wraps [json-schema](https://github.com/hoxworth/json-schema),
|
56
56
|
so other options that validator accepts will work here too.
|
57
57
|
```ruby
|
58
|
-
schema = {"
|
58
|
+
schema = { "data" => [{ "posts" => "1" }] }
|
59
59
|
|
60
|
-
JSON::API::Conformant.valid?(
|
61
|
-
JSON::API::Conformant.validate(
|
60
|
+
JSON::API::Conformant.valid?(schema, insert_defaults: true)
|
61
|
+
JSON::API::Conformant.validate(schema, errors_as_objects: true)
|
62
62
|
```
|
63
63
|
|
64
64
|
|
65
65
|
### RSpec Matcher
|
66
|
-
Only tested
|
66
|
+
Only tested for RSpec 3.
|
67
67
|
|
68
68
|
It is pretty straighword to use.
|
69
69
|
```ruby
|
70
70
|
it "validates that's JSON API conformant" do
|
71
|
-
data = {"
|
71
|
+
data = { "data" => [{ "posts" => "1" }] }
|
72
72
|
expect(data).to be_json_api_conformant
|
73
73
|
end
|
74
74
|
```
|
@@ -82,6 +82,9 @@ end
|
|
82
82
|
5. Push to the branch (`git push origin my-new-feature`)
|
83
83
|
6. Create a new Pull Request
|
84
84
|
|
85
|
+
Everyone interacting in this codebase and issue tracker is expected to follow
|
86
|
+
the [code of conduct](https://github.com/sebasoga/json_api_conformant/blob/master/CODE_OF_CONDUCT.md).
|
87
|
+
|
85
88
|
## License
|
86
89
|
|
87
90
|
See [LICENSE](https://github.com/sebasoga/json_api_conformant/blob/master/LICENSE.txt)
|
data/json_api_conformant.gemspec
CHANGED
@@ -18,8 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_development_dependency "bundler", "~> 1.
|
22
|
-
spec.add_development_dependency "rake", "~>
|
23
|
-
spec.add_dependency "rspec", "~> 3.
|
24
|
-
spec.add_dependency "json-schema", "~> 2.
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
22
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
23
|
+
spec.add_dependency "rspec", "~> 3.5"
|
24
|
+
spec.add_dependency "json-schema", "~> 2.8"
|
25
25
|
end
|
@@ -3,15 +3,15 @@ require 'json-schema'
|
|
3
3
|
module JSON
|
4
4
|
module API
|
5
5
|
class Conformant
|
6
|
-
|
6
|
+
DEFAULT_SCHEMA_VERSION = '1.0'
|
7
7
|
|
8
8
|
class << self
|
9
|
-
def valid?(data, options
|
10
|
-
validator.validate(schema, data, options)
|
9
|
+
def valid?(data, version: DEFAULT_SCHEMA_VERSION, **options)
|
10
|
+
validator.validate(schema(version), data, options)
|
11
11
|
end
|
12
12
|
|
13
|
-
def validate(data, options
|
14
|
-
validator.fully_validate(schema, data, options)
|
13
|
+
def validate(data, version: DEFAULT_SCHEMA_VERSION, **options)
|
14
|
+
validator.fully_validate(schema(version), data, options)
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
@@ -20,10 +20,10 @@ module JSON
|
|
20
20
|
JSON::Validator
|
21
21
|
end
|
22
22
|
|
23
|
-
def schema
|
23
|
+
def schema(version)
|
24
24
|
File.join(Pathname.new(File.dirname(__FILE__)).parent.parent,
|
25
25
|
'schemas',
|
26
|
-
"#{
|
26
|
+
"#{version}.json"
|
27
27
|
)
|
28
28
|
end
|
29
29
|
end
|
data/schemas/1.0.json
ADDED
@@ -0,0 +1,370 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "JSON API Schema",
|
4
|
+
"description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org",
|
5
|
+
"oneOf": [
|
6
|
+
{
|
7
|
+
"$ref": "#/definitions/success"
|
8
|
+
},
|
9
|
+
{
|
10
|
+
"$ref": "#/definitions/failure"
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"$ref": "#/definitions/info"
|
14
|
+
}
|
15
|
+
],
|
16
|
+
|
17
|
+
"definitions": {
|
18
|
+
"success": {
|
19
|
+
"type": "object",
|
20
|
+
"required": [
|
21
|
+
"data"
|
22
|
+
],
|
23
|
+
"properties": {
|
24
|
+
"data": {
|
25
|
+
"$ref": "#/definitions/data"
|
26
|
+
},
|
27
|
+
"included": {
|
28
|
+
"description": "To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called \"compound documents\".",
|
29
|
+
"type": "array",
|
30
|
+
"items": {
|
31
|
+
"$ref": "#/definitions/resource"
|
32
|
+
},
|
33
|
+
"uniqueItems": true
|
34
|
+
},
|
35
|
+
"meta": {
|
36
|
+
"$ref": "#/definitions/meta"
|
37
|
+
},
|
38
|
+
"links": {
|
39
|
+
"description": "Link members related to the primary data.",
|
40
|
+
"allOf": [
|
41
|
+
{
|
42
|
+
"$ref": "#/definitions/links"
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"$ref": "#/definitions/pagination"
|
46
|
+
}
|
47
|
+
]
|
48
|
+
},
|
49
|
+
"jsonapi": {
|
50
|
+
"$ref": "#/definitions/jsonapi"
|
51
|
+
}
|
52
|
+
},
|
53
|
+
"additionalProperties": false
|
54
|
+
},
|
55
|
+
"failure": {
|
56
|
+
"type": "object",
|
57
|
+
"required": [
|
58
|
+
"errors"
|
59
|
+
],
|
60
|
+
"properties": {
|
61
|
+
"errors": {
|
62
|
+
"type": "array",
|
63
|
+
"items": {
|
64
|
+
"$ref": "#/definitions/error"
|
65
|
+
},
|
66
|
+
"uniqueItems": true
|
67
|
+
},
|
68
|
+
"meta": {
|
69
|
+
"$ref": "#/definitions/meta"
|
70
|
+
},
|
71
|
+
"jsonapi": {
|
72
|
+
"$ref": "#/definitions/jsonapi"
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"additionalProperties": false
|
76
|
+
},
|
77
|
+
"info": {
|
78
|
+
"type": "object",
|
79
|
+
"required": [
|
80
|
+
"meta"
|
81
|
+
],
|
82
|
+
"properties": {
|
83
|
+
"meta": {
|
84
|
+
"$ref": "#/definitions/meta"
|
85
|
+
},
|
86
|
+
"links": {
|
87
|
+
"$ref": "#/definitions/links"
|
88
|
+
},
|
89
|
+
"jsonapi": {
|
90
|
+
"$ref": "#/definitions/jsonapi"
|
91
|
+
}
|
92
|
+
},
|
93
|
+
"additionalProperties": false
|
94
|
+
},
|
95
|
+
|
96
|
+
"meta": {
|
97
|
+
"description": "Non-standard meta-information that can not be represented as an attribute or relationship.",
|
98
|
+
"type": "object",
|
99
|
+
"additionalProperties": true
|
100
|
+
},
|
101
|
+
"data": {
|
102
|
+
"description": "The document's \"primary data\" is a representation of the resource or collection of resources targeted by a request.",
|
103
|
+
"oneOf": [
|
104
|
+
{
|
105
|
+
"$ref": "#/definitions/resource"
|
106
|
+
},
|
107
|
+
{
|
108
|
+
"description": "An array of resource objects, an array of resource identifier objects, or an empty array ([]), for requests that target resource collections.",
|
109
|
+
"type": "array",
|
110
|
+
"items": {
|
111
|
+
"$ref": "#/definitions/resource"
|
112
|
+
},
|
113
|
+
"uniqueItems": true
|
114
|
+
},
|
115
|
+
{
|
116
|
+
"description": "null if the request is one that might correspond to a single resource, but doesn't currently.",
|
117
|
+
"type": "null"
|
118
|
+
}
|
119
|
+
]
|
120
|
+
},
|
121
|
+
"resource": {
|
122
|
+
"description": "\"Resource objects\" appear in a JSON API document to represent resources.",
|
123
|
+
"type": "object",
|
124
|
+
"required": [
|
125
|
+
"type",
|
126
|
+
"id"
|
127
|
+
],
|
128
|
+
"properties": {
|
129
|
+
"type": {
|
130
|
+
"type": "string"
|
131
|
+
},
|
132
|
+
"id": {
|
133
|
+
"type": "string"
|
134
|
+
},
|
135
|
+
"attributes": {
|
136
|
+
"$ref": "#/definitions/attributes"
|
137
|
+
},
|
138
|
+
"relationships": {
|
139
|
+
"$ref": "#/definitions/relationships"
|
140
|
+
},
|
141
|
+
"links": {
|
142
|
+
"$ref": "#/definitions/links"
|
143
|
+
},
|
144
|
+
"meta": {
|
145
|
+
"$ref": "#/definitions/meta"
|
146
|
+
}
|
147
|
+
},
|
148
|
+
"additionalProperties": false
|
149
|
+
},
|
150
|
+
|
151
|
+
"links": {
|
152
|
+
"description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.",
|
153
|
+
"type": "object",
|
154
|
+
"properties": {
|
155
|
+
"self": {
|
156
|
+
"description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.",
|
157
|
+
"type": "string",
|
158
|
+
"format": "uri"
|
159
|
+
},
|
160
|
+
"related": {
|
161
|
+
"$ref": "#/definitions/link"
|
162
|
+
}
|
163
|
+
},
|
164
|
+
"additionalProperties": true
|
165
|
+
},
|
166
|
+
"link": {
|
167
|
+
"description": "A link **MUST** be represented as either: a string containing the link's URL or a link object.",
|
168
|
+
"oneOf": [
|
169
|
+
{
|
170
|
+
"description": "A string containing the link's URL.",
|
171
|
+
"type": "string",
|
172
|
+
"format": "uri"
|
173
|
+
},
|
174
|
+
{
|
175
|
+
"type": "object",
|
176
|
+
"required": [
|
177
|
+
"href"
|
178
|
+
],
|
179
|
+
"properties": {
|
180
|
+
"href": {
|
181
|
+
"description": "A string containing the link's URL.",
|
182
|
+
"type": "string",
|
183
|
+
"format": "uri"
|
184
|
+
},
|
185
|
+
"meta": {
|
186
|
+
"$ref": "#/definitions/meta"
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
]
|
191
|
+
},
|
192
|
+
|
193
|
+
"attributes": {
|
194
|
+
"description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.",
|
195
|
+
"type": "object",
|
196
|
+
"patternProperties": {
|
197
|
+
"^(?!relationships$|links$)\\w[-\\w_]*$": {
|
198
|
+
"description": "Attributes may contain any valid JSON value."
|
199
|
+
}
|
200
|
+
},
|
201
|
+
"additionalProperties": false
|
202
|
+
},
|
203
|
+
|
204
|
+
"relationships": {
|
205
|
+
"description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.",
|
206
|
+
"type": "object",
|
207
|
+
"patternProperties": {
|
208
|
+
"^\\w[-\\w_]*$": {
|
209
|
+
"properties": {
|
210
|
+
"links": {
|
211
|
+
"$ref": "#/definitions/links"
|
212
|
+
},
|
213
|
+
"data": {
|
214
|
+
"description": "Member, whose value represents \"resource linkage\".",
|
215
|
+
"oneOf": [
|
216
|
+
{
|
217
|
+
"$ref": "#/definitions/relationshipToOne"
|
218
|
+
},
|
219
|
+
{
|
220
|
+
"$ref": "#/definitions/relationshipToMany"
|
221
|
+
}
|
222
|
+
]
|
223
|
+
},
|
224
|
+
"meta": {
|
225
|
+
"$ref": "#/definitions/meta"
|
226
|
+
}
|
227
|
+
},
|
228
|
+
"additionalProperties": false
|
229
|
+
}
|
230
|
+
},
|
231
|
+
"additionalProperties": false
|
232
|
+
},
|
233
|
+
"relationshipToOne": {
|
234
|
+
"description": "References to other resource objects in a to-one (\"relationship\"). Relationships can be specified by including a member in a resource's links object.",
|
235
|
+
"anyOf": [
|
236
|
+
{
|
237
|
+
"$ref": "#/definitions/empty"
|
238
|
+
},
|
239
|
+
{
|
240
|
+
"$ref": "#/definitions/linkage"
|
241
|
+
}
|
242
|
+
]
|
243
|
+
},
|
244
|
+
"relationshipToMany": {
|
245
|
+
"description": "An array of objects each containing \"type\" and \"id\" members for to-many relationships.",
|
246
|
+
"type": "array",
|
247
|
+
"items": {
|
248
|
+
"$ref": "#/definitions/linkage"
|
249
|
+
},
|
250
|
+
"uniqueItems": true
|
251
|
+
},
|
252
|
+
"empty": {
|
253
|
+
"description": "Describes an empty to-one relationship.",
|
254
|
+
"type": "null"
|
255
|
+
},
|
256
|
+
"linkage": {
|
257
|
+
"description": "The \"type\" and \"id\" to non-empty members.",
|
258
|
+
"type": "object",
|
259
|
+
"required": [
|
260
|
+
"type",
|
261
|
+
"id"
|
262
|
+
],
|
263
|
+
"properties": {
|
264
|
+
"type": {
|
265
|
+
"type": "string"
|
266
|
+
},
|
267
|
+
"id": {
|
268
|
+
"type": "string"
|
269
|
+
},
|
270
|
+
"meta": {
|
271
|
+
"$ref": "#/definitions/meta"
|
272
|
+
}
|
273
|
+
},
|
274
|
+
"additionalProperties": false
|
275
|
+
},
|
276
|
+
"pagination": {
|
277
|
+
"type": "object",
|
278
|
+
"properties": {
|
279
|
+
"first": {
|
280
|
+
"description": "The first page of data",
|
281
|
+
"oneOf": [
|
282
|
+
{ "type": "string", "format": "uri" },
|
283
|
+
{ "type": "null" }
|
284
|
+
]
|
285
|
+
},
|
286
|
+
"last": {
|
287
|
+
"description": "The last page of data",
|
288
|
+
"oneOf": [
|
289
|
+
{ "type": "string", "format": "uri" },
|
290
|
+
{ "type": "null" }
|
291
|
+
]
|
292
|
+
},
|
293
|
+
"prev": {
|
294
|
+
"description": "The previous page of data",
|
295
|
+
"oneOf": [
|
296
|
+
{ "type": "string", "format": "uri" },
|
297
|
+
{ "type": "null" }
|
298
|
+
]
|
299
|
+
},
|
300
|
+
"next": {
|
301
|
+
"description": "The next page of data",
|
302
|
+
"oneOf": [
|
303
|
+
{ "type": "string", "format": "uri" },
|
304
|
+
{ "type": "null" }
|
305
|
+
]
|
306
|
+
}
|
307
|
+
}
|
308
|
+
},
|
309
|
+
|
310
|
+
"jsonapi": {
|
311
|
+
"description": "An object describing the server's implementation",
|
312
|
+
"type": "object",
|
313
|
+
"properties": {
|
314
|
+
"version": {
|
315
|
+
"type": "string"
|
316
|
+
},
|
317
|
+
"meta": {
|
318
|
+
"$ref": "#/definitions/meta"
|
319
|
+
}
|
320
|
+
},
|
321
|
+
"additionalProperties": false
|
322
|
+
},
|
323
|
+
|
324
|
+
"error": {
|
325
|
+
"type": "object",
|
326
|
+
"properties": {
|
327
|
+
"id": {
|
328
|
+
"description": "A unique identifier for this particular occurrence of the problem.",
|
329
|
+
"type": "string"
|
330
|
+
},
|
331
|
+
"links": {
|
332
|
+
"$ref": "#/definitions/links"
|
333
|
+
},
|
334
|
+
"status": {
|
335
|
+
"description": "The HTTP status code applicable to this problem, expressed as a string value.",
|
336
|
+
"type": "string"
|
337
|
+
},
|
338
|
+
"code": {
|
339
|
+
"description": "An application-specific error code, expressed as a string value.",
|
340
|
+
"type": "string"
|
341
|
+
},
|
342
|
+
"title": {
|
343
|
+
"description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.",
|
344
|
+
"type": "string"
|
345
|
+
},
|
346
|
+
"detail": {
|
347
|
+
"description": "A human-readable explanation specific to this occurrence of the problem.",
|
348
|
+
"type": "string"
|
349
|
+
},
|
350
|
+
"source": {
|
351
|
+
"type": "object",
|
352
|
+
"properties": {
|
353
|
+
"pointer": {
|
354
|
+
"description": "A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].",
|
355
|
+
"type": "string"
|
356
|
+
},
|
357
|
+
"parameter": {
|
358
|
+
"description": "A string indicating which query parameter caused the error.",
|
359
|
+
"type": "string"
|
360
|
+
}
|
361
|
+
}
|
362
|
+
},
|
363
|
+
"meta": {
|
364
|
+
"$ref": "#/definitions/meta"
|
365
|
+
}
|
366
|
+
},
|
367
|
+
"additionalProperties": false
|
368
|
+
}
|
369
|
+
}
|
370
|
+
}
|
data/spec/fixtures/invalid.json
CHANGED
@@ -1,24 +1,46 @@
|
|
1
1
|
{
|
2
2
|
"links": {
|
3
|
-
"
|
4
|
-
|
5
|
-
|
3
|
+
"self": "http://example.com/articles",
|
4
|
+
"next": "http://example.com/articles?page[offset]=2",
|
5
|
+
"last": "http://example.com/articles?page[offset]=10"
|
6
6
|
},
|
7
|
-
"
|
8
|
-
"
|
9
|
-
"
|
10
|
-
"
|
11
|
-
}
|
12
|
-
"
|
13
|
-
|
7
|
+
"data": [{
|
8
|
+
"type": "articles",
|
9
|
+
"attributes": {
|
10
|
+
"title": "JSON API paints my bikeshed!"
|
11
|
+
},
|
12
|
+
"relationships": {
|
13
|
+
"author": {
|
14
|
+
"links": {
|
15
|
+
"self": "http://example.com/articles/1/relationships/author",
|
16
|
+
"related": "http://example.com/articles/1/author"
|
17
|
+
},
|
18
|
+
"data": { "type": "people", "id": "9" }
|
19
|
+
},
|
20
|
+
"comments": {
|
21
|
+
"links": {
|
22
|
+
"self": "http://example.com/articles/1/relationships/comments",
|
23
|
+
"related": "http://example.com/articles/1/comments"
|
24
|
+
},
|
25
|
+
"data": [
|
26
|
+
{ "type": "comments", "id": "5" },
|
27
|
+
{ "type": "comments", "id": "12" }
|
28
|
+
]
|
29
|
+
}
|
30
|
+
},
|
14
31
|
"links": {
|
15
|
-
"
|
16
|
-
"comments": "6"
|
32
|
+
"self": "http://example.com/articles/1"
|
17
33
|
}
|
18
34
|
}],
|
19
|
-
"
|
20
|
-
"
|
21
|
-
|
22
|
-
|
23
|
-
|
35
|
+
"included": [{
|
36
|
+
"type": "people",
|
37
|
+
"attributes": {
|
38
|
+
"first-name": "Dan",
|
39
|
+
"last-name": "Gebhardt",
|
40
|
+
"twitter": "dgeb"
|
41
|
+
},
|
42
|
+
"links": {
|
43
|
+
"self": "http://example.com/people/9"
|
44
|
+
}
|
45
|
+
}]
|
24
46
|
}
|
data/spec/fixtures/valid.json
CHANGED
@@ -1,57 +1,76 @@
|
|
1
1
|
{
|
2
2
|
"links": {
|
3
|
-
"
|
4
|
-
|
5
|
-
|
6
|
-
},
|
7
|
-
"posts.comments": {
|
8
|
-
"href": "http://example.com/comments/{posts.comments}",
|
9
|
-
"type": "comments"
|
10
|
-
}
|
3
|
+
"self": "http://example.com/articles",
|
4
|
+
"next": "http://example.com/articles?page[offset]=2",
|
5
|
+
"last": "http://example.com/articles?page[offset]=10"
|
11
6
|
},
|
12
|
-
"
|
7
|
+
"data": [{
|
8
|
+
"type": "articles",
|
13
9
|
"id": "1",
|
14
|
-
"
|
10
|
+
"attributes": {
|
11
|
+
"title": "JSON API paints my bikeshed!"
|
12
|
+
},
|
13
|
+
"relationships": {
|
14
|
+
"author": {
|
15
|
+
"links": {
|
16
|
+
"self": "http://example.com/articles/1/relationships/author",
|
17
|
+
"related": "http://example.com/articles/1/author"
|
18
|
+
},
|
19
|
+
"data": { "type": "people", "id": "9" }
|
20
|
+
},
|
21
|
+
"comments": {
|
22
|
+
"links": {
|
23
|
+
"self": "http://example.com/articles/1/relationships/comments",
|
24
|
+
"related": "http://example.com/articles/1/comments"
|
25
|
+
},
|
26
|
+
"data": [
|
27
|
+
{ "type": "comments", "id": "5" },
|
28
|
+
{ "type": "comments", "id": "12" }
|
29
|
+
]
|
30
|
+
}
|
31
|
+
},
|
15
32
|
"links": {
|
16
|
-
"
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
"
|
33
|
+
"self": "http://example.com/articles/1"
|
34
|
+
}
|
35
|
+
}],
|
36
|
+
"included": [{
|
37
|
+
"type": "people",
|
38
|
+
"id": "9",
|
39
|
+
"attributes": {
|
40
|
+
"first-name": "Dan",
|
41
|
+
"last-name": "Gebhardt",
|
42
|
+
"twitter": "dgeb"
|
43
|
+
},
|
21
44
|
"links": {
|
22
|
-
"
|
23
|
-
|
24
|
-
|
25
|
-
"
|
26
|
-
"
|
45
|
+
"self": "http://example.com/people/9"
|
46
|
+
}
|
47
|
+
}, {
|
48
|
+
"type": "comments",
|
49
|
+
"id": "5",
|
50
|
+
"attributes": {
|
51
|
+
"body": "First!"
|
52
|
+
},
|
53
|
+
"relationships": {
|
54
|
+
"author": {
|
55
|
+
"data": { "type": "people", "id": "2" }
|
56
|
+
}
|
57
|
+
},
|
27
58
|
"links": {
|
28
|
-
"
|
29
|
-
"comments": [ "6" ]
|
59
|
+
"self": "http://example.com/comments/5"
|
30
60
|
}
|
31
|
-
}
|
32
|
-
|
33
|
-
"
|
34
|
-
|
35
|
-
"
|
36
|
-
}
|
37
|
-
"
|
38
|
-
"
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
}, {
|
47
|
-
"id": "4",
|
48
|
-
"body": "Parley is a discussion, especially one between enemies"
|
49
|
-
}, {
|
50
|
-
"id": "5",
|
51
|
-
"body": "The parsley letter"
|
52
|
-
}, {
|
53
|
-
"id": "6",
|
54
|
-
"body": "Dependency Injection is Not a Vice"
|
55
|
-
}]
|
56
|
-
}
|
61
|
+
}, {
|
62
|
+
"type": "comments",
|
63
|
+
"id": "12",
|
64
|
+
"attributes": {
|
65
|
+
"body": "I like XML better"
|
66
|
+
},
|
67
|
+
"relationships": {
|
68
|
+
"author": {
|
69
|
+
"data": { "type": "people", "id": "9" }
|
70
|
+
}
|
71
|
+
},
|
72
|
+
"links": {
|
73
|
+
"self": "http://example.com/comments/12"
|
74
|
+
}
|
75
|
+
}]
|
57
76
|
}
|
@@ -7,7 +7,7 @@ describe JSON::API::Conformant do
|
|
7
7
|
|
8
8
|
subject { JSON::API::Conformant }
|
9
9
|
|
10
|
-
describe ".
|
10
|
+
describe ".valid?" do
|
11
11
|
context "when data is valid" do
|
12
12
|
it "is true" do
|
13
13
|
expect(subject.valid? valid_data).to be true
|
@@ -19,6 +19,18 @@ describe JSON::API::Conformant do
|
|
19
19
|
expect(subject.valid? invalid_data).to be false
|
20
20
|
end
|
21
21
|
end
|
22
|
+
|
23
|
+
describe "version" do
|
24
|
+
it "defaults to draft" do
|
25
|
+
expect(subject).to receive(:schema).with("1.0").and_call_original
|
26
|
+
subject.validate({})
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can be overridden" do
|
30
|
+
expect(subject).to receive(:schema).with("draft").and_call_original
|
31
|
+
subject.validate({}, version: "draft")
|
32
|
+
end
|
33
|
+
end
|
22
34
|
end
|
23
35
|
|
24
36
|
describe ".validate" do
|
@@ -30,11 +42,47 @@ describe JSON::API::Conformant do
|
|
30
42
|
|
31
43
|
context "when data is invalid" do
|
32
44
|
it "returns an array with the errors" do
|
33
|
-
errors = [
|
34
|
-
|
35
|
-
|
45
|
+
errors = [<<ERROR.chomp]
|
46
|
+
The property '#/' of type object did not match any of the required schemas. The schema specific errors were:
|
47
|
+
|
48
|
+
- oneOf #0:
|
49
|
+
- The property '#/included/0' did not contain a required property of 'id'
|
50
|
+
- The property '#/data' of type array did not match any of the required schemas. The schema specific errors were:
|
51
|
+
|
52
|
+
- oneOf #0:
|
53
|
+
- The property '#/data' of type array did not match the following type: object
|
54
|
+
- oneOf #1:
|
55
|
+
- The property '#/data/0' did not contain a required property of 'id'
|
56
|
+
- oneOf #2:
|
57
|
+
- The property '#/data' of type array did not match the following type: null
|
58
|
+
- oneOf #1:
|
59
|
+
- The property '#/' contains additional properties ["links", "data", "included"] outside of the schema when none are allowed
|
60
|
+
- The property '#/' did not contain a required property of 'errors'
|
61
|
+
- oneOf #2:
|
62
|
+
- The property '#/' contains additional properties ["data", "included"] outside of the schema when none are allowed
|
63
|
+
- The property '#/' did not contain a required property of 'meta'
|
64
|
+
ERROR
|
36
65
|
expect(subject.validate invalid_data).to eq errors
|
37
66
|
end
|
38
67
|
end
|
68
|
+
|
69
|
+
describe "version" do
|
70
|
+
it "defaults to draft" do
|
71
|
+
expect(subject).to receive(:schema).with("1.0").and_call_original
|
72
|
+
subject.validate({})
|
73
|
+
end
|
74
|
+
|
75
|
+
it "can be overridden" do
|
76
|
+
expect(subject).to receive(:schema).with("draft").and_call_original
|
77
|
+
subject.validate({}, version: "draft")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#schema" do
|
83
|
+
it "allows version to be specified" do
|
84
|
+
expect(subject.send(:schema, "1.0")).to end_with "1.0.json"
|
85
|
+
expect(subject.send(:schema, "draft")).to end_with "draft.json"
|
86
|
+
end
|
39
87
|
end
|
40
88
|
end
|
@@ -27,9 +27,27 @@ RSpec.describe "be_json_api_conformant matcher" do
|
|
27
27
|
|
28
28
|
it "returns the correct message when the test fails" do
|
29
29
|
json = parse_fixture('invalid')
|
30
|
-
expected_message = [
|
31
|
-
|
32
|
-
|
30
|
+
expected_message = [<<-ERROR.chomp]
|
31
|
+
Expected data to be JSON API conformant:
|
32
|
+
* The property '#/' of type object did not match any of the required schemas. The schema specific errors were:
|
33
|
+
|
34
|
+
- oneOf #0:
|
35
|
+
- The property '#/included/0' did not contain a required property of 'id'
|
36
|
+
- The property '#/data' of type array did not match any of the required schemas. The schema specific errors were:
|
37
|
+
|
38
|
+
- oneOf #0:
|
39
|
+
- The property '#/data' of type array did not match the following type: object
|
40
|
+
- oneOf #1:
|
41
|
+
- The property '#/data/0' did not contain a required property of 'id'
|
42
|
+
- oneOf #2:
|
43
|
+
- The property '#/data' of type array did not match the following type: null
|
44
|
+
- oneOf #1:
|
45
|
+
- The property '#/' contains additional properties ["links", "data", "included"] outside of the schema when none are allowed
|
46
|
+
- The property '#/' did not contain a required property of 'errors'
|
47
|
+
- oneOf #2:
|
48
|
+
- The property '#/' contains additional properties ["data", "included"] outside of the schema when none are allowed
|
49
|
+
- The property '#/' did not contain a required property of 'meta'
|
50
|
+
ERROR
|
33
51
|
begin
|
34
52
|
expect(json).to be_json_api_conformant
|
35
53
|
rescue RSpec::Expectations::ExpectationNotMetError => e
|
metadata
CHANGED
@@ -1,71 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_api_conformant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Sogamoso
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
16
|
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
20
|
-
|
18
|
+
version: '1.14'
|
19
|
+
name: bundler
|
21
20
|
prerelease: false
|
21
|
+
type: :development
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.14'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
30
29
|
requirements:
|
31
30
|
- - "~>"
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
|
32
|
+
version: '12.0'
|
33
|
+
name: rake
|
35
34
|
prerelease: false
|
35
|
+
type: :development
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '12.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
44
43
|
requirements:
|
45
44
|
- - "~>"
|
46
45
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
48
|
-
|
46
|
+
version: '3.5'
|
47
|
+
name: rspec
|
49
48
|
prerelease: false
|
49
|
+
type: :runtime
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
54
|
+
version: '3.5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: json-schema
|
57
56
|
requirement: !ruby/object:Gem::Requirement
|
58
57
|
requirements:
|
59
58
|
- - "~>"
|
60
59
|
- !ruby/object:Gem::Version
|
61
|
-
version: '2.
|
62
|
-
|
60
|
+
version: '2.8'
|
61
|
+
name: json-schema
|
63
62
|
prerelease: false
|
63
|
+
type: :runtime
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '2.
|
68
|
+
version: '2.8'
|
69
69
|
description: Interface for validating JSON objects against a JSON API schema.
|
70
70
|
email:
|
71
71
|
- sebasoga@gmail.com
|
@@ -74,7 +74,9 @@ extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
76
|
- ".gitignore"
|
77
|
+
- ".rspec"
|
77
78
|
- ".travis.yml"
|
79
|
+
- CODE_OF_CONDUCT.md
|
78
80
|
- Gemfile
|
79
81
|
- LICENSE.txt
|
80
82
|
- README.md
|
@@ -84,6 +86,7 @@ files:
|
|
84
86
|
- lib/json_api_conformant/conformant.rb
|
85
87
|
- lib/json_api_conformant/matchers/rspec.rb
|
86
88
|
- lib/json_api_conformant/version.rb
|
89
|
+
- schemas/1.0.json
|
87
90
|
- schemas/draft.json
|
88
91
|
- spec/fixtures/invalid.json
|
89
92
|
- spec/fixtures/valid.json
|
@@ -95,7 +98,7 @@ homepage: https://github.com/sebasoga/json_api_conformant
|
|
95
98
|
licenses:
|
96
99
|
- MIT
|
97
100
|
metadata: {}
|
98
|
-
post_install_message:
|
101
|
+
post_install_message:
|
99
102
|
rdoc_options: []
|
100
103
|
require_paths:
|
101
104
|
- lib
|
@@ -110,9 +113,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
113
|
- !ruby/object:Gem::Version
|
111
114
|
version: '0'
|
112
115
|
requirements: []
|
113
|
-
rubyforge_project:
|
114
|
-
rubygems_version: 2.
|
115
|
-
signing_key:
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.6.8
|
118
|
+
signing_key:
|
116
119
|
specification_version: 4
|
117
120
|
summary: Ruby JSON API validator.
|
118
121
|
test_files:
|
@@ -122,4 +125,3 @@ test_files:
|
|
122
125
|
- spec/json_api_conformant/matchers/rspec_spec.rb
|
123
126
|
- spec/spec_helper.rb
|
124
127
|
- spec/support/json_parser.rb
|
125
|
-
has_rdoc:
|