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