grape 1.7.1 → 2.0.0
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 +37 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +30 -25
- data/UPGRADING.md +35 -0
- data/grape.gemspec +3 -6
- data/lib/grape/api.rb +2 -2
- data/lib/grape/content_types.rb +2 -8
- data/lib/grape/dsl/desc.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +11 -11
- data/lib/grape/dsl/request_response.rb +2 -1
- data/lib/grape/dsl/settings.rb +2 -6
- data/lib/grape/endpoint.rb +28 -18
- data/lib/grape/error_formatter/base.rb +1 -1
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/missing_group_type.rb +1 -6
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
- data/lib/grape/exceptions/validation_errors.rb +1 -6
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
- data/lib/grape/extensions/hash.rb +4 -7
- data/lib/grape/extensions/hashie/mash.rb +3 -3
- data/lib/grape/formatter/serializable_hash.rb +7 -7
- data/lib/grape/http/headers.rb +12 -2
- data/lib/grape/middleware/auth/base.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/error.rb +5 -5
- data/lib/grape/middleware/formatter.rb +6 -6
- data/lib/grape/middleware/versioner/header.rb +11 -19
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +8 -2
- data/lib/grape/router/route.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -11
- data/lib/grape/util/strict_hash_configuration.rb +3 -4
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +8 -2
- data/lib/grape/validations/single_attribute_iterator.rb +3 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
- data/lib/grape/validations/validators/base.rb +9 -20
- data/lib/grape/validations/validators/default_validator.rb +2 -20
- data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
- data/lib/grape/validations/validators/values_validator.rb +14 -5
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +26 -5
- metadata +13 -253
- data/lib/grape/config.rb +0 -34
- data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
- data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
- data/spec/grape/api/custom_validations_spec.rb +0 -256
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -473
- data/spec/grape/api_spec.rb +0 -4347
- data/spec/grape/config_spec.rb +0 -17
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -101
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -535
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -206
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -136
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -696
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2029
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
@@ -1,221 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe Grape::Middleware::Base do
|
4
|
-
subject { described_class.new(blank_app) }
|
5
|
-
|
6
|
-
let(:blank_app) { ->(_) { [200, {}, 'Hi there.'] } }
|
7
|
-
|
8
|
-
before do
|
9
|
-
# Keep it one object for testing.
|
10
|
-
allow(subject).to receive(:dup).and_return(subject)
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'has the app as an accessor' do
|
14
|
-
expect(subject.app).to eq(blank_app)
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'calls through to the app' do
|
18
|
-
expect(subject.call({})).to eq([200, {}, 'Hi there.'])
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'callbacks' do
|
22
|
-
after { subject.call!({}) }
|
23
|
-
|
24
|
-
it 'calls #before' do
|
25
|
-
expect(subject).to receive(:before)
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'calls #after' do
|
29
|
-
expect(subject).to receive(:after)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'callbacks on error' do
|
34
|
-
let(:blank_app) { ->(_) { raise StandardError } }
|
35
|
-
|
36
|
-
it 'calls #after' do
|
37
|
-
expect(subject).to receive(:after)
|
38
|
-
expect { subject.call({}) }.to raise_error(StandardError)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'after callback' do
|
43
|
-
before do
|
44
|
-
allow(subject).to receive(:after).and_return([200, {}, 'Hello from after callback'])
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'overwrites application response' do
|
48
|
-
expect(subject.call!({}).last).to eq('Hello from after callback')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context 'after callback with errors' do
|
53
|
-
it 'does not overwrite the application response' do
|
54
|
-
expect(subject.call({})).to eq([200, {}, 'Hi there.'])
|
55
|
-
end
|
56
|
-
|
57
|
-
context 'with patched warnings' do
|
58
|
-
before do
|
59
|
-
@warnings = warnings = []
|
60
|
-
allow_any_instance_of(described_class).to receive(:warn) { |m| warnings << m }
|
61
|
-
allow(subject).to receive(:after).and_raise(StandardError)
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'does show a warning' do
|
65
|
-
expect { subject.call({}) }.to raise_error(StandardError)
|
66
|
-
expect(@warnings).not_to be_empty
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'is able to access the response' do
|
72
|
-
subject.call({})
|
73
|
-
expect(subject.response).to be_a(Rack::Response)
|
74
|
-
end
|
75
|
-
|
76
|
-
describe '#response' do
|
77
|
-
subject do
|
78
|
-
described_class.new(response)
|
79
|
-
end
|
80
|
-
|
81
|
-
before { subject.call({}) }
|
82
|
-
|
83
|
-
context 'when Array' do
|
84
|
-
let(:rack_response) { Rack::Response.new('test', 204, abc: 1) }
|
85
|
-
let(:response) { ->(_) { [204, { abc: 1 }, 'test'] } }
|
86
|
-
|
87
|
-
it 'status' do
|
88
|
-
expect(subject.response.status).to eq(204)
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'body' do
|
92
|
-
expect(subject.response.body).to eq(['test'])
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'header' do
|
96
|
-
expect(subject.response.header).to have_key(:abc)
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'returns the memoized Rack::Response instance' do
|
100
|
-
allow(Rack::Response).to receive(:new).and_return(rack_response)
|
101
|
-
expect(subject.response).to eq(rack_response)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
context 'when Rack::Response' do
|
106
|
-
let(:rack_response) { Rack::Response.new('test', 204, abc: 1) }
|
107
|
-
let(:response) { ->(_) { rack_response } }
|
108
|
-
|
109
|
-
it 'status' do
|
110
|
-
expect(subject.response.status).to eq(204)
|
111
|
-
end
|
112
|
-
|
113
|
-
it 'body' do
|
114
|
-
expect(subject.response.body).to eq(['test'])
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'header' do
|
118
|
-
expect(subject.response.header).to have_key(:abc)
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'returns the memoized Rack::Response instance' do
|
122
|
-
expect(subject.response).to eq(rack_response)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
describe '#context' do
|
128
|
-
subject { described_class.new(blank_app) }
|
129
|
-
|
130
|
-
it 'allows access to response context' do
|
131
|
-
subject.call(Grape::Env::API_ENDPOINT => { header: 'some header' })
|
132
|
-
expect(subject.context).to eq(header: 'some header')
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
context 'options' do
|
137
|
-
it 'persists options passed at initialization' do
|
138
|
-
expect(described_class.new(blank_app, abc: true).options[:abc]).to be true
|
139
|
-
end
|
140
|
-
|
141
|
-
context 'defaults' do
|
142
|
-
module BaseSpec
|
143
|
-
class ExampleWare < Grape::Middleware::Base
|
144
|
-
def default_options
|
145
|
-
{ monkey: true }
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'persists the default options' do
|
151
|
-
expect(BaseSpec::ExampleWare.new(blank_app).options[:monkey]).to be true
|
152
|
-
end
|
153
|
-
|
154
|
-
it 'overrides default options when provided' do
|
155
|
-
expect(BaseSpec::ExampleWare.new(blank_app, monkey: false).options[:monkey]).to be false
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
context 'header' do
|
161
|
-
module HeaderSpec
|
162
|
-
class ExampleWare < Grape::Middleware::Base
|
163
|
-
def before
|
164
|
-
header 'X-Test-Before', 'Hi'
|
165
|
-
end
|
166
|
-
|
167
|
-
def after
|
168
|
-
header 'X-Test-After', 'Bye'
|
169
|
-
nil
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def app
|
175
|
-
Rack::Builder.app do
|
176
|
-
use HeaderSpec::ExampleWare
|
177
|
-
run ->(_) { [200, {}, ['Yeah']] }
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'is able to set a header' do
|
182
|
-
get '/'
|
183
|
-
expect(last_response.headers['X-Test-Before']).to eq('Hi')
|
184
|
-
expect(last_response.headers['X-Test-After']).to eq('Bye')
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
context 'header overwrite' do
|
189
|
-
module HeaderOverwritingSpec
|
190
|
-
class ExampleWare < Grape::Middleware::Base
|
191
|
-
def before
|
192
|
-
header 'X-Test-Overwriting', 'Hi'
|
193
|
-
end
|
194
|
-
|
195
|
-
def after
|
196
|
-
header 'X-Test-Overwriting', 'Bye'
|
197
|
-
nil
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
class API < Grape::API
|
202
|
-
get('/') do
|
203
|
-
header 'X-Test-Overwriting', 'Yeah'
|
204
|
-
'Hello'
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def app
|
210
|
-
Rack::Builder.app do
|
211
|
-
use HeaderOverwritingSpec::ExampleWare
|
212
|
-
run HeaderOverwritingSpec::API.new
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
it 'overwrites header by after headers' do
|
217
|
-
get '/'
|
218
|
-
expect(last_response.headers['X-Test-Overwriting']).to eq('Bye')
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'grape-entity'
|
4
|
-
|
5
|
-
describe Grape::Middleware::Error do
|
6
|
-
module ErrorSpec
|
7
|
-
class ErrorEntity < Grape::Entity
|
8
|
-
expose :code
|
9
|
-
expose :static
|
10
|
-
|
11
|
-
def static
|
12
|
-
'static text'
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class ErrApp
|
17
|
-
class << self
|
18
|
-
attr_accessor :error, :format
|
19
|
-
|
20
|
-
def call(_env)
|
21
|
-
throw :error, error
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def app
|
28
|
-
opts = options
|
29
|
-
Rack::Builder.app do
|
30
|
-
use Spec::Support::EndpointFaker
|
31
|
-
use Grape::Middleware::Error, **opts
|
32
|
-
run ErrorSpec::ErrApp
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
let(:options) { { default_message: 'Aww, hamburgers.' } }
|
37
|
-
|
38
|
-
it 'sets the status code appropriately' do
|
39
|
-
ErrorSpec::ErrApp.error = { status: 410 }
|
40
|
-
get '/'
|
41
|
-
expect(last_response.status).to eq(410)
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'sets the status code based on the rack util status code symbol' do
|
45
|
-
ErrorSpec::ErrApp.error = { status: :gone }
|
46
|
-
get '/'
|
47
|
-
expect(last_response.status).to eq(410)
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'sets the error message appropriately' do
|
51
|
-
ErrorSpec::ErrApp.error = { message: 'Awesome stuff.' }
|
52
|
-
get '/'
|
53
|
-
expect(last_response.body).to eq('Awesome stuff.')
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'defaults to a 500 status' do
|
57
|
-
ErrorSpec::ErrApp.error = {}
|
58
|
-
get '/'
|
59
|
-
expect(last_response.status).to eq(500)
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'has a default message' do
|
63
|
-
ErrorSpec::ErrApp.error = {}
|
64
|
-
get '/'
|
65
|
-
expect(last_response.body).to eq('Aww, hamburgers.')
|
66
|
-
end
|
67
|
-
|
68
|
-
context 'with http code' do
|
69
|
-
let(:options) { { default_message: 'Aww, hamburgers.' } }
|
70
|
-
|
71
|
-
it 'adds the status code if wanted' do
|
72
|
-
ErrorSpec::ErrApp.error = { message: { code: 200 } }
|
73
|
-
get '/'
|
74
|
-
|
75
|
-
expect(last_response.body).to eq({ code: 200 }.to_json)
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'presents an error message' do
|
79
|
-
ErrorSpec::ErrApp.error = { message: { code: 200, with: ErrorSpec::ErrorEntity } }
|
80
|
-
get '/'
|
81
|
-
|
82
|
-
expect(last_response.body).to eq({ code: 200, static: 'static text' }.to_json)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,294 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe Grape::Middleware::Error do
|
4
|
-
let(:exception_app) do
|
5
|
-
Class.new do
|
6
|
-
class << self
|
7
|
-
def call(_env)
|
8
|
-
raise 'rain!'
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:other_exception_app) do
|
15
|
-
Class.new do
|
16
|
-
class << self
|
17
|
-
def call(_env)
|
18
|
-
raise NotImplementedError, 'snow!'
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
let(:custom_error_app) do
|
25
|
-
Class.new do
|
26
|
-
class << self
|
27
|
-
class CustomError < Grape::Exceptions::Base; end
|
28
|
-
|
29
|
-
def call(_env)
|
30
|
-
raise CustomError.new(status: 400, message: 'failed validation')
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
let(:error_hash_app) do
|
37
|
-
Class.new do
|
38
|
-
class << self
|
39
|
-
def error!(message, status)
|
40
|
-
throw :error, message: { error: message, detail: 'missing widget' }, status: status
|
41
|
-
end
|
42
|
-
|
43
|
-
def call(_env)
|
44
|
-
error!('rain!', 401)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
let(:access_denied_app) do
|
51
|
-
Class.new do
|
52
|
-
class << self
|
53
|
-
def error!(message, status)
|
54
|
-
throw :error, message: message, status: status
|
55
|
-
end
|
56
|
-
|
57
|
-
def call(_env)
|
58
|
-
error!('Access Denied', 401)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
let(:app) do
|
65
|
-
builder = Rack::Builder.new
|
66
|
-
builder.use Spec::Support::EndpointFaker
|
67
|
-
if options.any?
|
68
|
-
builder.use described_class, options
|
69
|
-
else
|
70
|
-
builder.use described_class
|
71
|
-
end
|
72
|
-
builder.run running_app
|
73
|
-
builder.to_app
|
74
|
-
end
|
75
|
-
|
76
|
-
context 'with defaults' do
|
77
|
-
let(:running_app) { exception_app }
|
78
|
-
let(:options) { {} }
|
79
|
-
|
80
|
-
it 'does not trap errors by default' do
|
81
|
-
expect { get '/' }.to raise_error(RuntimeError, 'rain!')
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
context 'with rescue_all' do
|
86
|
-
context 'StandardError exception' do
|
87
|
-
let(:running_app) { exception_app }
|
88
|
-
let(:options) { { rescue_all: true } }
|
89
|
-
|
90
|
-
it 'sets the message appropriately' do
|
91
|
-
get '/'
|
92
|
-
expect(last_response.body).to eq('rain!')
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'defaults to a 500 status' do
|
96
|
-
get '/'
|
97
|
-
expect(last_response.status).to eq(500)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
context 'Non-StandardError exception' do
|
102
|
-
let(:running_app) { other_exception_app }
|
103
|
-
let(:options) { { rescue_all: true } }
|
104
|
-
|
105
|
-
it 'does not trap errors other than StandardError' do
|
106
|
-
expect { get '/' }.to raise_error(NotImplementedError, 'snow!')
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
context 'Non-StandardError exception with a provided rescue handler' do
|
112
|
-
context 'default error response' do
|
113
|
-
let(:running_app) { other_exception_app }
|
114
|
-
let(:options) { { rescue_handlers: { NotImplementedError => nil } } }
|
115
|
-
|
116
|
-
it 'rescues the exception using the default handler' do
|
117
|
-
get '/'
|
118
|
-
expect(last_response.body).to eq('snow!')
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
context 'custom error response' do
|
123
|
-
let(:running_app) { other_exception_app }
|
124
|
-
let(:options) { { rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } } } }
|
125
|
-
|
126
|
-
it 'rescues the exception using the provided handler' do
|
127
|
-
get '/'
|
128
|
-
expect(last_response.body).to eq('rescued')
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
context do
|
134
|
-
let(:running_app) { exception_app }
|
135
|
-
let(:options) { { rescue_all: true, default_status: 500 } }
|
136
|
-
|
137
|
-
it 'is possible to specify a different default status code' do
|
138
|
-
get '/'
|
139
|
-
expect(last_response.status).to eq(500)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
context do
|
144
|
-
let(:running_app) { exception_app }
|
145
|
-
let(:options) { { rescue_all: true, format: :json } }
|
146
|
-
|
147
|
-
it 'is possible to return errors in json format' do
|
148
|
-
get '/'
|
149
|
-
expect(last_response.body).to eq('{"error":"rain!"}')
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
context do
|
154
|
-
let(:running_app) { error_hash_app }
|
155
|
-
let(:options) { { rescue_all: true, format: :json } }
|
156
|
-
|
157
|
-
it 'is possible to return hash errors in json format' do
|
158
|
-
get '/'
|
159
|
-
expect(['{"error":"rain!","detail":"missing widget"}',
|
160
|
-
'{"detail":"missing widget","error":"rain!"}']).to include(last_response.body)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
context do
|
165
|
-
let(:running_app) { exception_app }
|
166
|
-
let(:options) { { rescue_all: true, format: :jsonapi } }
|
167
|
-
|
168
|
-
it 'is possible to return errors in jsonapi format' do
|
169
|
-
get '/'
|
170
|
-
expect(last_response.body).to eq('{"error":"rain!"}')
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
context do
|
175
|
-
let(:running_app) { error_hash_app }
|
176
|
-
let(:options) { { rescue_all: true, format: :jsonapi } }
|
177
|
-
|
178
|
-
it 'is possible to return hash errors in jsonapi format' do
|
179
|
-
get '/'
|
180
|
-
expect(['{"error":"rain!","detail":"missing widget"}',
|
181
|
-
'{"detail":"missing widget","error":"rain!"}']).to include(last_response.body)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
context do
|
186
|
-
let(:running_app) { exception_app }
|
187
|
-
let(:options) { { rescue_all: true, format: :xml } }
|
188
|
-
|
189
|
-
it 'is possible to return errors in xml format' do
|
190
|
-
get '/'
|
191
|
-
expect(last_response.body).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <message>rain!</message>\n</error>\n")
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
context do
|
196
|
-
let(:running_app) { error_hash_app }
|
197
|
-
let(:options) { { rescue_all: true, format: :xml } }
|
198
|
-
|
199
|
-
it 'is possible to return hash errors in xml format' do
|
200
|
-
get '/'
|
201
|
-
expect(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <detail>missing widget</detail>\n <error>rain!</error>\n</error>\n",
|
202
|
-
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <error>rain!</error>\n <detail>missing widget</detail>\n</error>\n"]).to include(last_response.body)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
context do
|
207
|
-
let(:running_app) { exception_app }
|
208
|
-
let(:options) do
|
209
|
-
{
|
210
|
-
rescue_all: true,
|
211
|
-
format: :custom,
|
212
|
-
error_formatters: {
|
213
|
-
custom: lambda do |message, _backtrace, _options, _env, _original_exception|
|
214
|
-
{ custom_formatter: message }.inspect
|
215
|
-
end
|
216
|
-
}
|
217
|
-
}
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'is possible to specify a custom formatter' do
|
221
|
-
get '/'
|
222
|
-
expect(last_response.body).to eq('{:custom_formatter=>"rain!"}')
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
context do
|
227
|
-
let(:running_app) { access_denied_app }
|
228
|
-
let(:options) { {} }
|
229
|
-
|
230
|
-
it 'does not trap regular error! codes' do
|
231
|
-
get '/'
|
232
|
-
expect(last_response.status).to eq(401)
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
context do
|
237
|
-
let(:running_app) { custom_error_app }
|
238
|
-
let(:options) { { rescue_all: false } }
|
239
|
-
|
240
|
-
it 'responds to custom Grape exceptions appropriately' do
|
241
|
-
get '/'
|
242
|
-
expect(last_response.status).to eq(400)
|
243
|
-
expect(last_response.body).to eq('failed validation')
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
context 'with rescue_options :backtrace and :exception set to true' do
|
248
|
-
let(:running_app) { exception_app }
|
249
|
-
let(:options) do
|
250
|
-
{
|
251
|
-
rescue_all: true,
|
252
|
-
format: :json,
|
253
|
-
rescue_options: { backtrace: true, original_exception: true }
|
254
|
-
}
|
255
|
-
end
|
256
|
-
|
257
|
-
it 'is possible to return the backtrace and the original exception in json format' do
|
258
|
-
get '/'
|
259
|
-
expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original_exception', 'RuntimeError')
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
context do
|
264
|
-
let(:running_app) { exception_app }
|
265
|
-
let(:options) do
|
266
|
-
{
|
267
|
-
rescue_all: true,
|
268
|
-
format: :xml,
|
269
|
-
rescue_options: { backtrace: true, original_exception: true }
|
270
|
-
}
|
271
|
-
end
|
272
|
-
|
273
|
-
it 'is possible to return the backtrace and the original exception in xml format' do
|
274
|
-
get '/'
|
275
|
-
expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original-exception', 'RuntimeError')
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
context do
|
280
|
-
let(:running_app) { exception_app }
|
281
|
-
let(:options) do
|
282
|
-
{
|
283
|
-
rescue_all: true,
|
284
|
-
format: :txt,
|
285
|
-
rescue_options: { backtrace: true, original_exception: true }
|
286
|
-
}
|
287
|
-
end
|
288
|
-
|
289
|
-
it 'is possible to return the backtrace and the original exception in txt format' do
|
290
|
-
get '/'
|
291
|
-
expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original exception', 'RuntimeError')
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|