rack-json_schema 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 +7 -0
- data/.gitignore +17 -0
- data/CHANGELOG.md +52 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +200 -0
- data/Rakefile +1 -0
- data/bin/specup +30 -0
- data/lib/rack-json_schema.rb +1 -0
- data/lib/rack/json_schema.rb +15 -0
- data/lib/rack/json_schema/base_request_handler.rb +64 -0
- data/lib/rack/json_schema/docs.rb +38 -0
- data/lib/rack/json_schema/error.rb +30 -0
- data/lib/rack/json_schema/error_handler.rb +19 -0
- data/lib/rack/json_schema/mock.rb +93 -0
- data/lib/rack/json_schema/request_validation.rb +184 -0
- data/lib/rack/json_schema/response_validation.rb +120 -0
- data/lib/rack/json_schema/schema.rb +55 -0
- data/lib/rack/json_schema/version.rb +5 -0
- data/rack-json_schema.gemspec +31 -0
- data/spec/fixtures/schema.json +152 -0
- data/spec/rack/spec/docs_spec.rb +93 -0
- data/spec/rack/spec/mock_spec.rb +126 -0
- data/spec/rack/spec_spec.rb +221 -0
- data/spec/spec_helper.rb +11 -0
- metadata +229 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 52921d3192984f9d393b0501b2d4953b5bb3be36
|
4
|
+
data.tar.gz: 635f81c7e1ac5b97893fca271f6326aefe2d03d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0c0a01694af3f7ab3df6c1579c6f5e755aba351abf08c3e8f8fa1490e87bcdef8a99ee25bdee239dbc26f9881c44a70b958ff04cfe74bdd37df137153f4774ac
|
7
|
+
data.tar.gz: d746e6349cf8c99755502182ae338c75ef13f1eba04b8bcd287778aabe802739b1137ac91f70219504ec28c485b0a70de33a9747eb7977ca133175861a7ead8f
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
## 1.0.0
|
2
|
+
* Rename: rack-spec -> rack-json_schema
|
3
|
+
|
4
|
+
## 0.1.8
|
5
|
+
* Add Rack::JsonSchema::Docs
|
6
|
+
|
7
|
+
## 0.1.7
|
8
|
+
* Reveal `Rack::JsonSchema::Schema#links`
|
9
|
+
|
10
|
+
## 0.1.6
|
11
|
+
* Support YAML schema at `specup`
|
12
|
+
|
13
|
+
## 0.1.5
|
14
|
+
* Add `specup` executable
|
15
|
+
|
16
|
+
## 0.1.4
|
17
|
+
* Add Rack::JsonSchema::Mock
|
18
|
+
* Prettify response JSON
|
19
|
+
|
20
|
+
## v0.1.3
|
21
|
+
* Array response support of Rack::JsonSchema::ResponseValidation
|
22
|
+
|
23
|
+
## v0.1.2
|
24
|
+
* Change Content-Type validation policy
|
25
|
+
* Add Rack::JsonSchema::ResponseValidation
|
26
|
+
|
27
|
+
## v0.1.1
|
28
|
+
* Add ErrorHandler rack middleware for building error response
|
29
|
+
|
30
|
+
## v0.1.0
|
31
|
+
* Rebuilt entire code based on JSON schema
|
32
|
+
|
33
|
+
## v0.0.5
|
34
|
+
* Change RESTful resource API (#get, #post, #put, and #delete)
|
35
|
+
|
36
|
+
## v0.0.4
|
37
|
+
* Add Rack::JsonSchema::Restful, strongly conventional RESTful API Provider
|
38
|
+
|
39
|
+
## v0.0.3
|
40
|
+
* Add a new constraint: required
|
41
|
+
* More DRY way for validator definition
|
42
|
+
|
43
|
+
## v0.0.2
|
44
|
+
* Change key name: queryParameters -> parameters
|
45
|
+
* Add a new constraint: only
|
46
|
+
* Add a new constraint: minimumLength
|
47
|
+
* Add a new constraint: maxinumLength
|
48
|
+
|
49
|
+
## v0.0.1
|
50
|
+
* Add a new constraint: type
|
51
|
+
* Add a new constraint: minimum
|
52
|
+
* Add a new constraint: maxinum
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ryo Nakamura
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
# Rack::JsonSchema
|
2
|
+
[JSON Schema](http://json-schema.org/) based Rack middlewares.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
```ruby
|
6
|
+
str = File.read("schema.json")
|
7
|
+
schema = JSON.parse(str)
|
8
|
+
|
9
|
+
use Rack::JsonSchema::Docs, schema: schema
|
10
|
+
use Rack::JsonSchema::ErrorHandler
|
11
|
+
use Rack::JsonSchema::RequestValidation, schema: schema
|
12
|
+
use Rack::JsonSchema::ResponseValidation, schema: schema if ENV["RACK_ENV"] == "test"
|
13
|
+
use Rack::JsonSchema::Mock, schema: schema if ENV["RACK_ENV"] == "mock"
|
14
|
+
```
|
15
|
+
|
16
|
+
### Rack::JsonSchema::RequestValidation
|
17
|
+
Validates request and raises errors below.
|
18
|
+
|
19
|
+
* Rack::JsonSchema::RequestValidation::InvalidContentType
|
20
|
+
* Rack::JsonSchema::RequestValidation::InvalidJson
|
21
|
+
* Rack::JsonSchema::RequestValidation::InvalidParameter
|
22
|
+
* Rack::JsonSchema::RequestValidation::LinkNotFound
|
23
|
+
|
24
|
+
```sh
|
25
|
+
$ curl http://localhost:9292/users
|
26
|
+
{
|
27
|
+
"id": "link_not_found",
|
28
|
+
"message": "Not found"
|
29
|
+
}
|
30
|
+
|
31
|
+
$ curl http://localhost:9292/apps -H "Content-Type: application/json" -d "invalid-json"
|
32
|
+
{
|
33
|
+
"id": "invalid_json",
|
34
|
+
"message": "Request body wasn't valid JSON"
|
35
|
+
}
|
36
|
+
|
37
|
+
$ curl http://localhost:9292/apps -H "Content-Type: text/plain" -d "{}"
|
38
|
+
{
|
39
|
+
"id": "invalid_content_type",
|
40
|
+
"message": "Invalid content type"
|
41
|
+
}
|
42
|
+
|
43
|
+
$ curl http://localhost:9292/apps -H "Content-Type: application/json" -d '{"name":"x"}'
|
44
|
+
{
|
45
|
+
"id": "invalid_parameter",
|
46
|
+
"message": "Invalid request.\n#/name: failed schema #/definitions/app/links/0/schema/properties/name: Expected string to match pattern \"/^[a-z][a-z0-9-]{3,50}$/\", value was: x."
|
47
|
+
}
|
48
|
+
```
|
49
|
+
|
50
|
+
### Rack::JsonSchema::ResponseValidation
|
51
|
+
Validates request and raises errors below.
|
52
|
+
|
53
|
+
* Rack::JsonSchema::RequestValidation::InvalidResponseContentType
|
54
|
+
* Rack::JsonSchema::RequestValidation::InvalidResponseType
|
55
|
+
|
56
|
+
```sh
|
57
|
+
$ curl http://localhost:9292/apps
|
58
|
+
{
|
59
|
+
"id": "invalid_response_content_type",
|
60
|
+
"message": "Response Content-Type wasn't for JSON"
|
61
|
+
}
|
62
|
+
|
63
|
+
$ curl http://localhost:9292/apps
|
64
|
+
{
|
65
|
+
"id": "invalid_response_type",
|
66
|
+
"message": "#: failed schema #/definitions/app: Expected data to be of type \"object\"; value was: [\"message\", \"dummy\"]."
|
67
|
+
}
|
68
|
+
```
|
69
|
+
|
70
|
+
### Rack::JsonSchema::Mock
|
71
|
+
Generates dummy response from JSON schema.
|
72
|
+
|
73
|
+
```sh
|
74
|
+
$ curl http://localhost:9292/apps/1
|
75
|
+
[
|
76
|
+
{
|
77
|
+
"id": "01234567-89ab-cdef-0123-456789abcdef",
|
78
|
+
"name": "example"
|
79
|
+
}
|
80
|
+
]
|
81
|
+
|
82
|
+
$ curl http://localhost:9292/apps/01234567-89ab-cdef-0123-456789abcdef
|
83
|
+
{
|
84
|
+
"id": "01234567-89ab-cdef-0123-456789abcdef",
|
85
|
+
"name": "example"
|
86
|
+
}
|
87
|
+
|
88
|
+
$ curl http://localhost:9292/apps/1 -d '{"name":"example"}'
|
89
|
+
{
|
90
|
+
"id": "01234567-89ab-cdef-0123-456789abcdef",
|
91
|
+
"name": "example"
|
92
|
+
}
|
93
|
+
|
94
|
+
$ curl http://localhost:9292/recipes
|
95
|
+
{
|
96
|
+
"id": "example_not_found",
|
97
|
+
"message": "No example found for #/definitions/recipe/id"
|
98
|
+
}
|
99
|
+
```
|
100
|
+
|
101
|
+
Note: `specup` executable command is bundled to rackup dummy API server.
|
102
|
+
|
103
|
+
```sh
|
104
|
+
$ specup schema.json
|
105
|
+
[2014-06-06 23:01:35] INFO WEBrick 1.3.1
|
106
|
+
[2014-06-06 23:01:35] INFO ruby 2.0.0 (2013-06-27) [x86_64-darwin12.5.0]
|
107
|
+
[2014-06-06 23:01:35] INFO WEBrick::HTTPServer#start: pid=24303 port=8080
|
108
|
+
```
|
109
|
+
|
110
|
+
### Rack::JsonSchema::ErrorHandler
|
111
|
+
Returns appropriate error response including following properties when RequestValidation raises error.
|
112
|
+
|
113
|
+
* message: Human readable message
|
114
|
+
* id: Error type identifier listed below
|
115
|
+
* example_not_found
|
116
|
+
* invalid_content_type
|
117
|
+
* invalid_json
|
118
|
+
* invalid_parameter
|
119
|
+
* invalid_response_content_type
|
120
|
+
* invalid_response_type
|
121
|
+
* link_not_found
|
122
|
+
|
123
|
+
Here is a tree of all possible errors defined in Rack::JsonSchema.
|
124
|
+
|
125
|
+
```
|
126
|
+
StandardError
|
127
|
+
|
|
128
|
+
Rack::JsonSchema::Error
|
129
|
+
|
|
130
|
+
|--Rack::JsonSchema::Mock::Error
|
131
|
+
| |
|
132
|
+
| `--Rack::JsonSchema::Mock::ExampleNotFound
|
133
|
+
|
|
134
|
+
|--Rack::JsonSchema::RequestValidation::Error
|
135
|
+
| |
|
136
|
+
| |--Rack::JsonSchema::RequestValidation::InvalidContentType
|
137
|
+
| |
|
138
|
+
| |--Rack::JsonSchema::RequestValidation::InvalidJson
|
139
|
+
| |
|
140
|
+
| |--Rack::JsonSchema::RequestValidation::InvalidParameter
|
141
|
+
| |
|
142
|
+
| `--Rack::JsonSchema::RequestValidation::LinkNotFound
|
143
|
+
|
|
144
|
+
`--Rack::JsonSchema::ResponseValidation::Error
|
145
|
+
|
|
146
|
+
|--Rack::JsonSchema::ResponseValidation::InvalidResponseContentType
|
147
|
+
|
|
148
|
+
`--Rack::JsonSchema::ResponseValidation::InvalidResponseType
|
149
|
+
```
|
150
|
+
|
151
|
+
### Rack::JsonSchema::Docs
|
152
|
+
Returns API documentation as a text/plain content, rendered in GitHub flavored Markdown.
|
153
|
+
|
154
|
+
* You can give `path` option to change default path: `GET /docs`
|
155
|
+
* API documentation is powered by [jdoc](https://github.com/r7kamura/jdoc) gem
|
156
|
+
* This middleware is also bundled in the `specup` executable command
|
157
|
+
|
158
|
+
```sh
|
159
|
+
$ specup schema.json
|
160
|
+
[2014-06-06 23:01:35] INFO WEBrick 1.3.1
|
161
|
+
[2014-06-06 23:01:35] INFO ruby 2.0.0 (2013-06-27) [x86_64-darwin12.5.0]
|
162
|
+
[2014-06-06 23:01:35] INFO WEBrick::HTTPServer#start: pid=24303 port=8080
|
163
|
+
|
164
|
+
$ curl :8080/docs -i
|
165
|
+
HTTP/1.1 200 OK
|
166
|
+
Content-Type: text/plain; charset=utf-8
|
167
|
+
Server: WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
|
168
|
+
Date: Sat, 07 Jun 2014 19:58:04 GMT
|
169
|
+
Content-Length: 2175
|
170
|
+
Connection: Keep-Alive
|
171
|
+
|
172
|
+
# Example API
|
173
|
+
* [App](#app)
|
174
|
+
* [GET /apps](#get-apps)
|
175
|
+
* [POST /apps](#post-apps)
|
176
|
+
* [GET /apps/:id](#get-appsid)
|
177
|
+
* [PATCH /apps/:id](#patch-appsid)
|
178
|
+
* [DELETE /apps/:id](#delete-appsid)
|
179
|
+
* [Recipe](#recipe)
|
180
|
+
* [GET /recipes](#get-recipes)
|
181
|
+
|
182
|
+
## App
|
183
|
+
An app is a program to be deployed.
|
184
|
+
|
185
|
+
### Properties
|
186
|
+
* id - unique identifier of app
|
187
|
+
* Example: `01234567-89ab-cdef-0123-456789abcdef`
|
188
|
+
* Type: string
|
189
|
+
* Format: uuid
|
190
|
+
* ReadOnly: true
|
191
|
+
* name - unique name of app
|
192
|
+
* Example: `example`
|
193
|
+
* Type: string
|
194
|
+
* Patern: `(?-mix:^[a-z][a-z0-9-]{3,50}$)`
|
195
|
+
|
196
|
+
### GET /apps
|
197
|
+
List existing apps.
|
198
|
+
|
199
|
+
...
|
200
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/specup
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
3
|
+
require "rack"
|
4
|
+
require "rack/json_schema"
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
begin
|
8
|
+
path = ARGV.shift or raise
|
9
|
+
str = File.read(path)
|
10
|
+
rescue
|
11
|
+
puts "Usage: #{$0} schema.json [rack options]"
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
|
15
|
+
case File.extname(path)
|
16
|
+
when ".yml", ".yaml"
|
17
|
+
schema = YAML.load(str)
|
18
|
+
else
|
19
|
+
schema = MultiJson.decode(str)
|
20
|
+
end
|
21
|
+
|
22
|
+
app = Rack::Builder.new do
|
23
|
+
use Rack::JsonSchema::Docs, schema: schema
|
24
|
+
use Rack::JsonSchema::ErrorHandler
|
25
|
+
use Rack::JsonSchema::Mock, schema: schema
|
26
|
+
run ->(env) { [404, {}, ["Not found"]] }
|
27
|
+
end
|
28
|
+
|
29
|
+
options = Rack::Server::Options.new.parse!(ARGV).merge(app: app)
|
30
|
+
Rack::Server.start(options)
|
@@ -0,0 +1 @@
|
|
1
|
+
require "rack/json_schema"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "jdoc"
|
2
|
+
require "json"
|
3
|
+
require "json_schema"
|
4
|
+
require "multi_json"
|
5
|
+
require "rack"
|
6
|
+
|
7
|
+
require "rack/json_schema/base_request_handler"
|
8
|
+
require "rack/json_schema/error"
|
9
|
+
require "rack/json_schema/docs"
|
10
|
+
require "rack/json_schema/error_handler"
|
11
|
+
require "rack/json_schema/mock"
|
12
|
+
require "rack/json_schema/request_validation"
|
13
|
+
require "rack/json_schema/response_validation"
|
14
|
+
require "rack/json_schema/schema"
|
15
|
+
require "rack/json_schema/version"
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Rack
|
2
|
+
module JsonSchema
|
3
|
+
# Base class for providing some utility methods to handle Rack env and JSON Schema
|
4
|
+
class BaseRequestHandler
|
5
|
+
# Utility wrapper method
|
6
|
+
def self.call(**args)
|
7
|
+
new(**args).call
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param env [Hash] Rack env
|
11
|
+
# @param schema [JsonSchema::Schema] Schema object
|
12
|
+
def initialize(env: nil, schema: nil)
|
13
|
+
@env = env
|
14
|
+
@schema = schema
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Treats env as a utility object to easily extract method and path
|
20
|
+
# @return [Rack::Request]
|
21
|
+
def request
|
22
|
+
@request ||= Rack::Request.new(@env)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] HTTP request method
|
26
|
+
# @example
|
27
|
+
# method #=> "GET"
|
28
|
+
def method
|
29
|
+
request.request_method
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String] Request path
|
33
|
+
# @example
|
34
|
+
# path #=> "/recipes"
|
35
|
+
def path
|
36
|
+
request.path_info
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [JsonSchema::Schema::Link, nil] Link for the current action
|
40
|
+
def link
|
41
|
+
if instance_variable_defined?(:@link)
|
42
|
+
@link
|
43
|
+
else
|
44
|
+
@link = @schema.link_for(method: method, path: path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [true, false] True if link is defined for the current action
|
49
|
+
def has_link_for_current_action?
|
50
|
+
!!link
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [JsonSchema::Schema] Schema for current link, specified by targetSchema or parent schema
|
54
|
+
def schema_for_current_link
|
55
|
+
link.target_schema || link.parent
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [true, false] True if response is intended to be list data
|
59
|
+
def has_list_data?
|
60
|
+
link.rel == "instances" && !link.target_schema
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Rack
|
2
|
+
module JsonSchema
|
3
|
+
class Docs
|
4
|
+
DEFAULT_PATH = "/docs"
|
5
|
+
|
6
|
+
# Behaves as a rack-middleware
|
7
|
+
# @param app [Object] Rack application
|
8
|
+
# @param path [String, nil] URL path to return document (default: /docs)
|
9
|
+
# @param schema [Hash] Schema object written in JSON schema format
|
10
|
+
def initialize(app, path: nil, schema: nil)
|
11
|
+
@app = app
|
12
|
+
@path = path
|
13
|
+
@document = Jdoc::Generator.call(schema)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns rendered document for document request
|
17
|
+
# @param env [Hash] Rack env
|
18
|
+
def call(env)
|
19
|
+
if env["REQUEST_METHOD"] == "GET" && env["PATH_INFO"] == path
|
20
|
+
[
|
21
|
+
200,
|
22
|
+
{ "Content-Type" => "text/plain; charset=utf-8" },
|
23
|
+
[@document],
|
24
|
+
]
|
25
|
+
else
|
26
|
+
@app.call(env)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# @return [String] Path to return document
|
33
|
+
def path
|
34
|
+
@path || DEFAULT_PATH
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|