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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # JsonSchemaify your gem's dependencies in rack-spec.gemspec
4
+ gemspec
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