rack-spec 0.1.0 → 0.1.1
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/CHANGELOG.md +3 -0
- data/README.md +41 -3
- data/lib/rack/spec.rb +2 -0
- data/lib/rack/spec/error.rb +30 -0
- data/lib/rack/spec/error_handler.rb +19 -0
- data/lib/rack/spec/request_validation.rb +72 -7
- data/lib/rack/spec/version.rb +1 -1
- data/rack-spec.gemspec +1 -0
- data/spec/rack/spec/request_validation_spec.rb +25 -8
- data/spec/spec_helper.rb +2 -0
- metadata +17 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9d070ee1c712f33a4d50a9480bea910ff8674ba
|
4
|
+
data.tar.gz: 34ec8da54e93829dc3123145add042b246f79e46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 507ee6434b83a63a9a75899500f5778a34aa3854b0c68285ceda7bb63f97ad4a66880712f98d15269c3c07aab4bc54745fd0b3e7fcaa1b358da58764ddea43af
|
7
|
+
data.tar.gz: 0a6076dec2ca43cc4979790b6bfa92e557d119308e2e385f3b3d624079bf7736936b9c77d4f56d12c49c08cdf238c3cc4d3815e66378aee2f9acc2a00fa96312
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,49 @@
|
|
1
1
|
# Rack::Spec
|
2
2
|
Generate API server from [JSON Schema](http://json-schema.org/).
|
3
3
|
|
4
|
-
##
|
4
|
+
## Usage
|
5
|
+
```ruby
|
6
|
+
use Rack::Spec::ErrorHandler
|
7
|
+
use Rack::Spec::RequestValidation, schema: JSON.parse(File.read("schema.json")))
|
8
|
+
```
|
9
|
+
|
10
|
+
### Example
|
11
|
+
```sh
|
12
|
+
$ curl http://localhost:9292/recipes
|
13
|
+
{"id":"link_not_found","message":"Not found"}
|
14
|
+
|
15
|
+
$ curl http://localhost:9292/apps -H "Content-Type: application/json" -d "invalid-json"
|
16
|
+
{"id":"invalid_json","message":"Request body wasn't valid JSON"}
|
17
|
+
|
18
|
+
$ curl http://localhost:9292/apps -H "Content-Type: text/plain" -d "{}"
|
19
|
+
{"id":"invalid_content_type","message":"Invalid content type"}
|
20
|
+
|
21
|
+
$ curl http://localhost:9292/apps -H "Content-Type: application/json" -d '{"name":"x"}'
|
22
|
+
{"id":"invalid_parameter","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."}
|
23
|
+
```
|
24
|
+
|
25
|
+
### Rack::Spec::RequestValidation
|
5
26
|
* Raise `Rack::Spec::RequestValidation::LinkNotFound` when given request is not defined in schema
|
6
27
|
* Raise `Rack::Spec::RequestValidation::InvalidContentType` for invalid content type
|
7
28
|
* Raise `Rack::Spec::RequestValidation::InvalidParameter` for invalid request parameter
|
8
29
|
|
9
|
-
|
10
|
-
|
30
|
+
### Rack::Spec::ErrorHandler
|
31
|
+
Returns appropriate error response including following properties when RequestValidation raises error.
|
32
|
+
|
33
|
+
* id: Error type identifier (e.g. `link_not_found`, `invalid_content_type`)
|
34
|
+
* message: Human readable message (e.g. `Not Found`, `Invalid Content-Type`)
|
35
|
+
|
36
|
+
### Errors
|
37
|
+
```
|
38
|
+
StandardError
|
39
|
+
|
|
40
|
+
Rack::Spec::Error
|
41
|
+
|
|
42
|
+
Rack::Spec::RequestValidation::Error
|
43
|
+
|
|
44
|
+
|--Rack::Spec::RequestValidation::LinkNotFound
|
45
|
+
|
|
46
|
+
|--Rack::Spec::RequestValidation::InvalidContentType
|
47
|
+
|
|
48
|
+
`--Rack::Spec::RequestValidation::InvalidParameter
|
11
49
|
```
|
data/lib/rack/spec.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rack
|
2
|
+
module Spec
|
3
|
+
class Error < StandardError
|
4
|
+
# @return [Array] Rack response
|
5
|
+
def to_rack_response
|
6
|
+
[status, headers, [body]]
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# @note Override this
|
12
|
+
def status
|
13
|
+
500
|
14
|
+
end
|
15
|
+
|
16
|
+
# @note Override this
|
17
|
+
def id
|
18
|
+
"internal_server_error"
|
19
|
+
end
|
20
|
+
|
21
|
+
def headers
|
22
|
+
{ "Content-Type" => "application/json" }
|
23
|
+
end
|
24
|
+
|
25
|
+
def body
|
26
|
+
{ id: id, message: message }.to_json
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rack
|
2
|
+
module Spec
|
3
|
+
class ErrorHandler
|
4
|
+
# Behaves as a rack middleware
|
5
|
+
# @param app [Object] Rack application
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
# Behaves as a rack middleware
|
11
|
+
# @param env [Hash] Rack env
|
12
|
+
def call(env)
|
13
|
+
@app.call(env)
|
14
|
+
rescue Rack::Spec::Error => exception
|
15
|
+
exception.to_rack_response
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -38,21 +38,27 @@ module Rack
|
|
38
38
|
raise LinkNotFound
|
39
39
|
when has_body? && !has_valid_content_type?
|
40
40
|
raise InvalidContentType
|
41
|
-
when
|
42
|
-
raise
|
41
|
+
when has_body? && !has_valid_json?
|
42
|
+
raise InvalidJson
|
43
|
+
when has_body? && has_schema? && !has_valid_parameter?
|
44
|
+
raise InvalidParameter, "Invalid request.\n#{schema_validation_error_message}"
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
46
48
|
private
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
valid
|
50
|
+
def has_valid_json?
|
51
|
+
parameters
|
52
|
+
true
|
52
53
|
rescue MultiJson::ParseError
|
53
54
|
false
|
54
55
|
end
|
55
56
|
|
57
|
+
# @return [true, false] True if request parameters are all valid
|
58
|
+
def has_valid_parameter?
|
59
|
+
schema_validation_result[0]
|
60
|
+
end
|
61
|
+
|
56
62
|
# @return [true, false] True if any schema is defined for the current action
|
57
63
|
def has_schema?
|
58
64
|
!!link.schema
|
@@ -73,6 +79,21 @@ module Rack
|
|
73
79
|
!!link
|
74
80
|
end
|
75
81
|
|
82
|
+
# @return [Array] A result of schema validation for the current action
|
83
|
+
def schema_validation_result
|
84
|
+
@schema_validation_result ||= link.schema.validate(parameters)
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Array] Errors of schema validation
|
88
|
+
def schema_validation_errors
|
89
|
+
schema_validation_result[1]
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [String] Joined error message to the result of schema validation
|
93
|
+
def schema_validation_error_message
|
94
|
+
JsonSchema::SchemaError.aggregate(schema_validation_errors).join("\n")
|
95
|
+
end
|
96
|
+
|
76
97
|
# @return [JsonSchema::Schema::Link, nil] Link for the current action
|
77
98
|
def link
|
78
99
|
if instance_variable_defined?(:@link)
|
@@ -134,19 +155,63 @@ module Rack
|
|
134
155
|
end
|
135
156
|
|
136
157
|
# Base error class for Rack::Spec::RequestValidation
|
137
|
-
class Error <
|
158
|
+
class Error < Error
|
138
159
|
end
|
139
160
|
|
140
161
|
# Error class for case when no link defined for given request
|
141
162
|
class LinkNotFound < Error
|
163
|
+
def initialize
|
164
|
+
super("Not found")
|
165
|
+
end
|
166
|
+
|
167
|
+
def status
|
168
|
+
404
|
169
|
+
end
|
170
|
+
|
171
|
+
def id
|
172
|
+
"link_not_found"
|
173
|
+
end
|
142
174
|
end
|
143
175
|
|
144
176
|
# Error class for invalid request content type
|
145
177
|
class InvalidContentType < Error
|
178
|
+
def initialize
|
179
|
+
super("Invalid content type")
|
180
|
+
end
|
181
|
+
|
182
|
+
def status
|
183
|
+
400
|
184
|
+
end
|
185
|
+
|
186
|
+
def id
|
187
|
+
"invalid_content_type"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Error class for invalid JSON
|
192
|
+
class InvalidJson < Error
|
193
|
+
def initialize
|
194
|
+
super("Request body wasn't valid JSON")
|
195
|
+
end
|
196
|
+
|
197
|
+
def status
|
198
|
+
400
|
199
|
+
end
|
200
|
+
|
201
|
+
def id
|
202
|
+
"invalid_json"
|
203
|
+
end
|
146
204
|
end
|
147
205
|
|
148
206
|
# Error class for invalid request parameter
|
149
207
|
class InvalidParameter < Error
|
208
|
+
def status
|
209
|
+
400
|
210
|
+
end
|
211
|
+
|
212
|
+
def id
|
213
|
+
"invalid_parameter"
|
214
|
+
end
|
150
215
|
end
|
151
216
|
end
|
152
217
|
end
|
data/lib/rack/spec/version.rb
CHANGED
data/rack-spec.gemspec
CHANGED
@@ -6,6 +6,7 @@ describe Rack::Spec::RequestValidation do
|
|
6
6
|
let(:app) do
|
7
7
|
data = schema
|
8
8
|
Rack::Builder.app do
|
9
|
+
use Rack::Spec::ErrorHandler
|
9
10
|
use Rack::Spec::RequestValidation, schema: data
|
10
11
|
run ->(env) do
|
11
12
|
[200, {}, ["OK"]]
|
@@ -75,8 +76,12 @@ describe Rack::Spec::RequestValidation do
|
|
75
76
|
"/undefined"
|
76
77
|
end
|
77
78
|
|
78
|
-
it "
|
79
|
-
|
79
|
+
it "returns link_not_found error" do
|
80
|
+
should == 404
|
81
|
+
response.body.should be_json_as(
|
82
|
+
id: "link_not_found",
|
83
|
+
message: "Not found",
|
84
|
+
)
|
80
85
|
end
|
81
86
|
end
|
82
87
|
|
@@ -85,8 +90,12 @@ describe Rack::Spec::RequestValidation do
|
|
85
90
|
env["CONTENT_TYPE"] = "text/plain"
|
86
91
|
end
|
87
92
|
|
88
|
-
it "
|
89
|
-
|
93
|
+
it "returns invalid_content_type error" do
|
94
|
+
should == 400
|
95
|
+
response.body.should be_json_as(
|
96
|
+
id: "invalid_content_type",
|
97
|
+
message: "Invalid content type",
|
98
|
+
)
|
90
99
|
end
|
91
100
|
end
|
92
101
|
|
@@ -99,8 +108,12 @@ describe Rack::Spec::RequestValidation do
|
|
99
108
|
{ name: "ab" }.to_json
|
100
109
|
end
|
101
110
|
|
102
|
-
it "
|
103
|
-
|
111
|
+
it "returns invalid_parameter error" do
|
112
|
+
should == 400
|
113
|
+
response.body.should be_json_as(
|
114
|
+
id: "invalid_parameter",
|
115
|
+
message: %r<\AInvalid request\.\n#/name: failed schema .+: Expected string to match pattern>,
|
116
|
+
)
|
104
117
|
end
|
105
118
|
end
|
106
119
|
|
@@ -109,8 +122,12 @@ describe Rack::Spec::RequestValidation do
|
|
109
122
|
"malformed"
|
110
123
|
end
|
111
124
|
|
112
|
-
it "
|
113
|
-
|
125
|
+
it "returns invalid_parameter error" do
|
126
|
+
should == 400
|
127
|
+
response.body.should be_json_as(
|
128
|
+
id: "invalid_json",
|
129
|
+
message: "Request body wasn't valid JSON",
|
130
|
+
)
|
114
131
|
end
|
115
132
|
end
|
116
133
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require "rack/builder"
|
2
2
|
require "rack/spec"
|
3
3
|
require "rack/test"
|
4
|
+
require "rspec/json_matcher"
|
4
5
|
|
5
6
|
RSpec.configure do |config|
|
6
7
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
7
8
|
config.run_all_when_everything_filtered = true
|
8
9
|
config.filter_run :focus
|
10
|
+
config.include RSpec::JsonMatcher
|
9
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-spec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryo Nakamura
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec-json_matcher
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
description:
|
126
140
|
email:
|
127
141
|
- r7kamura@gmail.com
|
@@ -137,6 +151,8 @@ files:
|
|
137
151
|
- Rakefile
|
138
152
|
- lib/rack-spec.rb
|
139
153
|
- lib/rack/spec.rb
|
154
|
+
- lib/rack/spec/error.rb
|
155
|
+
- lib/rack/spec/error_handler.rb
|
140
156
|
- lib/rack/spec/request_validation.rb
|
141
157
|
- lib/rack/spec/schema.rb
|
142
158
|
- lib/rack/spec/version.rb
|