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,846 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe Grape::Endpoint do
|
4
|
-
subject { Class.new(Grape::API) }
|
5
|
-
|
6
|
-
def app
|
7
|
-
subject
|
8
|
-
end
|
9
|
-
|
10
|
-
describe '#declared' do
|
11
|
-
before do
|
12
|
-
subject.format :json
|
13
|
-
subject.params do
|
14
|
-
requires :first
|
15
|
-
optional :second
|
16
|
-
optional :third, default: 'third-default'
|
17
|
-
optional :multiple_types, types: [Integer, String]
|
18
|
-
optional :nested, type: Hash do
|
19
|
-
optional :fourth
|
20
|
-
optional :fifth
|
21
|
-
optional :nested_two, type: Hash do
|
22
|
-
optional :sixth
|
23
|
-
optional :nested_three, type: Hash do
|
24
|
-
optional :seventh
|
25
|
-
end
|
26
|
-
end
|
27
|
-
optional :nested_arr, type: Array do
|
28
|
-
optional :eighth
|
29
|
-
end
|
30
|
-
optional :empty_arr, type: Array
|
31
|
-
optional :empty_typed_arr, type: Array[String]
|
32
|
-
optional :empty_hash, type: Hash
|
33
|
-
optional :empty_set, type: Set
|
34
|
-
optional :empty_typed_set, type: Set[String]
|
35
|
-
end
|
36
|
-
optional :arr, type: Array do
|
37
|
-
optional :nineth
|
38
|
-
end
|
39
|
-
optional :empty_arr, type: Array
|
40
|
-
optional :empty_typed_arr, type: Array[String]
|
41
|
-
optional :empty_hash, type: Hash
|
42
|
-
optional :empty_set, type: Set
|
43
|
-
optional :empty_typed_set, type: Set[String]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context 'when params are not built with default class' do
|
48
|
-
it 'returns an object that corresponds with the params class - hash with indifferent access' do
|
49
|
-
subject.params do
|
50
|
-
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
51
|
-
end
|
52
|
-
subject.get '/declared' do
|
53
|
-
d = declared(params, include_missing: true)
|
54
|
-
{ declared_class: d.class.to_s }
|
55
|
-
end
|
56
|
-
|
57
|
-
get '/declared?first=present'
|
58
|
-
expect(JSON.parse(last_response.body)['declared_class']).to eq('ActiveSupport::HashWithIndifferentAccess')
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'returns an object that corresponds with the params class - hashie mash' do
|
62
|
-
subject.params do
|
63
|
-
build_with Grape::Extensions::Hashie::Mash::ParamBuilder
|
64
|
-
end
|
65
|
-
subject.get '/declared' do
|
66
|
-
d = declared(params, include_missing: true)
|
67
|
-
{ declared_class: d.class.to_s }
|
68
|
-
end
|
69
|
-
|
70
|
-
get '/declared?first=present'
|
71
|
-
expect(JSON.parse(last_response.body)['declared_class']).to eq('Hashie::Mash')
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'returns an object that corresponds with the params class - hash' do
|
75
|
-
subject.params do
|
76
|
-
build_with Grape::Extensions::Hash::ParamBuilder
|
77
|
-
end
|
78
|
-
subject.get '/declared' do
|
79
|
-
d = declared(params, include_missing: true)
|
80
|
-
{ declared_class: d.class.to_s }
|
81
|
-
end
|
82
|
-
|
83
|
-
get '/declared?first=present'
|
84
|
-
expect(JSON.parse(last_response.body)['declared_class']).to eq('Hash')
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'shows nil for nested params if include_missing is true' do
|
89
|
-
subject.get '/declared' do
|
90
|
-
declared(params, include_missing: true)
|
91
|
-
end
|
92
|
-
|
93
|
-
get '/declared?first=present'
|
94
|
-
expect(last_response.status).to eq(200)
|
95
|
-
expect(JSON.parse(last_response.body)['nested']['fourth']).to be_nil
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'shows nil for multiple allowed types if include_missing is true' do
|
99
|
-
subject.get '/declared' do
|
100
|
-
declared(params, include_missing: true)
|
101
|
-
end
|
102
|
-
|
103
|
-
get '/declared?first=present'
|
104
|
-
expect(last_response.status).to eq(200)
|
105
|
-
expect(JSON.parse(last_response.body)['multiple_types']).to be_nil
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'does not work in a before filter' do
|
109
|
-
subject.before do
|
110
|
-
declared(params)
|
111
|
-
end
|
112
|
-
subject.get('/declared') { declared(params) }
|
113
|
-
|
114
|
-
expect { get('/declared') }.to raise_error(
|
115
|
-
Grape::DSL::InsideRoute::MethodNotYetAvailable
|
116
|
-
)
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'has as many keys as there are declared params' do
|
120
|
-
subject.get '/declared' do
|
121
|
-
declared(params)
|
122
|
-
end
|
123
|
-
get '/declared?first=present'
|
124
|
-
expect(last_response.status).to eq(200)
|
125
|
-
expect(JSON.parse(last_response.body).keys.size).to eq(11)
|
126
|
-
end
|
127
|
-
|
128
|
-
it 'has a optional param with default value all the time' do
|
129
|
-
subject.get '/declared' do
|
130
|
-
declared(params)
|
131
|
-
end
|
132
|
-
get '/declared?first=one'
|
133
|
-
expect(last_response.status).to eq(200)
|
134
|
-
expect(JSON.parse(last_response.body)['third']).to eql('third-default')
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'builds nested params' do
|
138
|
-
subject.get '/declared' do
|
139
|
-
declared(params)
|
140
|
-
end
|
141
|
-
|
142
|
-
get '/declared?first=present&nested[fourth]=1'
|
143
|
-
expect(last_response.status).to eq(200)
|
144
|
-
expect(JSON.parse(last_response.body)['nested'].keys.size).to eq 9
|
145
|
-
end
|
146
|
-
|
147
|
-
it 'builds arrays correctly' do
|
148
|
-
subject.params do
|
149
|
-
requires :first
|
150
|
-
optional :second, type: Array
|
151
|
-
end
|
152
|
-
subject.post('/declared') { declared(params) }
|
153
|
-
|
154
|
-
post '/declared', first: 'present', second: ['present']
|
155
|
-
expect(last_response.status).to eq(201)
|
156
|
-
|
157
|
-
body = JSON.parse(last_response.body)
|
158
|
-
expect(body['second']).to eq(['present'])
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'builds nested params when given array' do
|
162
|
-
subject.get '/dummy' do
|
163
|
-
end
|
164
|
-
subject.params do
|
165
|
-
requires :first
|
166
|
-
optional :second
|
167
|
-
optional :third, default: 'third-default'
|
168
|
-
optional :nested, type: Array do
|
169
|
-
optional :fourth
|
170
|
-
end
|
171
|
-
end
|
172
|
-
subject.get '/declared' do
|
173
|
-
declared(params)
|
174
|
-
end
|
175
|
-
|
176
|
-
get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
|
177
|
-
expect(last_response.status).to eq(200)
|
178
|
-
expect(JSON.parse(last_response.body)['nested'].size).to eq 2
|
179
|
-
end
|
180
|
-
|
181
|
-
context 'when the param is missing and include_missing=false' do
|
182
|
-
before do
|
183
|
-
subject.get('/declared') { declared(params, include_missing: false) }
|
184
|
-
end
|
185
|
-
|
186
|
-
it 'sets nested objects to be nil' do
|
187
|
-
get '/declared?first=present'
|
188
|
-
expect(last_response.status).to eq(200)
|
189
|
-
expect(JSON.parse(last_response.body)['nested']).to be_nil
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
context 'when the param is missing and include_missing=true' do
|
194
|
-
before do
|
195
|
-
subject.get('/declared') { declared(params, include_missing: true) }
|
196
|
-
end
|
197
|
-
|
198
|
-
it 'sets objects with type=Hash to be a hash' do
|
199
|
-
get '/declared?first=present'
|
200
|
-
expect(last_response.status).to eq(200)
|
201
|
-
|
202
|
-
body = JSON.parse(last_response.body)
|
203
|
-
expect(body['empty_hash']).to eq({})
|
204
|
-
expect(body['nested']).to be_a(Hash)
|
205
|
-
expect(body['nested']['empty_hash']).to eq({})
|
206
|
-
expect(body['nested']['nested_two']).to be_a(Hash)
|
207
|
-
end
|
208
|
-
|
209
|
-
it 'sets objects with type=Set to be a set' do
|
210
|
-
get '/declared?first=present'
|
211
|
-
expect(last_response.status).to eq(200)
|
212
|
-
|
213
|
-
body = JSON.parse(last_response.body)
|
214
|
-
expect(['#<Set: {}>', []]).to include(body['empty_set'])
|
215
|
-
expect(['#<Set: {}>', []]).to include(body['empty_typed_set'])
|
216
|
-
expect(['#<Set: {}>', []]).to include(body['nested']['empty_set'])
|
217
|
-
expect(['#<Set: {}>', []]).to include(body['nested']['empty_typed_set'])
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'sets objects with type=Array to be an array' do
|
221
|
-
get '/declared?first=present'
|
222
|
-
expect(last_response.status).to eq(200)
|
223
|
-
|
224
|
-
body = JSON.parse(last_response.body)
|
225
|
-
expect(body['empty_arr']).to eq([])
|
226
|
-
expect(body['empty_typed_arr']).to eq([])
|
227
|
-
expect(body['arr']).to eq([])
|
228
|
-
expect(body['nested']['empty_arr']).to eq([])
|
229
|
-
expect(body['nested']['empty_typed_arr']).to eq([])
|
230
|
-
expect(body['nested']['nested_arr']).to eq([])
|
231
|
-
end
|
232
|
-
|
233
|
-
it 'includes all declared children when type=Hash' do
|
234
|
-
get '/declared?first=present'
|
235
|
-
expect(last_response.status).to eq(200)
|
236
|
-
|
237
|
-
body = JSON.parse(last_response.body)
|
238
|
-
expect(body['nested'].keys).to eq(%w[fourth fifth nested_two nested_arr empty_arr empty_typed_arr empty_hash empty_set empty_typed_set])
|
239
|
-
expect(body['nested']['nested_two'].keys).to eq(%w[sixth nested_three])
|
240
|
-
expect(body['nested']['nested_two']['nested_three'].keys).to eq(%w[seventh])
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
it 'filters out any additional params that are given' do
|
245
|
-
subject.get '/declared' do
|
246
|
-
declared(params)
|
247
|
-
end
|
248
|
-
get '/declared?first=one&other=two'
|
249
|
-
expect(last_response.status).to eq(200)
|
250
|
-
expect(JSON.parse(last_response.body).key?(:other)).to be false
|
251
|
-
end
|
252
|
-
|
253
|
-
it 'stringifies if that option is passed' do
|
254
|
-
subject.get '/declared' do
|
255
|
-
declared(params, stringify: true)
|
256
|
-
end
|
257
|
-
|
258
|
-
get '/declared?first=one&other=two'
|
259
|
-
expect(last_response.status).to eq(200)
|
260
|
-
expect(JSON.parse(last_response.body)['first']).to eq 'one'
|
261
|
-
end
|
262
|
-
|
263
|
-
it 'does not include missing attributes if that option is passed' do
|
264
|
-
subject.get '/declared' do
|
265
|
-
error! 'expected nil', 400 if declared(params, include_missing: false).key?(:second)
|
266
|
-
''
|
267
|
-
end
|
268
|
-
|
269
|
-
get '/declared?first=one&other=two'
|
270
|
-
expect(last_response.status).to eq(200)
|
271
|
-
end
|
272
|
-
|
273
|
-
it 'does not include renamed missing attributes if that option is passed' do
|
274
|
-
subject.params do
|
275
|
-
optional :renamed_original, as: :renamed
|
276
|
-
end
|
277
|
-
subject.get '/declared' do
|
278
|
-
error! 'expected nil', 400 if declared(params, include_missing: false).key?(:renamed)
|
279
|
-
''
|
280
|
-
end
|
281
|
-
|
282
|
-
get '/declared?first=one&other=two'
|
283
|
-
expect(last_response.status).to eq(200)
|
284
|
-
end
|
285
|
-
|
286
|
-
it 'includes attributes with value that evaluates to false' do
|
287
|
-
subject.params do
|
288
|
-
requires :first
|
289
|
-
optional :boolean
|
290
|
-
end
|
291
|
-
|
292
|
-
subject.post '/declared' do
|
293
|
-
error!('expected false', 400) if declared(params, include_missing: false)[:boolean] != false
|
294
|
-
''
|
295
|
-
end
|
296
|
-
|
297
|
-
post '/declared', ::Grape::Json.dump(first: 'one', boolean: false), 'CONTENT_TYPE' => 'application/json'
|
298
|
-
expect(last_response.status).to eq(201)
|
299
|
-
end
|
300
|
-
|
301
|
-
it 'includes attributes with value that evaluates to nil' do
|
302
|
-
subject.params do
|
303
|
-
requires :first
|
304
|
-
optional :second
|
305
|
-
end
|
306
|
-
|
307
|
-
subject.post '/declared' do
|
308
|
-
error!('expected nil', 400) unless declared(params, include_missing: false)[:second].nil?
|
309
|
-
''
|
310
|
-
end
|
311
|
-
|
312
|
-
post '/declared', ::Grape::Json.dump(first: 'one', second: nil), 'CONTENT_TYPE' => 'application/json'
|
313
|
-
expect(last_response.status).to eq(201)
|
314
|
-
end
|
315
|
-
|
316
|
-
it 'includes missing attributes with defaults when there are nested hashes' do
|
317
|
-
subject.get '/dummy' do
|
318
|
-
end
|
319
|
-
|
320
|
-
subject.params do
|
321
|
-
requires :first
|
322
|
-
optional :second
|
323
|
-
optional :third, default: nil
|
324
|
-
optional :nested, type: Hash do
|
325
|
-
optional :fourth, default: nil
|
326
|
-
optional :fifth, default: nil
|
327
|
-
requires :nested_nested, type: Hash do
|
328
|
-
optional :sixth, default: 'sixth-default'
|
329
|
-
optional :seven, default: nil
|
330
|
-
end
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
subject.get '/declared' do
|
335
|
-
declared(params, include_missing: false)
|
336
|
-
end
|
337
|
-
|
338
|
-
get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'
|
339
|
-
json = JSON.parse(last_response.body)
|
340
|
-
expect(last_response.status).to eq(200)
|
341
|
-
expect(json['first']).to eq 'present'
|
342
|
-
expect(json['nested'].keys).to eq %w[fourth fifth nested_nested]
|
343
|
-
expect(json['nested']['fourth']).to eq ''
|
344
|
-
expect(json['nested']['nested_nested'].keys).to eq %w[sixth seven]
|
345
|
-
expect(json['nested']['nested_nested']['sixth']).to eq 'sixth'
|
346
|
-
end
|
347
|
-
|
348
|
-
it 'does not include missing attributes when there are nested hashes' do
|
349
|
-
subject.get '/dummy' do
|
350
|
-
end
|
351
|
-
|
352
|
-
subject.params do
|
353
|
-
requires :first
|
354
|
-
optional :second
|
355
|
-
optional :third
|
356
|
-
optional :nested, type: Hash do
|
357
|
-
optional :fourth
|
358
|
-
optional :fifth
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
subject.get '/declared' do
|
363
|
-
declared(params, include_missing: false)
|
364
|
-
end
|
365
|
-
|
366
|
-
get '/declared?first=present&nested[fourth]=4'
|
367
|
-
json = JSON.parse(last_response.body)
|
368
|
-
expect(last_response.status).to eq(200)
|
369
|
-
expect(json['first']).to eq 'present'
|
370
|
-
expect(json['nested'].keys).to eq %w[fourth]
|
371
|
-
expect(json['nested']['fourth']).to eq '4'
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
describe '#declared; call from child namespace' do
|
376
|
-
before do
|
377
|
-
subject.format :json
|
378
|
-
subject.namespace :parent do
|
379
|
-
params do
|
380
|
-
requires :parent_name, type: String
|
381
|
-
end
|
382
|
-
|
383
|
-
namespace ':parent_name' do
|
384
|
-
params do
|
385
|
-
requires :child_name, type: String
|
386
|
-
requires :child_age, type: Integer
|
387
|
-
end
|
388
|
-
|
389
|
-
namespace ':child_name' do
|
390
|
-
params do
|
391
|
-
requires :grandchild_name, type: String
|
392
|
-
end
|
393
|
-
|
394
|
-
get ':grandchild_name' do
|
395
|
-
{
|
396
|
-
'params' => params,
|
397
|
-
'without_parent_namespaces' => declared(params, include_parent_namespaces: false),
|
398
|
-
'with_parent_namespaces' => declared(params, include_parent_namespaces: true)
|
399
|
-
}
|
400
|
-
end
|
401
|
-
end
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
get '/parent/foo/bar/baz', child_age: 5, extra: 'hello'
|
406
|
-
end
|
407
|
-
|
408
|
-
let(:parsed_response) { JSON.parse(last_response.body, symbolize_names: true) }
|
409
|
-
|
410
|
-
it { expect(last_response.status).to eq 200 }
|
411
|
-
|
412
|
-
context 'with include_parent_namespaces: false' do
|
413
|
-
it 'returns declared parameters only from current namespace' do
|
414
|
-
expect(parsed_response[:without_parent_namespaces]).to eq(
|
415
|
-
grandchild_name: 'baz'
|
416
|
-
)
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
context 'with include_parent_namespaces: true' do
|
421
|
-
it 'returns declared parameters from every parent namespace' do
|
422
|
-
expect(parsed_response[:with_parent_namespaces]).to eq(
|
423
|
-
parent_name: 'foo',
|
424
|
-
child_name: 'bar',
|
425
|
-
grandchild_name: 'baz',
|
426
|
-
child_age: 5
|
427
|
-
)
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
context 'without declaration' do
|
432
|
-
it 'returns all requested parameters' do
|
433
|
-
expect(parsed_response[:params]).to eq(
|
434
|
-
parent_name: 'foo',
|
435
|
-
child_name: 'bar',
|
436
|
-
grandchild_name: 'baz',
|
437
|
-
child_age: 5,
|
438
|
-
extra: 'hello'
|
439
|
-
)
|
440
|
-
end
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
describe '#declared; from a nested mounted endpoint' do
|
445
|
-
before do
|
446
|
-
doubly_mounted = Class.new(Grape::API)
|
447
|
-
doubly_mounted.namespace :more do
|
448
|
-
params do
|
449
|
-
requires :y, type: Integer
|
450
|
-
end
|
451
|
-
route_param :y do
|
452
|
-
get do
|
453
|
-
{
|
454
|
-
params: params,
|
455
|
-
declared_params: declared(params)
|
456
|
-
}
|
457
|
-
end
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
|
-
mounted = Class.new(Grape::API)
|
462
|
-
mounted.namespace :another do
|
463
|
-
params do
|
464
|
-
requires :mount_space, type: Integer
|
465
|
-
end
|
466
|
-
route_param :mount_space do
|
467
|
-
mount doubly_mounted
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
subject.format :json
|
472
|
-
subject.namespace :something do
|
473
|
-
params do
|
474
|
-
requires :id, type: Integer
|
475
|
-
end
|
476
|
-
resource ':id' do
|
477
|
-
mount mounted
|
478
|
-
end
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
it 'can access parent attributes' do
|
483
|
-
get '/something/123/another/456/more/789'
|
484
|
-
expect(last_response.status).to eq 200
|
485
|
-
json = JSON.parse(last_response.body, symbolize_names: true)
|
486
|
-
|
487
|
-
# test all three levels of params
|
488
|
-
expect(json[:declared_params][:y]).to eq 789
|
489
|
-
expect(json[:declared_params][:mount_space]).to eq 456
|
490
|
-
expect(json[:declared_params][:id]).to eq 123
|
491
|
-
end
|
492
|
-
end
|
493
|
-
|
494
|
-
describe '#declared; mixed nesting' do
|
495
|
-
before do
|
496
|
-
subject.format :json
|
497
|
-
subject.resource :users do
|
498
|
-
route_param :id, type: Integer, desc: 'ID desc' do
|
499
|
-
# Adding this causes route_setting(:declared_params) to be nil for the
|
500
|
-
# get block in namespace 'foo' below
|
501
|
-
get do
|
502
|
-
end
|
503
|
-
|
504
|
-
namespace 'foo' do
|
505
|
-
get do
|
506
|
-
{
|
507
|
-
params: params,
|
508
|
-
declared_params: declared(params),
|
509
|
-
declared_params_no_parent: declared(params, include_parent_namespaces: false)
|
510
|
-
}
|
511
|
-
end
|
512
|
-
end
|
513
|
-
end
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
it 'can access parent route_param' do
|
518
|
-
get '/users/123/foo', bar: 'bar'
|
519
|
-
expect(last_response.status).to eq 200
|
520
|
-
json = JSON.parse(last_response.body, symbolize_names: true)
|
521
|
-
|
522
|
-
expect(json[:declared_params][:id]).to eq 123
|
523
|
-
expect(json[:declared_params_no_parent][:id]).to be_nil
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
|
-
describe '#declared; with multiple route_param' do
|
528
|
-
before do
|
529
|
-
mounted = Class.new(Grape::API)
|
530
|
-
mounted.namespace :albums do
|
531
|
-
get do
|
532
|
-
declared(params)
|
533
|
-
end
|
534
|
-
end
|
535
|
-
|
536
|
-
subject.format :json
|
537
|
-
subject.namespace :artists do
|
538
|
-
route_param :id, type: Integer do
|
539
|
-
get do
|
540
|
-
declared(params)
|
541
|
-
end
|
542
|
-
|
543
|
-
params do
|
544
|
-
requires :filter, type: String
|
545
|
-
end
|
546
|
-
get :some_route do
|
547
|
-
declared(params)
|
548
|
-
end
|
549
|
-
end
|
550
|
-
|
551
|
-
route_param :artist_id, type: Integer do
|
552
|
-
namespace :compositions do
|
553
|
-
get do
|
554
|
-
declared(params)
|
555
|
-
end
|
556
|
-
end
|
557
|
-
end
|
558
|
-
|
559
|
-
route_param :compositor_id, type: Integer do
|
560
|
-
mount mounted
|
561
|
-
end
|
562
|
-
end
|
563
|
-
end
|
564
|
-
|
565
|
-
it 'return only :id without :artist_id' do
|
566
|
-
get '/artists/1'
|
567
|
-
json = JSON.parse(last_response.body, symbolize_names: true)
|
568
|
-
|
569
|
-
expect(json).to be_key(:id)
|
570
|
-
expect(json).not_to be_key(:artist_id)
|
571
|
-
end
|
572
|
-
|
573
|
-
it 'return only :artist_id without :id' do
|
574
|
-
get '/artists/1/compositions'
|
575
|
-
json = JSON.parse(last_response.body, symbolize_names: true)
|
576
|
-
|
577
|
-
expect(json).to be_key(:artist_id)
|
578
|
-
expect(json).not_to be_key(:id)
|
579
|
-
end
|
580
|
-
|
581
|
-
it 'return :filter and :id parameters in declared for second enpoint inside route_param' do
|
582
|
-
get '/artists/1/some_route', filter: 'some_filter'
|
583
|
-
json = JSON.parse(last_response.body, symbolize_names: true)
|
584
|
-
|
585
|
-
expect(json).to be_key(:filter)
|
586
|
-
expect(json).to be_key(:id)
|
587
|
-
expect(json).not_to be_key(:artist_id)
|
588
|
-
end
|
589
|
-
|
590
|
-
it 'return :compositor_id for mounter in route_param' do
|
591
|
-
get '/artists/1/albums'
|
592
|
-
json = JSON.parse(last_response.body, symbolize_names: true)
|
593
|
-
|
594
|
-
expect(json).to be_key(:compositor_id)
|
595
|
-
expect(json).not_to be_key(:id)
|
596
|
-
expect(json).not_to be_key(:artist_id)
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
|
-
describe 'parameter renaming' do
|
601
|
-
context 'with a deeply nested parameter structure' do
|
602
|
-
let(:params) do
|
603
|
-
{
|
604
|
-
i_a: 'a',
|
605
|
-
i_b: {
|
606
|
-
i_c: 'c',
|
607
|
-
i_d: {
|
608
|
-
i_e: {
|
609
|
-
i_f: 'f',
|
610
|
-
i_g: 'g',
|
611
|
-
i_h: [
|
612
|
-
{
|
613
|
-
i_ha: 'ha1',
|
614
|
-
i_hb: {
|
615
|
-
i_hc: 'c'
|
616
|
-
}
|
617
|
-
},
|
618
|
-
{
|
619
|
-
i_ha: 'ha2',
|
620
|
-
i_hb: {
|
621
|
-
i_hc: 'c'
|
622
|
-
}
|
623
|
-
}
|
624
|
-
]
|
625
|
-
}
|
626
|
-
}
|
627
|
-
}
|
628
|
-
}
|
629
|
-
end
|
630
|
-
let(:declared) do
|
631
|
-
{
|
632
|
-
o_a: 'a',
|
633
|
-
o_b: {
|
634
|
-
o_c: 'c',
|
635
|
-
o_d: {
|
636
|
-
o_e: {
|
637
|
-
o_f: 'f',
|
638
|
-
o_g: 'g',
|
639
|
-
o_h: [
|
640
|
-
{
|
641
|
-
o_ha: 'ha1',
|
642
|
-
o_hb: {
|
643
|
-
o_hc: 'c'
|
644
|
-
}
|
645
|
-
},
|
646
|
-
{
|
647
|
-
o_ha: 'ha2',
|
648
|
-
o_hb: {
|
649
|
-
o_hc: 'c'
|
650
|
-
}
|
651
|
-
}
|
652
|
-
]
|
653
|
-
}
|
654
|
-
}
|
655
|
-
}
|
656
|
-
}
|
657
|
-
end
|
658
|
-
let(:params_keys) do
|
659
|
-
[
|
660
|
-
'i_a',
|
661
|
-
'i_b',
|
662
|
-
'i_b[i_c]',
|
663
|
-
'i_b[i_d]',
|
664
|
-
'i_b[i_d][i_e]',
|
665
|
-
'i_b[i_d][i_e][i_f]',
|
666
|
-
'i_b[i_d][i_e][i_g]',
|
667
|
-
'i_b[i_d][i_e][i_h]',
|
668
|
-
'i_b[i_d][i_e][i_h][i_ha]',
|
669
|
-
'i_b[i_d][i_e][i_h][i_hb]',
|
670
|
-
'i_b[i_d][i_e][i_h][i_hb][i_hc]'
|
671
|
-
]
|
672
|
-
end
|
673
|
-
|
674
|
-
before do
|
675
|
-
subject.format :json
|
676
|
-
subject.params do
|
677
|
-
optional :i_a, type: String, as: :o_a
|
678
|
-
optional :i_b, type: Hash, as: :o_b do
|
679
|
-
optional :i_c, type: String, as: :o_c
|
680
|
-
optional :i_d, type: Hash, as: :o_d do
|
681
|
-
optional :i_e, type: Hash, as: :o_e do
|
682
|
-
optional :i_f, type: String, as: :o_f
|
683
|
-
optional :i_g, type: String, as: :o_g
|
684
|
-
optional :i_h, type: Array, as: :o_h do
|
685
|
-
optional :i_ha, type: String, as: :o_ha
|
686
|
-
optional :i_hb, type: Hash, as: :o_hb do
|
687
|
-
optional :i_hc, type: String, as: :o_hc
|
688
|
-
end
|
689
|
-
end
|
690
|
-
end
|
691
|
-
end
|
692
|
-
end
|
693
|
-
end
|
694
|
-
subject.post '/test' do
|
695
|
-
declared(params, include_missing: false)
|
696
|
-
end
|
697
|
-
subject.post '/test/no-mod' do
|
698
|
-
before = params.to_h
|
699
|
-
declared(params, include_missing: false)
|
700
|
-
after = params.to_h
|
701
|
-
{ before: before, after: after }
|
702
|
-
end
|
703
|
-
end
|
704
|
-
|
705
|
-
it 'generates the correct parameter names for documentation' do
|
706
|
-
expect(subject.routes.first.params.keys).to match(params_keys)
|
707
|
-
end
|
708
|
-
|
709
|
-
it 'maps the renamed parameter correctly' do
|
710
|
-
post '/test', **params
|
711
|
-
expect(JSON.parse(last_response.body, symbolize_names: true)).to \
|
712
|
-
match(declared)
|
713
|
-
end
|
714
|
-
|
715
|
-
it 'maps no parameters when none are given' do
|
716
|
-
post '/test'
|
717
|
-
expect(JSON.parse(last_response.body)).to match({})
|
718
|
-
end
|
719
|
-
|
720
|
-
it 'does not modify the request params' do
|
721
|
-
post '/test/no-mod', **params
|
722
|
-
result = JSON.parse(last_response.body, symbolize_names: true)
|
723
|
-
expect(result[:before]).to match(result[:after])
|
724
|
-
end
|
725
|
-
end
|
726
|
-
|
727
|
-
context 'with a renamed root parameter' do
|
728
|
-
before do
|
729
|
-
subject.format :json
|
730
|
-
subject.params do
|
731
|
-
optional :email_address, type: String, regexp: /.+@.+/, as: :email
|
732
|
-
end
|
733
|
-
subject.post '/test' do
|
734
|
-
declared(params, include_missing: false)
|
735
|
-
end
|
736
|
-
end
|
737
|
-
|
738
|
-
it 'generates the correct parameter names for documentation' do
|
739
|
-
expect(subject.routes.first.params.keys).to match(%w[email_address])
|
740
|
-
end
|
741
|
-
|
742
|
-
it 'maps the renamed parameter correctly (original name)' do
|
743
|
-
post '/test', email_address: 'test@example.com'
|
744
|
-
expect(JSON.parse(last_response.body)).to \
|
745
|
-
match('email' => 'test@example.com')
|
746
|
-
end
|
747
|
-
|
748
|
-
it 'validates the renamed parameter correctly (original name)' do
|
749
|
-
post '/test', email_address: 'bad[at]example.com'
|
750
|
-
expect(JSON.parse(last_response.body)).to \
|
751
|
-
match('error' => 'email_address is invalid')
|
752
|
-
end
|
753
|
-
|
754
|
-
it 'ignores the renamed parameter (as name)' do
|
755
|
-
post '/test', email: 'test@example.com'
|
756
|
-
expect(JSON.parse(last_response.body)).to match({})
|
757
|
-
end
|
758
|
-
end
|
759
|
-
|
760
|
-
context 'with a renamed hash with nested parameters' do
|
761
|
-
before do
|
762
|
-
subject.format :json
|
763
|
-
subject.params do
|
764
|
-
optional :address, type: Hash, as: :address_attributes do
|
765
|
-
optional :street, type: String, values: ['Street 1', 'Street 2'],
|
766
|
-
default: 'Street 1'
|
767
|
-
optional :city, type: String
|
768
|
-
end
|
769
|
-
end
|
770
|
-
subject.post '/test' do
|
771
|
-
declared(params, include_missing: false)
|
772
|
-
end
|
773
|
-
end
|
774
|
-
|
775
|
-
it 'generates the correct parameter names for documentation' do
|
776
|
-
expect(subject.routes.first.params.keys).to \
|
777
|
-
match(%w[address address[street] address[city]])
|
778
|
-
end
|
779
|
-
|
780
|
-
it 'maps the renamed parameter correctly (original name)' do
|
781
|
-
post '/test', address: { city: 'Berlin', street: 'Street 2', t: 't' }
|
782
|
-
expect(JSON.parse(last_response.body)).to \
|
783
|
-
match('address_attributes' => { 'city' => 'Berlin',
|
784
|
-
'street' => 'Street 2' })
|
785
|
-
end
|
786
|
-
|
787
|
-
it 'validates the renamed parameter correctly (original name)' do
|
788
|
-
post '/test', address: { street: 'unknown' }
|
789
|
-
expect(JSON.parse(last_response.body)).to \
|
790
|
-
match('error' => 'address[street] does not have a valid value')
|
791
|
-
end
|
792
|
-
|
793
|
-
it 'ignores the renamed parameter (as name)' do
|
794
|
-
post '/test', address_attributes: { city: 'Berlin', unknown: '1' }
|
795
|
-
expect(JSON.parse(last_response.body)).to match({})
|
796
|
-
end
|
797
|
-
end
|
798
|
-
|
799
|
-
context 'with a renamed hash with nested renamed parameter' do
|
800
|
-
before do
|
801
|
-
subject.format :json
|
802
|
-
subject.params do
|
803
|
-
optional :user, type: Hash, as: :user_attributes do
|
804
|
-
optional :email_address, type: String, regexp: /.+@.+/, as: :email
|
805
|
-
end
|
806
|
-
end
|
807
|
-
subject.post '/test' do
|
808
|
-
declared(params, include_missing: false)
|
809
|
-
end
|
810
|
-
end
|
811
|
-
|
812
|
-
it 'generates the correct parameter names for documentation' do
|
813
|
-
expect(subject.routes.first.params.keys).to \
|
814
|
-
match(%w[user user[email_address]])
|
815
|
-
end
|
816
|
-
|
817
|
-
it 'maps the renamed parameter correctly (original name)' do
|
818
|
-
post '/test', user: { email_address: 'test@example.com' }
|
819
|
-
expect(JSON.parse(last_response.body)).to \
|
820
|
-
match('user_attributes' => { 'email' => 'test@example.com' })
|
821
|
-
end
|
822
|
-
|
823
|
-
it 'validates the renamed parameter correctly (original name)' do
|
824
|
-
post '/test', user: { email_address: 'bad[at]example.com' }
|
825
|
-
expect(JSON.parse(last_response.body)).to \
|
826
|
-
match('error' => 'user[email_address] is invalid')
|
827
|
-
end
|
828
|
-
|
829
|
-
it 'ignores the renamed parameter (as name, 1)' do
|
830
|
-
post '/test', user: { email: 'test@example.com' }
|
831
|
-
expect(JSON.parse(last_response.body)).to \
|
832
|
-
match({ 'user_attributes' => {} })
|
833
|
-
end
|
834
|
-
|
835
|
-
it 'ignores the renamed parameter (as name, 2)' do
|
836
|
-
post '/test', user_attributes: { email_address: 'test@example.com' }
|
837
|
-
expect(JSON.parse(last_response.body)).to match({})
|
838
|
-
end
|
839
|
-
|
840
|
-
it 'ignores the renamed parameter (as name, 3)' do
|
841
|
-
post '/test', user_attributes: { email: 'test@example.com' }
|
842
|
-
expect(JSON.parse(last_response.body)).to match({})
|
843
|
-
end
|
844
|
-
end
|
845
|
-
end
|
846
|
-
end
|