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.
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