grape 0.13.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +9 -4
- data/CHANGELOG.md +28 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +166 -0
- data/README.md +305 -163
- data/Rakefile +30 -33
- data/UPGRADING.md +31 -0
- data/benchmark/simple.rb +27 -0
- data/gemfiles/rack_1.5.2.gemfile +13 -0
- data/gemfiles/rails_3.gemfile +2 -2
- data/gemfiles/rails_4.gemfile +1 -2
- data/grape.gemspec +5 -4
- data/lib/grape.rb +9 -5
- data/lib/grape/dsl/configuration.rb +5 -2
- data/lib/grape/dsl/helpers.rb +8 -3
- data/lib/grape/dsl/inside_route.rb +67 -44
- data/lib/grape/dsl/parameters.rb +21 -12
- data/lib/grape/dsl/request_response.rb +1 -1
- data/lib/grape/dsl/routing.rb +3 -4
- data/lib/grape/endpoint.rb +63 -28
- data/lib/grape/error_formatter/base.rb +6 -6
- data/lib/grape/exceptions/base.rb +5 -5
- data/lib/grape/exceptions/invalid_version_header.rb +10 -0
- data/lib/grape/formatter/serializable_hash.rb +3 -2
- data/lib/grape/locale/en.yml +4 -1
- data/lib/grape/middleware/auth/base.rb +2 -2
- data/lib/grape/middleware/auth/dsl.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/base.rb +7 -4
- data/lib/grape/middleware/error.rb +3 -2
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +47 -44
- data/lib/grape/middleware/globals.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
- data/lib/grape/middleware/versioner/header.rb +113 -50
- data/lib/grape/middleware/versioner/param.rb +5 -8
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
- data/lib/grape/middleware/versioner/path.rb +3 -6
- data/lib/grape/path.rb +3 -3
- data/lib/grape/request.rb +40 -0
- data/lib/grape/util/content_types.rb +9 -9
- data/lib/grape/util/env.rb +22 -0
- data/lib/grape/util/strict_hash_configuration.rb +2 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -3
- data/lib/grape/validations/params_scope.rb +83 -15
- data/lib/grape/validations/types.rb +144 -0
- data/lib/grape/validations/types/build_coercer.rb +53 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
- data/lib/grape/validations/types/file.rb +28 -0
- data/lib/grape/validations/types/json.rb +65 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
- data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -3
- data/lib/grape/validations/validators/base.rb +7 -0
- data/lib/grape/validations/validators/coerce.rb +31 -42
- data/lib/grape/validations/validators/presence.rb +2 -3
- data/lib/grape/validations/validators/regexp.rb +2 -4
- data/lib/grape/validations/validators/values.rb +3 -3
- data/lib/grape/version.rb +1 -1
- data/pkg/grape-0.13.0.gem +0 -0
- data/spec/grape/api/custom_validations_spec.rb +5 -4
- data/spec/grape/api/deeply_included_options_spec.rb +7 -7
- data/spec/grape/api/nested_helpers_spec.rb +4 -2
- data/spec/grape/api/shared_helpers_spec.rb +8 -8
- data/spec/grape/api_spec.rb +88 -54
- data/spec/grape/dsl/configuration_spec.rb +13 -0
- data/spec/grape/dsl/helpers_spec.rb +16 -2
- data/spec/grape/dsl/inside_route_spec.rb +3 -2
- data/spec/grape/dsl/parameters_spec.rb +0 -6
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +61 -20
- data/spec/grape/entity_spec.rb +10 -8
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
- data/spec/grape/integration/rack_spec.rb +3 -2
- data/spec/grape/middleware/base_spec.rb +7 -5
- data/spec/grape/middleware/error_spec.rb +16 -15
- data/spec/grape/middleware/exception_spec.rb +45 -43
- data/spec/grape/middleware/formatter_spec.rb +34 -0
- data/spec/grape/middleware/versioner/header_spec.rb +79 -47
- data/spec/grape/path_spec.rb +10 -10
- data/spec/grape/presenters/presenter_spec.rb +2 -2
- data/spec/grape/request_spec.rb +100 -0
- data/spec/grape/validations/params_scope_spec.rb +11 -9
- data/spec/grape/validations/types_spec.rb +95 -0
- data/spec/grape/validations/validators/coerce_spec.rb +335 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -15
- data/spec/grape/validations_spec.rb +53 -24
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/versioned_helpers.rb +2 -2
- metadata +51 -13
- data/.gitignore +0 -46
- data/.rspec +0 -2
- data/.rubocop.yml +0 -7
- data/.rubocop_todo.yml +0 -84
- data/.travis.yml +0 -20
- data/.yardopts +0 -2
- data/lib/grape/http/request.rb +0 -35
- data/lib/grape/util/parameter_types.rb +0 -58
- data/spec/grape/util/parameter_types_spec.rb +0 -54
@@ -3,48 +3,50 @@ require 'active_support/core_ext/hash'
|
|
3
3
|
|
4
4
|
describe Grape::Middleware::Error do
|
5
5
|
# raises a text exception
|
6
|
-
|
7
|
-
class
|
8
|
-
|
9
|
-
|
6
|
+
module ExceptionSpec
|
7
|
+
class ExceptionApp
|
8
|
+
class << self
|
9
|
+
def call(_env)
|
10
|
+
fail 'rain!'
|
11
|
+
end
|
10
12
|
end
|
11
13
|
end
|
12
|
-
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
# raises a hash error
|
16
|
+
class ErrorHashApp
|
17
|
+
class << self
|
18
|
+
def error!(message, status)
|
19
|
+
throw :error, message: { error: message, detail: 'missing widget' }, status: status
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
def call(_env)
|
23
|
+
error!('rain!', 401)
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
25
|
-
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
# raises an error!
|
29
|
+
class AccessDeniedApp
|
30
|
+
class << self
|
31
|
+
def error!(message, status)
|
32
|
+
throw :error, message: message, status: status
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
def call(_env)
|
36
|
+
error!('Access Denied', 401)
|
37
|
+
end
|
36
38
|
end
|
37
39
|
end
|
38
|
-
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
# raises a custom error
|
42
|
+
class CustomError < Grape::Exceptions::Base
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
class CustomErrorApp
|
46
|
+
class << self
|
47
|
+
def call(_env)
|
48
|
+
fail CustomError, status: 400, message: 'failed validation'
|
49
|
+
end
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
@@ -55,7 +57,7 @@ describe Grape::Middleware::Error do
|
|
55
57
|
@app ||= Rack::Builder.app do
|
56
58
|
use Spec::Support::EndpointFaker
|
57
59
|
use Grape::Middleware::Error
|
58
|
-
run ExceptionApp
|
60
|
+
run ExceptionSpec::ExceptionApp
|
59
61
|
end
|
60
62
|
expect { get '/' }.to raise_error
|
61
63
|
end
|
@@ -65,7 +67,7 @@ describe Grape::Middleware::Error do
|
|
65
67
|
@app ||= Rack::Builder.app do
|
66
68
|
use Spec::Support::EndpointFaker
|
67
69
|
use Grape::Middleware::Error, rescue_all: true
|
68
|
-
run ExceptionApp
|
70
|
+
run ExceptionSpec::ExceptionApp
|
69
71
|
end
|
70
72
|
get '/'
|
71
73
|
expect(last_response.body).to eq('rain!')
|
@@ -75,7 +77,7 @@ describe Grape::Middleware::Error do
|
|
75
77
|
@app ||= Rack::Builder.app do
|
76
78
|
use Spec::Support::EndpointFaker
|
77
79
|
use Grape::Middleware::Error, rescue_all: true
|
78
|
-
run ExceptionApp
|
80
|
+
run ExceptionSpec::ExceptionApp
|
79
81
|
end
|
80
82
|
get '/'
|
81
83
|
expect(last_response.status).to eq(500)
|
@@ -85,7 +87,7 @@ describe Grape::Middleware::Error do
|
|
85
87
|
@app ||= Rack::Builder.app do
|
86
88
|
use Spec::Support::EndpointFaker
|
87
89
|
use Grape::Middleware::Error, rescue_all: true, default_status: 500
|
88
|
-
run ExceptionApp
|
90
|
+
run ExceptionSpec::ExceptionApp
|
89
91
|
end
|
90
92
|
get '/'
|
91
93
|
expect(last_response.status).to eq(500)
|
@@ -95,7 +97,7 @@ describe Grape::Middleware::Error do
|
|
95
97
|
@app ||= Rack::Builder.app do
|
96
98
|
use Spec::Support::EndpointFaker
|
97
99
|
use Grape::Middleware::Error, rescue_all: true, format: :json
|
98
|
-
run ExceptionApp
|
100
|
+
run ExceptionSpec::ExceptionApp
|
99
101
|
end
|
100
102
|
get '/'
|
101
103
|
expect(last_response.body).to eq('{"error":"rain!"}')
|
@@ -105,7 +107,7 @@ describe Grape::Middleware::Error do
|
|
105
107
|
@app ||= Rack::Builder.app do
|
106
108
|
use Spec::Support::EndpointFaker
|
107
109
|
use Grape::Middleware::Error, rescue_all: true, format: :json
|
108
|
-
run ErrorHashApp
|
110
|
+
run ExceptionSpec::ErrorHashApp
|
109
111
|
end
|
110
112
|
get '/'
|
111
113
|
expect(['{"error":"rain!","detail":"missing widget"}',
|
@@ -116,7 +118,7 @@ describe Grape::Middleware::Error do
|
|
116
118
|
@app ||= Rack::Builder.app do
|
117
119
|
use Spec::Support::EndpointFaker
|
118
120
|
use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
|
119
|
-
run ExceptionApp
|
121
|
+
run ExceptionSpec::ExceptionApp
|
120
122
|
end
|
121
123
|
get '/'
|
122
124
|
expect(last_response.body).to eq('{"error":"rain!"}')
|
@@ -126,7 +128,7 @@ describe Grape::Middleware::Error do
|
|
126
128
|
@app ||= Rack::Builder.app do
|
127
129
|
use Spec::Support::EndpointFaker
|
128
130
|
use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
|
129
|
-
run ErrorHashApp
|
131
|
+
run ExceptionSpec::ErrorHashApp
|
130
132
|
end
|
131
133
|
get '/'
|
132
134
|
expect(['{"error":"rain!","detail":"missing widget"}',
|
@@ -137,7 +139,7 @@ describe Grape::Middleware::Error do
|
|
137
139
|
@app ||= Rack::Builder.app do
|
138
140
|
use Spec::Support::EndpointFaker
|
139
141
|
use Grape::Middleware::Error, rescue_all: true, format: :xml
|
140
|
-
run ExceptionApp
|
142
|
+
run ExceptionSpec::ExceptionApp
|
141
143
|
end
|
142
144
|
get '/'
|
143
145
|
expect(last_response.body).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <message>rain!</message>\n</error>\n")
|
@@ -147,7 +149,7 @@ describe Grape::Middleware::Error do
|
|
147
149
|
@app ||= Rack::Builder.app do
|
148
150
|
use Spec::Support::EndpointFaker
|
149
151
|
use Grape::Middleware::Error, rescue_all: true, format: :xml
|
150
|
-
run ErrorHashApp
|
152
|
+
run ExceptionSpec::ErrorHashApp
|
151
153
|
end
|
152
154
|
get '/'
|
153
155
|
expect(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <detail>missing widget</detail>\n <error>rain!</error>\n</error>\n",
|
@@ -164,7 +166,7 @@ describe Grape::Middleware::Error do
|
|
164
166
|
{ custom_formatter: message }.inspect
|
165
167
|
end
|
166
168
|
}
|
167
|
-
run ExceptionApp
|
169
|
+
run ExceptionSpec::ExceptionApp
|
168
170
|
end
|
169
171
|
get '/'
|
170
172
|
expect(last_response.body).to eq('{:custom_formatter=>"rain!"}')
|
@@ -174,7 +176,7 @@ describe Grape::Middleware::Error do
|
|
174
176
|
@app ||= Rack::Builder.app do
|
175
177
|
use Spec::Support::EndpointFaker
|
176
178
|
use Grape::Middleware::Error
|
177
|
-
run AccessDeniedApp
|
179
|
+
run ExceptionSpec::AccessDeniedApp
|
178
180
|
end
|
179
181
|
get '/'
|
180
182
|
expect(last_response.status).to eq(401)
|
@@ -184,7 +186,7 @@ describe Grape::Middleware::Error do
|
|
184
186
|
@app ||= Rack::Builder.app do
|
185
187
|
use Spec::Support::EndpointFaker
|
186
188
|
use Grape::Middleware::Error, rescue_all: false
|
187
|
-
run CustomErrorApp
|
189
|
+
run ExceptionSpec::CustomErrorApp
|
188
190
|
end
|
189
191
|
|
190
192
|
get '/'
|
@@ -132,6 +132,18 @@ describe Grape::Middleware::Formatter do
|
|
132
132
|
expect(subject.env['api.format']).to eq(:xml)
|
133
133
|
end
|
134
134
|
|
135
|
+
context 'with custom vendored content types' do
|
136
|
+
before do
|
137
|
+
subject.options[:content_types] = {}
|
138
|
+
subject.options[:content_types][:custom] = 'application/vnd.test+json'
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'it uses the custom type' do
|
142
|
+
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
|
143
|
+
expect(subject.env['api.format']).to eq(:custom)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
135
147
|
it 'parses headers with symbols as hash keys' do
|
136
148
|
subject.call('PATH_INFO' => '/info', 'http_accept' => 'application/xml', system_time: '091293')
|
137
149
|
expect(subject.env[:system_time]).to eq('091293')
|
@@ -157,6 +169,16 @@ describe Grape::Middleware::Formatter do
|
|
157
169
|
_, headers, = subject.call('PATH_INFO' => '/info.custom')
|
158
170
|
expect(headers['Content-type']).to eq('application/x-custom')
|
159
171
|
end
|
172
|
+
it 'is set for vendored with registered type' do
|
173
|
+
subject.options[:content_types] = {}
|
174
|
+
subject.options[:content_types][:custom] = 'application/vnd.test+json'
|
175
|
+
_, headers, = subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
|
176
|
+
expect(headers['Content-type']).to eq('application/vnd.test+json')
|
177
|
+
end
|
178
|
+
it 'is set to closest generic for custom vendored/versioned without registered type' do
|
179
|
+
_, headers, = subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
|
180
|
+
expect(headers['Content-type']).to eq('application/json')
|
181
|
+
end
|
160
182
|
end
|
161
183
|
|
162
184
|
context 'format' do
|
@@ -179,6 +201,18 @@ describe Grape::Middleware::Formatter do
|
|
179
201
|
end
|
180
202
|
end
|
181
203
|
|
204
|
+
context 'no content responses' do
|
205
|
+
let(:no_content_response) { ->(status) { [status, {}, ['']] } }
|
206
|
+
|
207
|
+
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.each do |status|
|
208
|
+
it "does not modify a #{status} response" do
|
209
|
+
expected_response = no_content_response[status]
|
210
|
+
allow(app).to receive(:call).and_return(expected_response)
|
211
|
+
expect(subject.call({})).to eq(expected_response)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
182
216
|
context 'input' do
|
183
217
|
%w(POST PATCH PUT DELETE).each do |method|
|
184
218
|
['application/json', 'application/json; charset=utf-8'].each do |content_type|
|
@@ -56,13 +56,13 @@ describe Grape::Middleware::Versioner::Header do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'is set' do
|
59
|
-
status, _, env =
|
59
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
|
60
60
|
expect(env['api.format']).to eql 'json'
|
61
61
|
expect(status).to eq(200)
|
62
62
|
end
|
63
63
|
|
64
64
|
it 'is nil if not provided' do
|
65
|
-
status, _, env =
|
65
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
|
66
66
|
expect(env['api.format']).to eql nil
|
67
67
|
expect(status).to eq(200)
|
68
68
|
end
|
@@ -72,13 +72,13 @@ describe Grape::Middleware::Versioner::Header do
|
|
72
72
|
|
73
73
|
context 'api.vendor' do
|
74
74
|
it 'is set' do
|
75
|
-
status, _, env =
|
75
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor')
|
76
76
|
expect(env['api.vendor']).to eql 'vendor'
|
77
77
|
expect(status).to eq(200)
|
78
78
|
end
|
79
79
|
|
80
80
|
it 'is set if format provided' do
|
81
|
-
status, _, env =
|
81
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor+json')
|
82
82
|
expect(env['api.vendor']).to eql 'vendor'
|
83
83
|
expect(status).to eq(200)
|
84
84
|
end
|
@@ -89,7 +89,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
89
89
|
expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
|
90
90
|
expect(exception.headers).to eql('X-Cascade' => 'pass')
|
91
91
|
expect(exception.status).to eql 406
|
92
|
-
expect(exception.message).to include 'API vendor
|
92
|
+
expect(exception.message).to include 'API vendor not found'
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
@@ -99,13 +99,13 @@ describe Grape::Middleware::Versioner::Header do
|
|
99
99
|
end
|
100
100
|
|
101
101
|
it 'is set' do
|
102
|
-
status, _, env =
|
102
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
|
103
103
|
expect(env['api.vendor']).to eql 'vendor'
|
104
104
|
expect(status).to eq(200)
|
105
105
|
end
|
106
106
|
|
107
107
|
it 'is set if format provided' do
|
108
|
-
status, _, env =
|
108
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
|
109
109
|
expect(env['api.vendor']).to eql 'vendor'
|
110
110
|
expect(status).to eq(200)
|
111
111
|
end
|
@@ -116,7 +116,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
116
116
|
expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
|
117
117
|
expect(exception.headers).to eql('X-Cascade' => 'pass')
|
118
118
|
expect(exception.status).to eql 406
|
119
|
-
expect(exception.message).to include('API vendor
|
119
|
+
expect(exception.message).to include('API vendor not found')
|
120
120
|
end
|
121
121
|
end
|
122
122
|
end
|
@@ -128,23 +128,23 @@ describe Grape::Middleware::Versioner::Header do
|
|
128
128
|
end
|
129
129
|
|
130
130
|
it 'is set' do
|
131
|
-
status, _, env =
|
131
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
|
132
132
|
expect(env['api.version']).to eql 'v1'
|
133
133
|
expect(status).to eq(200)
|
134
134
|
end
|
135
135
|
|
136
136
|
it 'is set if format provided' do
|
137
|
-
status, _, env =
|
137
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
|
138
138
|
expect(env['api.version']).to eql 'v1'
|
139
139
|
expect(status).to eq(200)
|
140
140
|
end
|
141
141
|
|
142
142
|
it 'fails with 406 Not Acceptable if version is invalid' do
|
143
143
|
expect { subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v2+json').last }.to raise_exception do |exception|
|
144
|
-
expect(exception).to be_a(Grape::Exceptions::
|
144
|
+
expect(exception).to be_a(Grape::Exceptions::InvalidVersionHeader)
|
145
145
|
expect(exception.headers).to eql('X-Cascade' => 'pass')
|
146
146
|
expect(exception.status).to eql 406
|
147
|
-
expect(exception.message).to include('API
|
147
|
+
expect(exception.message).to include('API version not found')
|
148
148
|
end
|
149
149
|
end
|
150
150
|
end
|
@@ -184,24 +184,6 @@ describe Grape::Middleware::Versioner::Header do
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
-
it 'fails with 406 Not Acceptable if type is a range' do
|
188
|
-
expect { subject.call('HTTP_ACCEPT' => '*/*').last }.to raise_exception do |exception|
|
189
|
-
expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
|
190
|
-
expect(exception.headers).to eql('X-Cascade' => 'pass')
|
191
|
-
expect(exception.status).to eql 406
|
192
|
-
expect(exception.message).to include('Accept header must not contain ranges ("*").')
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
it 'fails with 406 Not Acceptable if subtype is a range' do
|
197
|
-
expect { subject.call('HTTP_ACCEPT' => 'application/*').last }.to raise_exception do |exception|
|
198
|
-
expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
|
199
|
-
expect(exception.headers).to eql('X-Cascade' => 'pass')
|
200
|
-
expect(exception.status).to eql 406
|
201
|
-
expect(exception.message).to include('Accept header must not contain ranges ("*").')
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
187
|
it 'succeeds if proper header is set' do
|
206
188
|
expect(subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json').first).to eq(200)
|
207
189
|
end
|
@@ -223,30 +205,22 @@ describe Grape::Middleware::Versioner::Header do
|
|
223
205
|
end
|
224
206
|
end
|
225
207
|
|
226
|
-
it 'fails with 406 Not Acceptable if header is
|
227
|
-
expect { subject.call('HTTP_ACCEPT' => '').last }
|
228
|
-
|
229
|
-
expect(exception.headers).to eql({})
|
230
|
-
expect(exception.status).to eql 406
|
231
|
-
expect(exception.message).to include('Accept header must be set.')
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
it 'fails with 406 Not Acceptable if type is a range' do
|
236
|
-
expect { subject.call('HTTP_ACCEPT' => '*/*').last }.to raise_exception do |exception|
|
208
|
+
it 'fails with 406 Not Acceptable if header is application/xml' do
|
209
|
+
expect { subject.call('HTTP_ACCEPT' => 'application/xml').last }
|
210
|
+
.to raise_exception do |exception|
|
237
211
|
expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
|
238
212
|
expect(exception.headers).to eql({})
|
239
213
|
expect(exception.status).to eql 406
|
240
|
-
expect(exception.message).to include('
|
214
|
+
expect(exception.message).to include('API vendor or version not found.')
|
241
215
|
end
|
242
216
|
end
|
243
217
|
|
244
|
-
it 'fails with 406 Not Acceptable if
|
245
|
-
expect { subject.call('HTTP_ACCEPT' => '
|
218
|
+
it 'fails with 406 Not Acceptable if header is empty' do
|
219
|
+
expect { subject.call('HTTP_ACCEPT' => '').last }.to raise_exception do |exception|
|
246
220
|
expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
|
247
221
|
expect(exception.headers).to eql({})
|
248
222
|
expect(exception.status).to eql 406
|
249
|
-
expect(exception.message).to include('Accept header must
|
223
|
+
expect(exception.message).to include('Accept header must be set.')
|
250
224
|
end
|
251
225
|
end
|
252
226
|
|
@@ -270,10 +244,68 @@ describe Grape::Middleware::Versioner::Header do
|
|
270
244
|
|
271
245
|
it 'fails with another version' do
|
272
246
|
expect { subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v3+json') }.to raise_exception do |exception|
|
273
|
-
expect(exception).to be_a(Grape::Exceptions::
|
247
|
+
expect(exception).to be_a(Grape::Exceptions::InvalidVersionHeader)
|
274
248
|
expect(exception.headers).to eql('X-Cascade' => 'pass')
|
275
249
|
expect(exception.status).to eql 406
|
276
|
-
expect(exception.message).to include('API
|
250
|
+
expect(exception.message).to include('API version not found')
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context 'when there are multiple versions with complex vendor specified with rescue_from :all' do
|
256
|
+
subject {
|
257
|
+
Class.new(Grape::API) do
|
258
|
+
rescue_from :all
|
259
|
+
end
|
260
|
+
}
|
261
|
+
|
262
|
+
let(:v1_app) {
|
263
|
+
Class.new(Grape::API) do
|
264
|
+
version 'v1', using: :header, vendor: 'test.a-cool_resource', cascade: false, strict: true
|
265
|
+
content_type :v1_test, 'application/vnd.test.a-cool_resource-v1+json'
|
266
|
+
formatter :v1_test, ->(object, _) { object }
|
267
|
+
format :v1_test
|
268
|
+
|
269
|
+
resources :users do
|
270
|
+
get :hello do
|
271
|
+
'one'
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
}
|
276
|
+
|
277
|
+
let(:v2_app) {
|
278
|
+
Class.new(Grape::API) do
|
279
|
+
version 'v2', using: :header, vendor: 'test.a-cool_resource', strict: true
|
280
|
+
content_type :v2_test, 'application/vnd.test.a-cool_resource-v2+json'
|
281
|
+
formatter :v2_test, ->(object, _) { object }
|
282
|
+
format :v2_test
|
283
|
+
|
284
|
+
resources :users do
|
285
|
+
get :hello do
|
286
|
+
'two'
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
}
|
291
|
+
|
292
|
+
def app
|
293
|
+
subject.mount v2_app
|
294
|
+
subject.mount v1_app
|
295
|
+
subject
|
296
|
+
end
|
297
|
+
|
298
|
+
context 'with header versioned endpoints and a rescue_all block defined' do
|
299
|
+
it 'responds correctly to a v1 request' do
|
300
|
+
versioned_get '/users/hello', 'v1', using: :header, vendor: 'test.a-cool_resource'
|
301
|
+
expect(last_response.body).to eq('one')
|
302
|
+
expect(last_response.body).not_to include('API vendor or version not found')
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'responds correctly to a v2 request' do
|
306
|
+
versioned_get '/users/hello', 'v2', using: :header, vendor: 'test.a-cool_resource'
|
307
|
+
expect(last_response.body).to eq('two')
|
308
|
+
expect(last_response.body).not_to include('API vendor or version not found')
|
277
309
|
end
|
278
310
|
end
|
279
311
|
end
|