grape 1.3.3 → 1.6.2
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 +111 -2
- data/CONTRIBUTING.md +2 -1
- data/README.md +135 -23
- data/UPGRADING.md +237 -46
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +34 -42
- data/lib/grape/api.rb +21 -16
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +8 -5
- data/lib/grape/dsl/inside_route.rb +72 -53
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +11 -7
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +8 -9
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/dsl/validations.rb +18 -1
- data/lib/grape/eager_load.rb +1 -1
- data/lib/grape/endpoint.rb +29 -42
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/validation.rb +2 -3
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/auth/dsl.rb +7 -1
- data/lib/grape/middleware/base.rb +6 -3
- data/lib/grape/middleware/error.rb +11 -13
- data/lib/grape/middleware/formatter.rb +7 -7
- data/lib/grape/middleware/stack.rb +10 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/parser/json.rb +1 -1
- data/lib/grape/parser/xml.rb +1 -1
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +4 -1
- data/lib/grape/router/attribute_translator.rb +3 -3
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +31 -30
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
- data/lib/grape/util/base_inheritable.rb +2 -2
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +4 -2
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +97 -62
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +4 -5
- data/lib/grape/validations/types.rb +1 -4
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/all_or_none.rb +8 -5
- data/lib/grape/validations/validators/allow_blank.rb +9 -7
- data/lib/grape/validations/validators/as.rb +6 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
- data/lib/grape/validations/validators/base.rb +74 -69
- data/lib/grape/validations/validators/coerce.rb +63 -76
- data/lib/grape/validations/validators/default.rb +36 -34
- data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
- data/lib/grape/validations/validators/except_values.rb +13 -11
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
- data/lib/grape/validations/validators/presence.rb +7 -4
- data/lib/grape/validations/validators/regexp.rb +8 -5
- data/lib/grape/validations/validators/same_as.rb +18 -15
- data/lib/grape/validations/validators/values.rb +61 -56
- data/lib/grape/validations.rb +6 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +7 -3
- data/spec/grape/api/custom_validations_spec.rb +77 -45
- data/spec/grape/api/deeply_included_options_spec.rb +3 -3
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
- data/spec/grape/api_remount_spec.rb +25 -19
- data/spec/grape/api_spec.rb +576 -211
- data/spec/grape/dsl/callbacks_spec.rb +2 -1
- data/spec/grape/dsl/headers_spec.rb +39 -9
- data/spec/grape/dsl/helpers_spec.rb +3 -2
- data/spec/grape/dsl/inside_route_spec.rb +185 -34
- data/spec/grape/dsl/logger_spec.rb +16 -18
- data/spec/grape/dsl/middleware_spec.rb +2 -1
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +10 -7
- data/spec/grape/endpoint/declared_spec.rb +848 -0
- data/spec/grape/endpoint_spec.rb +77 -589
- data/spec/grape/entity_spec.rb +29 -23
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
- data/spec/grape/exceptions/validation_spec.rb +5 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
- data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
- data/spec/grape/loading_spec.rb +8 -8
- data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
- data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
- data/spec/grape/middleware/base_spec.rb +24 -15
- data/spec/grape/middleware/error_spec.rb +3 -3
- data/spec/grape/middleware/exception_spec.rb +111 -161
- data/spec/grape/middleware/formatter_spec.rb +28 -7
- data/spec/grape/middleware/globals_spec.rb +7 -4
- data/spec/grape/middleware/stack_spec.rb +15 -12
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
- data/spec/grape/middleware/versioner/header_spec.rb +14 -13
- data/spec/grape/middleware/versioner/param_spec.rb +7 -1
- data/spec/grape/middleware/versioner/path_spec.rb +5 -1
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +4 -0
- data/spec/grape/path_spec.rb +52 -52
- data/spec/grape/presenters/presenter_spec.rb +7 -6
- data/spec/grape/request_spec.rb +6 -4
- data/spec/grape/util/inheritable_setting_spec.rb +7 -7
- data/spec/grape/util/inheritable_values_spec.rb +3 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
- data/spec/grape/util/stackable_values_spec.rb +7 -5
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
- data/spec/grape/validations/params_scope_spec.rb +72 -10
- data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
- data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
- data/spec/grape/validations/types_spec.rb +8 -8
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
- data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
- data/spec/grape/validations/validators/coerce_spec.rb +248 -33
- data/spec/grape/validations/validators/default_spec.rb +121 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
- data/spec/grape/validations/validators/except_values_spec.rb +4 -3
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
- data/spec/grape/validations/validators/presence_spec.rb +16 -1
- data/spec/grape/validations/validators/regexp_spec.rb +25 -31
- data/spec/grape/validations/validators/same_as_spec.rb +14 -20
- data/spec/grape/validations/validators/values_spec.rb +183 -178
- data/spec/grape/validations_spec.rb +342 -29
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +1 -1
- data/spec/integration/multi_xml/xml_spec.rb +1 -1
- data/spec/shared/versioning_examples.rb +32 -29
- data/spec/spec_helper.rb +12 -12
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/chunks.rb +14 -0
- data/spec/support/versioned_helpers.rb +4 -6
- metadata +110 -102
@@ -3,76 +3,82 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Grape::Middleware::Error do
|
6
|
-
|
7
|
-
|
8
|
-
class ExceptionApp
|
6
|
+
let(:exception_app) do
|
7
|
+
Class.new do
|
9
8
|
class << self
|
10
9
|
def call(_env)
|
11
10
|
raise 'rain!'
|
12
11
|
end
|
13
12
|
end
|
14
13
|
end
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
let(:other_exception_app) do
|
17
|
+
Class.new do
|
18
18
|
class << self
|
19
19
|
def call(_env)
|
20
20
|
raise NotImplementedError, 'snow!'
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
26
|
+
let(:custom_error_app) do
|
27
|
+
Class.new do
|
27
28
|
class << self
|
28
|
-
|
29
|
-
throw :error, message: { error: message, detail: 'missing widget' }, status: status
|
30
|
-
end
|
29
|
+
class CustomError < Grape::Exceptions::Base; end
|
31
30
|
|
32
31
|
def call(_env)
|
33
|
-
|
32
|
+
raise CustomError.new(status: 400, message: 'failed validation')
|
34
33
|
end
|
35
34
|
end
|
36
35
|
end
|
36
|
+
end
|
37
37
|
|
38
|
-
|
39
|
-
|
38
|
+
let(:error_hash_app) do
|
39
|
+
Class.new do
|
40
40
|
class << self
|
41
41
|
def error!(message, status)
|
42
|
-
throw :error, message: message, status: status
|
42
|
+
throw :error, message: { error: message, detail: 'missing widget' }, status: status
|
43
43
|
end
|
44
44
|
|
45
45
|
def call(_env)
|
46
|
-
error!('
|
46
|
+
error!('rain!', 401)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
class CustomErrorApp
|
52
|
+
let(:access_denied_app) do
|
53
|
+
Class.new do
|
56
54
|
class << self
|
55
|
+
def error!(message, status)
|
56
|
+
throw :error, message: message, status: status
|
57
|
+
end
|
58
|
+
|
57
59
|
def call(_env)
|
58
|
-
|
60
|
+
error!('Access Denied', 401)
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
+
let(:app) do
|
67
|
+
builder = Rack::Builder.new
|
68
|
+
builder.use Spec::Support::EndpointFaker
|
69
|
+
if options.any?
|
70
|
+
builder.use described_class, options
|
71
|
+
else
|
72
|
+
builder.use described_class
|
73
|
+
end
|
74
|
+
builder.run running_app
|
75
|
+
builder.to_app
|
66
76
|
end
|
67
77
|
|
68
78
|
context 'with defaults' do
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
use Grape::Middleware::Error
|
73
|
-
run ExceptionSpec::ExceptionApp
|
74
|
-
end
|
75
|
-
end
|
79
|
+
let(:running_app) { exception_app }
|
80
|
+
let(:options) { {} }
|
81
|
+
|
76
82
|
it 'does not trap errors by default' do
|
77
83
|
expect { get '/' }.to raise_error(RuntimeError, 'rain!')
|
78
84
|
end
|
@@ -80,17 +86,14 @@ describe Grape::Middleware::Error do
|
|
80
86
|
|
81
87
|
context 'with rescue_all' do
|
82
88
|
context 'StandardError exception' do
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
use Grape::Middleware::Error, rescue_all: true
|
87
|
-
run ExceptionSpec::ExceptionApp
|
88
|
-
end
|
89
|
-
end
|
89
|
+
let(:running_app) { exception_app }
|
90
|
+
let(:options) { { rescue_all: true } }
|
91
|
+
|
90
92
|
it 'sets the message appropriately' do
|
91
93
|
get '/'
|
92
94
|
expect(last_response.body).to eq('rain!')
|
93
95
|
end
|
96
|
+
|
94
97
|
it 'defaults to a 500 status' do
|
95
98
|
get '/'
|
96
99
|
expect(last_response.status).to eq(500)
|
@@ -98,13 +101,9 @@ describe Grape::Middleware::Error do
|
|
98
101
|
end
|
99
102
|
|
100
103
|
context 'Non-StandardError exception' do
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
use Grape::Middleware::Error, rescue_all: true
|
105
|
-
run ExceptionSpec::OtherExceptionApp
|
106
|
-
end
|
107
|
-
end
|
104
|
+
let(:running_app) { other_exception_app }
|
105
|
+
let(:options) { { rescue_all: true } }
|
106
|
+
|
108
107
|
it 'does not trap errors other than StandardError' do
|
109
108
|
expect { get '/' }.to raise_error(NotImplementedError, 'snow!')
|
110
109
|
end
|
@@ -113,13 +112,9 @@ describe Grape::Middleware::Error do
|
|
113
112
|
|
114
113
|
context 'Non-StandardError exception with a provided rescue handler' do
|
115
114
|
context 'default error response' do
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => nil }
|
120
|
-
run ExceptionSpec::OtherExceptionApp
|
121
|
-
end
|
122
|
-
end
|
115
|
+
let(:running_app) { other_exception_app }
|
116
|
+
let(:options) { { rescue_handlers: { NotImplementedError => nil } } }
|
117
|
+
|
123
118
|
it 'rescues the exception using the default handler' do
|
124
119
|
get '/'
|
125
120
|
expect(last_response.body).to eq('snow!')
|
@@ -127,13 +122,9 @@ describe Grape::Middleware::Error do
|
|
127
122
|
end
|
128
123
|
|
129
124
|
context 'custom error response' do
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } }
|
134
|
-
run ExceptionSpec::OtherExceptionApp
|
135
|
-
end
|
136
|
-
end
|
125
|
+
let(:running_app) { other_exception_app }
|
126
|
+
let(:options) { { rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } } } }
|
127
|
+
|
137
128
|
it 'rescues the exception using the provided handler' do
|
138
129
|
get '/'
|
139
130
|
expect(last_response.body).to eq('rescued')
|
@@ -142,13 +133,9 @@ describe Grape::Middleware::Error do
|
|
142
133
|
end
|
143
134
|
|
144
135
|
context do
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
use Grape::Middleware::Error, rescue_all: true, default_status: 500
|
149
|
-
run ExceptionSpec::ExceptionApp
|
150
|
-
end
|
151
|
-
end
|
136
|
+
let(:running_app) { exception_app }
|
137
|
+
let(:options) { { rescue_all: true, default_status: 500 } }
|
138
|
+
|
152
139
|
it 'is possible to specify a different default status code' do
|
153
140
|
get '/'
|
154
141
|
expect(last_response.status).to eq(500)
|
@@ -156,13 +143,9 @@ describe Grape::Middleware::Error do
|
|
156
143
|
end
|
157
144
|
|
158
145
|
context do
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
use Grape::Middleware::Error, rescue_all: true, format: :json
|
163
|
-
run ExceptionSpec::ExceptionApp
|
164
|
-
end
|
165
|
-
end
|
146
|
+
let(:running_app) { exception_app }
|
147
|
+
let(:options) { { rescue_all: true, format: :json } }
|
148
|
+
|
166
149
|
it 'is possible to return errors in json format' do
|
167
150
|
get '/'
|
168
151
|
expect(last_response.body).to eq('{"error":"rain!"}')
|
@@ -170,13 +153,9 @@ describe Grape::Middleware::Error do
|
|
170
153
|
end
|
171
154
|
|
172
155
|
context do
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
use Grape::Middleware::Error, rescue_all: true, format: :json
|
177
|
-
run ExceptionSpec::ErrorHashApp
|
178
|
-
end
|
179
|
-
end
|
156
|
+
let(:running_app) { error_hash_app }
|
157
|
+
let(:options) { { rescue_all: true, format: :json } }
|
158
|
+
|
180
159
|
it 'is possible to return hash errors in json format' do
|
181
160
|
get '/'
|
182
161
|
expect(['{"error":"rain!","detail":"missing widget"}',
|
@@ -185,13 +164,9 @@ describe Grape::Middleware::Error do
|
|
185
164
|
end
|
186
165
|
|
187
166
|
context do
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
|
192
|
-
run ExceptionSpec::ExceptionApp
|
193
|
-
end
|
194
|
-
end
|
167
|
+
let(:running_app) { exception_app }
|
168
|
+
let(:options) { { rescue_all: true, format: :jsonapi } }
|
169
|
+
|
195
170
|
it 'is possible to return errors in jsonapi format' do
|
196
171
|
get '/'
|
197
172
|
expect(last_response.body).to eq('{"error":"rain!"}')
|
@@ -199,13 +174,8 @@ describe Grape::Middleware::Error do
|
|
199
174
|
end
|
200
175
|
|
201
176
|
context do
|
202
|
-
|
203
|
-
|
204
|
-
use Spec::Support::EndpointFaker
|
205
|
-
use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
|
206
|
-
run ExceptionSpec::ErrorHashApp
|
207
|
-
end
|
208
|
-
end
|
177
|
+
let(:running_app) { error_hash_app }
|
178
|
+
let(:options) { { rescue_all: true, format: :jsonapi } }
|
209
179
|
|
210
180
|
it 'is possible to return hash errors in jsonapi format' do
|
211
181
|
get '/'
|
@@ -215,13 +185,9 @@ describe Grape::Middleware::Error do
|
|
215
185
|
end
|
216
186
|
|
217
187
|
context do
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
use Grape::Middleware::Error, rescue_all: true, format: :xml
|
222
|
-
run ExceptionSpec::ExceptionApp
|
223
|
-
end
|
224
|
-
end
|
188
|
+
let(:running_app) { exception_app }
|
189
|
+
let(:options) { { rescue_all: true, format: :xml } }
|
190
|
+
|
225
191
|
it 'is possible to return errors in xml format' do
|
226
192
|
get '/'
|
227
193
|
expect(last_response.body).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <message>rain!</message>\n</error>\n")
|
@@ -229,13 +195,9 @@ describe Grape::Middleware::Error do
|
|
229
195
|
end
|
230
196
|
|
231
197
|
context do
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
use Grape::Middleware::Error, rescue_all: true, format: :xml
|
236
|
-
run ExceptionSpec::ErrorHashApp
|
237
|
-
end
|
238
|
-
end
|
198
|
+
let(:running_app) { error_hash_app }
|
199
|
+
let(:options) { { rescue_all: true, format: :xml } }
|
200
|
+
|
239
201
|
it 'is possible to return hash errors in xml format' do
|
240
202
|
get '/'
|
241
203
|
expect(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <detail>missing widget</detail>\n <error>rain!</error>\n</error>\n",
|
@@ -244,20 +206,19 @@ describe Grape::Middleware::Error do
|
|
244
206
|
end
|
245
207
|
|
246
208
|
context do
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
run ExceptionSpec::ExceptionApp
|
259
|
-
end
|
209
|
+
let(:running_app) { exception_app }
|
210
|
+
let(:options) do
|
211
|
+
{
|
212
|
+
rescue_all: true,
|
213
|
+
format: :custom,
|
214
|
+
error_formatters: {
|
215
|
+
custom: lambda do |message, _backtrace, _options, _env, _original_exception|
|
216
|
+
{ custom_formatter: message }.inspect
|
217
|
+
end
|
218
|
+
}
|
219
|
+
}
|
260
220
|
end
|
221
|
+
|
261
222
|
it 'is possible to specify a custom formatter' do
|
262
223
|
get '/'
|
263
224
|
expect(last_response.body).to eq('{:custom_formatter=>"rain!"}')
|
@@ -265,13 +226,9 @@ describe Grape::Middleware::Error do
|
|
265
226
|
end
|
266
227
|
|
267
228
|
context do
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
use Grape::Middleware::Error
|
272
|
-
run ExceptionSpec::AccessDeniedApp
|
273
|
-
end
|
274
|
-
end
|
229
|
+
let(:running_app) { access_denied_app }
|
230
|
+
let(:options) { {} }
|
231
|
+
|
275
232
|
it 'does not trap regular error! codes' do
|
276
233
|
get '/'
|
277
234
|
expect(last_response.status).to eq(401)
|
@@ -279,13 +236,9 @@ describe Grape::Middleware::Error do
|
|
279
236
|
end
|
280
237
|
|
281
238
|
context do
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
use Grape::Middleware::Error, rescue_all: false
|
286
|
-
run ExceptionSpec::CustomErrorApp
|
287
|
-
end
|
288
|
-
end
|
239
|
+
let(:running_app) { custom_error_app }
|
240
|
+
let(:options) { { rescue_all: false } }
|
241
|
+
|
289
242
|
it 'responds to custom Grape exceptions appropriately' do
|
290
243
|
get '/'
|
291
244
|
expect(last_response.status).to eq(400)
|
@@ -294,16 +247,15 @@ describe Grape::Middleware::Error do
|
|
294
247
|
end
|
295
248
|
|
296
249
|
context 'with rescue_options :backtrace and :exception set to true' do
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
run ExceptionSpec::ExceptionApp
|
305
|
-
end
|
250
|
+
let(:running_app) { exception_app }
|
251
|
+
let(:options) do
|
252
|
+
{
|
253
|
+
rescue_all: true,
|
254
|
+
format: :json,
|
255
|
+
rescue_options: { backtrace: true, original_exception: true }
|
256
|
+
}
|
306
257
|
end
|
258
|
+
|
307
259
|
it 'is possible to return the backtrace and the original exception in json format' do
|
308
260
|
get '/'
|
309
261
|
expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original_exception', 'RuntimeError')
|
@@ -311,16 +263,15 @@ describe Grape::Middleware::Error do
|
|
311
263
|
end
|
312
264
|
|
313
265
|
context do
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
run ExceptionSpec::ExceptionApp
|
322
|
-
end
|
266
|
+
let(:running_app) { exception_app }
|
267
|
+
let(:options) do
|
268
|
+
{
|
269
|
+
rescue_all: true,
|
270
|
+
format: :xml,
|
271
|
+
rescue_options: { backtrace: true, original_exception: true }
|
272
|
+
}
|
323
273
|
end
|
274
|
+
|
324
275
|
it 'is possible to return the backtrace and the original exception in xml format' do
|
325
276
|
get '/'
|
326
277
|
expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original-exception', 'RuntimeError')
|
@@ -328,16 +279,15 @@ describe Grape::Middleware::Error do
|
|
328
279
|
end
|
329
280
|
|
330
281
|
context do
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
run ExceptionSpec::ExceptionApp
|
339
|
-
end
|
282
|
+
let(:running_app) { exception_app }
|
283
|
+
let(:options) do
|
284
|
+
{
|
285
|
+
rescue_all: true,
|
286
|
+
format: :txt,
|
287
|
+
rescue_options: { backtrace: true, original_exception: true }
|
288
|
+
}
|
340
289
|
end
|
290
|
+
|
341
291
|
it 'is possible to return the backtrace and the original exception in txt format' do
|
342
292
|
get '/'
|
343
293
|
expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original exception', 'RuntimeError')
|
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Grape::Middleware::Formatter do
|
6
|
-
subject {
|
6
|
+
subject { described_class.new(app) }
|
7
|
+
|
7
8
|
before { allow(subject).to receive(:dup).and_return(subject) }
|
8
9
|
|
9
10
|
let(:body) { { 'foo' => 'bar' } }
|
@@ -11,6 +12,7 @@ describe Grape::Middleware::Formatter do
|
|
11
12
|
|
12
13
|
context 'serialization' do
|
13
14
|
let(:body) { { 'abc' => 'def' } }
|
15
|
+
|
14
16
|
it 'looks at the bodies for possibly serializable data' do
|
15
17
|
_, _, bodies = *subject.call('PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json')
|
16
18
|
bodies.each { |b| expect(b).to eq(::Grape::Json.dump(body)) }
|
@@ -18,9 +20,10 @@ describe Grape::Middleware::Formatter do
|
|
18
20
|
|
19
21
|
context 'default format' do
|
20
22
|
let(:body) { ['foo'] }
|
23
|
+
|
21
24
|
it 'calls #to_json since default format is json' do
|
22
25
|
body.instance_eval do
|
23
|
-
def to_json
|
26
|
+
def to_json(*_args)
|
24
27
|
'"bar"'
|
25
28
|
end
|
26
29
|
end
|
@@ -31,9 +34,10 @@ describe Grape::Middleware::Formatter do
|
|
31
34
|
|
32
35
|
context 'jsonapi' do
|
33
36
|
let(:body) { { 'foos' => [{ 'bar' => 'baz' }] } }
|
37
|
+
|
34
38
|
it 'calls #to_json if the content type is jsonapi' do
|
35
39
|
body.instance_eval do
|
36
|
-
def to_json
|
40
|
+
def to_json(*_args)
|
37
41
|
'{"foos":[{"bar":"baz"}] }'
|
38
42
|
end
|
39
43
|
end
|
@@ -44,6 +48,7 @@ describe Grape::Middleware::Formatter do
|
|
44
48
|
|
45
49
|
context 'xml' do
|
46
50
|
let(:body) { +'string' }
|
51
|
+
|
47
52
|
it 'calls #to_xml if the content type is xml' do
|
48
53
|
body.instance_eval do
|
49
54
|
def to_xml
|
@@ -58,6 +63,7 @@ describe Grape::Middleware::Formatter do
|
|
58
63
|
|
59
64
|
context 'error handling' do
|
60
65
|
let(:formatter) { double(:formatter) }
|
66
|
+
|
61
67
|
before do
|
62
68
|
allow(Grape::Formatter).to receive(:formatter_for) { formatter }
|
63
69
|
end
|
@@ -67,7 +73,7 @@ describe Grape::Middleware::Formatter do
|
|
67
73
|
|
68
74
|
expect do
|
69
75
|
catch(:error) { subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json') }
|
70
|
-
end.
|
76
|
+
end.not_to raise_error
|
71
77
|
end
|
72
78
|
|
73
79
|
it 'does not rescue other exceptions' do
|
@@ -147,7 +153,7 @@ describe Grape::Middleware::Formatter do
|
|
147
153
|
subject.options[:content_types][:custom] = 'application/vnd.test+json'
|
148
154
|
end
|
149
155
|
|
150
|
-
it '
|
156
|
+
it 'uses the custom type' do
|
151
157
|
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
|
152
158
|
expect(subject.env['api.format']).to eq(:custom)
|
153
159
|
end
|
@@ -164,26 +170,31 @@ describe Grape::Middleware::Formatter do
|
|
164
170
|
_, headers, = subject.call('PATH_INFO' => '/info.json')
|
165
171
|
expect(headers['Content-type']).to eq('application/json')
|
166
172
|
end
|
173
|
+
|
167
174
|
it 'is set for xml' do
|
168
175
|
_, headers, = subject.call('PATH_INFO' => '/info.xml')
|
169
176
|
expect(headers['Content-type']).to eq('application/xml')
|
170
177
|
end
|
178
|
+
|
171
179
|
it 'is set for txt' do
|
172
180
|
_, headers, = subject.call('PATH_INFO' => '/info.txt')
|
173
181
|
expect(headers['Content-type']).to eq('text/plain')
|
174
182
|
end
|
183
|
+
|
175
184
|
it 'is set for custom' do
|
176
185
|
subject.options[:content_types] = {}
|
177
186
|
subject.options[:content_types][:custom] = 'application/x-custom'
|
178
187
|
_, headers, = subject.call('PATH_INFO' => '/info.custom')
|
179
188
|
expect(headers['Content-type']).to eq('application/x-custom')
|
180
189
|
end
|
190
|
+
|
181
191
|
it 'is set for vendored with registered type' do
|
182
192
|
subject.options[:content_types] = {}
|
183
193
|
subject.options[:content_types][:custom] = 'application/vnd.test+json'
|
184
194
|
_, headers, = subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
|
185
195
|
expect(headers['Content-type']).to eq('application/vnd.test+json')
|
186
196
|
end
|
197
|
+
|
187
198
|
it 'is set to closest generic for custom vendored/versioned without registered type' do
|
188
199
|
_, headers, = subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
|
189
200
|
expect(headers['Content-type']).to eq('application/json')
|
@@ -198,13 +209,16 @@ describe Grape::Middleware::Formatter do
|
|
198
209
|
_, _, body = subject.call('PATH_INFO' => '/info.custom')
|
199
210
|
expect(read_chunks(body)).to eq(['CUSTOM FORMAT'])
|
200
211
|
end
|
212
|
+
|
201
213
|
context 'default' do
|
202
214
|
let(:body) { ['blah'] }
|
215
|
+
|
203
216
|
it 'uses default json formatter' do
|
204
217
|
_, _, body = subject.call('PATH_INFO' => '/info.json')
|
205
218
|
expect(read_chunks(body)).to eq(['["blah"]'])
|
206
219
|
end
|
207
220
|
end
|
221
|
+
|
208
222
|
it 'uses custom json formatter' do
|
209
223
|
subject.options[:formatters][:json] = ->(_obj, _env) { 'CUSTOM JSON FORMAT' }
|
210
224
|
_, _, body = subject.call('PATH_INFO' => '/info.json')
|
@@ -272,6 +286,7 @@ describe Grape::Middleware::Formatter do
|
|
272
286
|
|
273
287
|
context 'when body is nil' do
|
274
288
|
let(:io) { double }
|
289
|
+
|
275
290
|
before do
|
276
291
|
allow(io).to receive_message_chain(:rewind, :read).and_return(nil)
|
277
292
|
end
|
@@ -290,6 +305,7 @@ describe Grape::Middleware::Formatter do
|
|
290
305
|
|
291
306
|
context 'when body is empty' do
|
292
307
|
let(:io) { double }
|
308
|
+
|
293
309
|
before do
|
294
310
|
allow(io).to receive_message_chain(:rewind, :read).and_return('')
|
295
311
|
end
|
@@ -334,6 +350,7 @@ describe Grape::Middleware::Formatter do
|
|
334
350
|
expect(subject.env['rack.request.form_hash']['is_boolean']).to be true
|
335
351
|
expect(subject.env['rack.request.form_hash']['string']).to eq('thing')
|
336
352
|
end
|
353
|
+
|
337
354
|
it 'rewinds IO' do
|
338
355
|
io = StringIO.new('{"is_boolean":true,"string":"thing"}')
|
339
356
|
io.read
|
@@ -347,6 +364,7 @@ describe Grape::Middleware::Formatter do
|
|
347
364
|
expect(subject.env['rack.request.form_hash']['is_boolean']).to be true
|
348
365
|
expect(subject.env['rack.request.form_hash']['string']).to eq('thing')
|
349
366
|
end
|
367
|
+
|
350
368
|
it "parses the body from an xml #{method} and copies values into rack.request.from_hash" do
|
351
369
|
io = StringIO.new('<thing><name>Test</name></thing>')
|
352
370
|
subject.call(
|
@@ -362,6 +380,7 @@ describe Grape::Middleware::Formatter do
|
|
362
380
|
expect(subject.env['rack.request.form_hash']['thing']['name']['__content__']).to eq('Test')
|
363
381
|
end
|
364
382
|
end
|
383
|
+
|
365
384
|
[Rack::Request::FORM_DATA_MEDIA_TYPES, Rack::Request::PARSEABLE_DATA_MEDIA_TYPES].flatten.each do |content_type|
|
366
385
|
it "ignores #{content_type}" do
|
367
386
|
io = StringIO.new('name=Other+Test+Thing')
|
@@ -380,7 +399,7 @@ describe Grape::Middleware::Formatter do
|
|
380
399
|
|
381
400
|
context 'send file' do
|
382
401
|
let(:file) { double(File) }
|
383
|
-
let(:file_body) { Grape::
|
402
|
+
let(:file_body) { Grape::ServeStream::StreamResponse.new(file) }
|
384
403
|
let(:app) { ->(_env) { [200, {}, file_body] } }
|
385
404
|
|
386
405
|
it 'returns a file response' do
|
@@ -400,10 +419,12 @@ describe Grape::Middleware::Formatter do
|
|
400
419
|
end
|
401
420
|
end
|
402
421
|
let(:app) { ->(_env) { [200, {}, ['']] } }
|
422
|
+
|
403
423
|
before do
|
404
424
|
Grape::Formatter.register :invalid, InvalidFormatter
|
405
425
|
Grape::ContentTypes.register :invalid, 'application/x-invalid'
|
406
426
|
end
|
427
|
+
|
407
428
|
after do
|
408
429
|
Grape::ContentTypes.default_elements.delete(:invalid)
|
409
430
|
Grape::Formatter.default_elements.delete(:invalid)
|
@@ -418,7 +439,7 @@ describe Grape::Middleware::Formatter do
|
|
418
439
|
|
419
440
|
context 'custom parser raises exception and rescue options are enabled for backtrace and original_exception' do
|
420
441
|
it 'adds the backtrace and original_exception to the error output' do
|
421
|
-
subject =
|
442
|
+
subject = described_class.new(
|
422
443
|
app,
|
423
444
|
rescue_options: { backtrace: true, original_exception: true },
|
424
445
|
parsers: { json: ->(_object, _env) { raise StandardError, 'fail' } }
|
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Grape::Middleware::Globals do
|
6
|
-
subject {
|
6
|
+
subject { described_class.new(blank_app) }
|
7
|
+
|
7
8
|
before { allow(subject).to receive(:dup).and_return(subject) }
|
8
9
|
|
9
10
|
let(:blank_app) { ->(_env) { [200, {}, 'Hi there.'] } }
|
@@ -13,15 +14,17 @@ describe Grape::Middleware::Globals do
|
|
13
14
|
end
|
14
15
|
|
15
16
|
context 'environment' do
|
16
|
-
it '
|
17
|
+
it 'sets the grape.request environment' do
|
17
18
|
subject.call({})
|
18
19
|
expect(subject.env['grape.request']).to be_a(Grape::Request)
|
19
20
|
end
|
20
|
-
|
21
|
+
|
22
|
+
it 'sets the grape.request.headers environment' do
|
21
23
|
subject.call({})
|
22
24
|
expect(subject.env['grape.request.headers']).to be_a(Hash)
|
23
25
|
end
|
24
|
-
|
26
|
+
|
27
|
+
it 'sets the grape.request.params environment' do
|
25
28
|
subject.call('QUERY_STRING' => 'test=1', 'rack.input' => StringIO.new)
|
26
29
|
expect(subject.env['grape.request.params']).to be_a(Hash)
|
27
30
|
end
|