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 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