grape 1.7.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +30 -25
  5. data/UPGRADING.md +35 -0
  6. data/grape.gemspec +3 -6
  7. data/lib/grape/api.rb +2 -2
  8. data/lib/grape/content_types.rb +2 -8
  9. data/lib/grape/dsl/desc.rb +1 -1
  10. data/lib/grape/dsl/inside_route.rb +11 -11
  11. data/lib/grape/dsl/request_response.rb +2 -1
  12. data/lib/grape/dsl/settings.rb +2 -6
  13. data/lib/grape/endpoint.rb +28 -18
  14. data/lib/grape/error_formatter/base.rb +1 -1
  15. data/lib/grape/exceptions/base.rb +2 -2
  16. data/lib/grape/exceptions/missing_group_type.rb +1 -6
  17. data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
  18. data/lib/grape/exceptions/validation_errors.rb +1 -6
  19. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
  20. data/lib/grape/extensions/hash.rb +4 -7
  21. data/lib/grape/extensions/hashie/mash.rb +3 -3
  22. data/lib/grape/formatter/serializable_hash.rb +7 -7
  23. data/lib/grape/http/headers.rb +12 -2
  24. data/lib/grape/middleware/auth/base.rb +1 -1
  25. data/lib/grape/middleware/auth/strategies.rb +1 -2
  26. data/lib/grape/middleware/error.rb +5 -5
  27. data/lib/grape/middleware/formatter.rb +6 -6
  28. data/lib/grape/middleware/versioner/header.rb +11 -19
  29. data/lib/grape/railtie.rb +9 -0
  30. data/lib/grape/request.rb +8 -2
  31. data/lib/grape/router/route.rb +1 -3
  32. data/lib/grape/util/lazy_value.rb +3 -11
  33. data/lib/grape/util/strict_hash_configuration.rb +3 -4
  34. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  35. data/lib/grape/validations/params_scope.rb +8 -2
  36. data/lib/grape/validations/single_attribute_iterator.rb +3 -1
  37. data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
  38. data/lib/grape/validations/validators/base.rb +9 -20
  39. data/lib/grape/validations/validators/default_validator.rb +2 -20
  40. data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
  41. data/lib/grape/validations/validators/values_validator.rb +14 -5
  42. data/lib/grape/version.rb +1 -1
  43. data/lib/grape.rb +26 -5
  44. metadata +13 -253
  45. data/lib/grape/config.rb +0 -34
  46. data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
  47. data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
  48. data/spec/grape/api/custom_validations_spec.rb +0 -256
  49. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  50. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  51. data/spec/grape/api/documentation_spec.rb +0 -59
  52. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  53. data/spec/grape/api/instance_spec.rb +0 -103
  54. data/spec/grape/api/invalid_format_spec.rb +0 -45
  55. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  56. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  57. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  58. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  59. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  60. data/spec/grape/api/recognize_path_spec.rb +0 -21
  61. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  62. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  63. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  64. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  65. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  66. data/spec/grape/api_remount_spec.rb +0 -473
  67. data/spec/grape/api_spec.rb +0 -4347
  68. data/spec/grape/config_spec.rb +0 -17
  69. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  70. data/spec/grape/dsl/desc_spec.rb +0 -101
  71. data/spec/grape/dsl/headers_spec.rb +0 -62
  72. data/spec/grape/dsl/helpers_spec.rb +0 -100
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -535
  74. data/spec/grape/dsl/logger_spec.rb +0 -24
  75. data/spec/grape/dsl/middleware_spec.rb +0 -60
  76. data/spec/grape/dsl/parameters_spec.rb +0 -180
  77. data/spec/grape/dsl/request_response_spec.rb +0 -206
  78. data/spec/grape/dsl/routing_spec.rb +0 -275
  79. data/spec/grape/dsl/settings_spec.rb +0 -261
  80. data/spec/grape/dsl/validations_spec.rb +0 -55
  81. data/spec/grape/endpoint/declared_spec.rb +0 -846
  82. data/spec/grape/endpoint_spec.rb +0 -1085
  83. data/spec/grape/entity_spec.rb +0 -336
  84. data/spec/grape/exceptions/base_spec.rb +0 -81
  85. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
  86. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  87. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  88. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  89. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  90. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
  91. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  92. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  93. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  94. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  95. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
  96. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  97. data/spec/grape/exceptions/validation_spec.rb +0 -19
  98. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  99. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  100. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  101. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  102. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  103. data/spec/grape/integration/rack_spec.rb +0 -51
  104. data/spec/grape/loading_spec.rb +0 -44
  105. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  106. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  107. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  108. data/spec/grape/middleware/base_spec.rb +0 -221
  109. data/spec/grape/middleware/error_spec.rb +0 -85
  110. data/spec/grape/middleware/exception_spec.rb +0 -294
  111. data/spec/grape/middleware/formatter_spec.rb +0 -461
  112. data/spec/grape/middleware/globals_spec.rb +0 -30
  113. data/spec/grape/middleware/stack_spec.rb +0 -155
  114. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  115. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  116. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  117. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  118. data/spec/grape/middleware/versioner_spec.rb +0 -21
  119. data/spec/grape/named_api_spec.rb +0 -19
  120. data/spec/grape/parser_spec.rb +0 -86
  121. data/spec/grape/path_spec.rb +0 -252
  122. data/spec/grape/presenters/presenter_spec.rb +0 -71
  123. data/spec/grape/request_spec.rb +0 -136
  124. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  125. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  126. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  127. data/spec/grape/util/stackable_values_spec.rb +0 -128
  128. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  129. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  130. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  131. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
  132. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  133. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
  134. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  135. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  136. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  137. data/spec/grape/validations/types_spec.rb +0 -111
  138. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  139. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  140. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  141. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  142. data/spec/grape/validations/validators/default_spec.rb +0 -463
  143. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  144. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  145. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  146. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  147. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  148. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  149. data/spec/grape/validations/validators/values_spec.rb +0 -696
  150. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  151. data/spec/grape/validations_spec.rb +0 -2029
  152. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  153. data/spec/integration/multi_json/json_spec.rb +0 -7
  154. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  155. data/spec/shared/versioning_examples.rb +0 -215
  156. data/spec/spec_helper.rb +0 -52
  157. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  158. data/spec/support/chunks.rb +0 -14
  159. data/spec/support/content_type_helpers.rb +0 -15
  160. data/spec/support/endpoint_faker.rb +0 -25
  161. data/spec/support/file_streamer.rb +0 -13
  162. data/spec/support/integer_helpers.rb +0 -13
  163. 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