rack-spec 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a554761d92c1fbeb80f0983023a2cc61970cb9f6
4
- data.tar.gz: 578034987add0a2733821b66a3d7fa9743e9aaac
3
+ metadata.gz: a9d070ee1c712f33a4d50a9480bea910ff8674ba
4
+ data.tar.gz: 34ec8da54e93829dc3123145add042b246f79e46
5
5
  SHA512:
6
- metadata.gz: c12e918bd0224e9968dc856e0bd3fcc89293b1133bd1d6ed35c6c49d02ee8bff9ed988078f3281fd34947b99b128f8453bf22350eb820fad334262613e807e47
7
- data.tar.gz: c95c7728c4d01ffb2066b3193102e08a44ef9202c9da36b343027352f7e77d1f76219a39762e23c954b6b34ad4f4afa6f2e69a81636e65436eb095fbd16c6809
6
+ metadata.gz: 507ee6434b83a63a9a75899500f5778a34aa3854b0c68285ceda7bb63f97ad4a66880712f98d15269c3c07aab4bc54745fd0b3e7fcaa1b358da58764ddea43af
7
+ data.tar.gz: 0a6076dec2ca43cc4979790b6bfa92e557d119308e2e385f3b3d624079bf7736936b9c77d4f56d12c49c08cdf238c3cc4d3815e66378aee2f9acc2a00fa96312
@@ -1,3 +1,6 @@
1
+ ## v0.1.1
2
+ * Add ErrorHandler rack middleware for building error response
3
+
1
4
  ## v0.1.0
2
5
  * Rebuilt entire code based on JSON schema
3
6
 
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
- ## RequestValidation
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
- ```ruby
10
- use Rack::Spec::RequestValidation, schema: JSON.parse("schema.json")
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
  ```
@@ -2,6 +2,8 @@ require "json"
2
2
  require "json_schema"
3
3
  require "multi_json"
4
4
 
5
+ require "rack/spec/error_handler"
6
+ require "rack/spec/error"
5
7
  require "rack/spec/request_validation"
6
8
  require "rack/spec/schema"
7
9
  require "rack/spec/version"
@@ -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 has_schema? && !has_valid_parameter?
42
- raise InvalidParameter
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
- # @return [true, false] True if request parameters are all valid
49
- def has_valid_parameter?
50
- valid, errors = link.schema.validate(parameters)
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 < StandardError
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
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module Spec
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "rake"
26
26
  spec.add_development_dependency "rspec", "2.14.1"
27
27
  spec.add_development_dependency "rspec-console"
28
+ spec.add_development_dependency "rspec-json_matcher"
28
29
  end
@@ -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 "raises Rack::Spec::RequestValidation::LinkNotFound" do
79
- expect { subject }.to raise_error(Rack::Spec::RequestValidation::LinkNotFound)
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 "raises Rack::Spec::RequestValidation::InvalidContentType" do
89
- expect { subject }.to raise_error(Rack::Spec::RequestValidation::InvalidContentType)
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 "raises Rack::Spec::RequestValidation::InvalidParameter" do
103
- expect { subject }.to raise_error(Rack::Spec::RequestValidation::InvalidParameter)
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 "raises Rack::Spec::RequestValidation::InvalidParameter" do
113
- expect { subject }.to raise_error(Rack::Spec::RequestValidation::InvalidParameter)
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
@@ -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.0
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