rack-json_schema 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|