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