grape 1.8.0 → 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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/README.md +19 -22
  4. data/UPGRADING.md +35 -0
  5. data/grape.gemspec +1 -4
  6. data/lib/grape/dsl/desc.rb +1 -1
  7. data/lib/grape/dsl/inside_route.rb +9 -9
  8. data/lib/grape/endpoint.rb +9 -1
  9. data/lib/grape/exceptions/missing_group_type.rb +1 -1
  10. data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
  11. data/lib/grape/http/headers.rb +12 -2
  12. data/lib/grape/middleware/auth/strategies.rb +1 -2
  13. data/lib/grape/middleware/error.rb +4 -4
  14. data/lib/grape/middleware/formatter.rb +5 -5
  15. data/lib/grape/railtie.rb +9 -0
  16. data/lib/grape/request.rb +8 -2
  17. data/lib/grape/router/route.rb +1 -1
  18. data/lib/grape/validations/validators/base.rb +1 -1
  19. data/lib/grape/validations/validators/values_validator.rb +2 -2
  20. data/lib/grape/version.rb +1 -1
  21. data/lib/grape.rb +15 -2
  22. metadata +8 -243
  23. data/spec/grape/api/custom_validations_spec.rb +0 -213
  24. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  25. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  26. data/spec/grape/api/documentation_spec.rb +0 -59
  27. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  28. data/spec/grape/api/instance_spec.rb +0 -103
  29. data/spec/grape/api/invalid_format_spec.rb +0 -45
  30. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  31. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  32. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  33. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  34. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  35. data/spec/grape/api/recognize_path_spec.rb +0 -21
  36. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  37. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  38. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  39. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  40. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  41. data/spec/grape/api_remount_spec.rb +0 -509
  42. data/spec/grape/api_spec.rb +0 -4356
  43. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  44. data/spec/grape/dsl/desc_spec.rb +0 -98
  45. data/spec/grape/dsl/headers_spec.rb +0 -62
  46. data/spec/grape/dsl/helpers_spec.rb +0 -100
  47. data/spec/grape/dsl/inside_route_spec.rb +0 -531
  48. data/spec/grape/dsl/logger_spec.rb +0 -24
  49. data/spec/grape/dsl/middleware_spec.rb +0 -60
  50. data/spec/grape/dsl/parameters_spec.rb +0 -180
  51. data/spec/grape/dsl/request_response_spec.rb +0 -225
  52. data/spec/grape/dsl/routing_spec.rb +0 -275
  53. data/spec/grape/dsl/settings_spec.rb +0 -261
  54. data/spec/grape/dsl/validations_spec.rb +0 -55
  55. data/spec/grape/endpoint/declared_spec.rb +0 -846
  56. data/spec/grape/endpoint_spec.rb +0 -1085
  57. data/spec/grape/entity_spec.rb +0 -336
  58. data/spec/grape/exceptions/base_spec.rb +0 -81
  59. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
  60. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  61. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  62. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  63. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  64. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
  65. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  66. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  67. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  68. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  69. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
  70. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  71. data/spec/grape/exceptions/validation_spec.rb +0 -19
  72. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  73. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  74. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  75. data/spec/grape/grape_spec.rb +0 -9
  76. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  77. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  78. data/spec/grape/integration/rack_spec.rb +0 -51
  79. data/spec/grape/loading_spec.rb +0 -44
  80. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  81. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  82. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  83. data/spec/grape/middleware/base_spec.rb +0 -221
  84. data/spec/grape/middleware/error_spec.rb +0 -85
  85. data/spec/grape/middleware/exception_spec.rb +0 -294
  86. data/spec/grape/middleware/formatter_spec.rb +0 -461
  87. data/spec/grape/middleware/globals_spec.rb +0 -30
  88. data/spec/grape/middleware/stack_spec.rb +0 -155
  89. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  90. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  91. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  92. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  93. data/spec/grape/middleware/versioner_spec.rb +0 -21
  94. data/spec/grape/named_api_spec.rb +0 -19
  95. data/spec/grape/parser_spec.rb +0 -86
  96. data/spec/grape/path_spec.rb +0 -252
  97. data/spec/grape/presenters/presenter_spec.rb +0 -71
  98. data/spec/grape/request_spec.rb +0 -126
  99. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  100. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  101. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  102. data/spec/grape/util/stackable_values_spec.rb +0 -128
  103. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  104. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  105. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  106. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
  107. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  108. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
  109. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  110. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  111. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  112. data/spec/grape/validations/types_spec.rb +0 -111
  113. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  114. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  115. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  116. data/spec/grape/validations/validators/base_spec.rb +0 -38
  117. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  118. data/spec/grape/validations/validators/default_spec.rb +0 -463
  119. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  120. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  121. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  122. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  123. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  124. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  125. data/spec/grape/validations/validators/values_spec.rb +0 -733
  126. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  127. data/spec/grape/validations_spec.rb +0 -2030
  128. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  129. data/spec/integration/multi_json/json_spec.rb +0 -7
  130. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  131. data/spec/shared/deprecated_class_examples.rb +0 -16
  132. data/spec/shared/versioning_examples.rb +0 -215
  133. data/spec/spec_helper.rb +0 -52
  134. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  135. data/spec/support/chunks.rb +0 -14
  136. data/spec/support/content_type_helpers.rb +0 -15
  137. data/spec/support/endpoint_faker.rb +0 -25
  138. data/spec/support/file_streamer.rb +0 -13
  139. data/spec/support/integer_helpers.rb +0 -13
  140. data/spec/support/versioned_helpers.rb +0 -55
@@ -1,463 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Validators::DefaultValidator do
4
- let_it_be(:app) do
5
- Class.new(Grape::API) do
6
- default_format :json
7
-
8
- params do
9
- optional :id
10
- optional :type, default: 'default-type'
11
- end
12
- get '/' do
13
- { id: params[:id], type: params[:type] }
14
- end
15
-
16
- params do
17
- optional :type1, default: 'default-type1'
18
- optional :type2, default: 'default-type2'
19
- end
20
- get '/user' do
21
- { type1: params[:type1], type2: params[:type2] }
22
- end
23
-
24
- params do
25
- requires :id
26
- optional :type1, default: 'default-type1'
27
- optional :type2, default: 'default-type2'
28
- end
29
-
30
- get '/message' do
31
- { id: params[:id], type1: params[:type1], type2: params[:type2] }
32
- end
33
-
34
- params do
35
- optional :random, default: -> { Random.rand }
36
- optional :not_random, default: Random.rand
37
- end
38
- get '/numbers' do
39
- { random_number: params[:random], non_random_number: params[:non_random_number] }
40
- end
41
-
42
- params do
43
- optional :array, type: Array do
44
- requires :name
45
- optional :with_default, default: 'default'
46
- end
47
- end
48
- get '/array' do
49
- { array: params[:array] }
50
- end
51
-
52
- params do
53
- requires :thing1
54
- optional :more_things, type: Array do
55
- requires :nested_thing
56
- requires :other_thing, default: 1
57
- end
58
- end
59
- get '/optional_array' do
60
- { thing1: params[:thing1] }
61
- end
62
-
63
- params do
64
- requires :root, type: Hash do
65
- optional :some_things, type: Array do
66
- requires :foo
67
- optional :options, type: Array do
68
- requires :name, type: String
69
- requires :value, type: String
70
- end
71
- end
72
- end
73
- end
74
- get '/nested_optional_array' do
75
- { root: params[:root] }
76
- end
77
-
78
- params do
79
- requires :root, type: Hash do
80
- optional :some_things, type: Array do
81
- requires :foo
82
- optional :options, type: Array do
83
- optional :name, type: String
84
- optional :value, type: String
85
- end
86
- end
87
- end
88
- end
89
- get '/another_nested_optional_array' do
90
- { root: params[:root] }
91
- end
92
- end
93
- end
94
-
95
- it 'lets you leave required values nested inside an optional blank' do
96
- get '/optional_array', thing1: 'stuff'
97
- expect(last_response.status).to eq(200)
98
- expect(last_response.body).to eq({ thing1: 'stuff' }.to_json)
99
- end
100
-
101
- it 'allows optional arrays to be omitted' do
102
- params = { some_things:
103
- [{ foo: 'one', options: [{ name: 'wat', value: 'nope' }] },
104
- { foo: 'two' },
105
- { foo: 'three', options: [{ name: 'wooop', value: 'yap' }] }] }
106
- get '/nested_optional_array', root: params
107
- expect(last_response.status).to eq(200)
108
- expect(last_response.body).to eq({ root: params }.to_json)
109
- end
110
-
111
- it 'does not allows faulty optional arrays' do
112
- params = { some_things:
113
- [
114
- { foo: 'one', options: [{ name: 'wat', value: 'nope' }] },
115
- { foo: 'two', options: [{ name: 'wat' }] },
116
- { foo: 'three' }
117
- ] }
118
- error = { error: 'root[some_things][1][options][0][value] is missing' }
119
- get '/nested_optional_array', root: params
120
- expect(last_response.status).to eq(400)
121
- expect(last_response.body).to eq(error.to_json)
122
- end
123
-
124
- it 'allows optional arrays with optional params' do
125
- params = { some_things:
126
- [
127
- { foo: 'one', options: [{ value: 'nope' }] },
128
- { foo: 'two', options: [{ name: 'wat' }] },
129
- { foo: 'three' }
130
- ] }
131
- get '/another_nested_optional_array', root: params
132
- expect(last_response.status).to eq(200)
133
- expect(last_response.body).to eq({ root: params }.to_json)
134
- end
135
-
136
- it 'set default value for optional param' do
137
- get('/')
138
- expect(last_response.status).to eq(200)
139
- expect(last_response.body).to eq({ id: nil, type: 'default-type' }.to_json)
140
- end
141
-
142
- it 'set default values for optional params' do
143
- get('/user')
144
- expect(last_response.status).to eq(200)
145
- expect(last_response.body).to eq({ type1: 'default-type1', type2: 'default-type2' }.to_json)
146
- end
147
-
148
- it 'set default values for missing params in the request' do
149
- get('/user?type2=value2')
150
- expect(last_response.status).to eq(200)
151
- expect(last_response.body).to eq({ type1: 'default-type1', type2: 'value2' }.to_json)
152
- end
153
-
154
- it 'set default values for optional params and allow to use required fields in the same time' do
155
- get('/message?id=1')
156
- expect(last_response.status).to eq(200)
157
- expect(last_response.body).to eq({ id: '1', type1: 'default-type1', type2: 'default-type2' }.to_json)
158
- end
159
-
160
- it 'sets lambda based defaults at the time of call' do
161
- get('/numbers')
162
- expect(last_response.status).to eq(200)
163
- before = JSON.parse(last_response.body)
164
- get('/numbers')
165
- expect(last_response.status).to eq(200)
166
- after = JSON.parse(last_response.body)
167
-
168
- expect(before['non_random_number']).to eq(after['non_random_number'])
169
- expect(before['random_number']).not_to eq(after['random_number'])
170
- end
171
-
172
- it 'sets default values for grouped arrays' do
173
- get('/array?array[][name]=name&array[][name]=name2&array[][with_default]=bar2')
174
- expect(last_response.status).to eq(200)
175
- expect(last_response.body).to eq({ array: [{ name: 'name', with_default: 'default' }, { name: 'name2', with_default: 'bar2' }] }.to_json)
176
- end
177
-
178
- context 'optional group with defaults' do
179
- subject do
180
- Class.new(Grape::API) do
181
- default_format :json
182
- end
183
- end
184
-
185
- def app
186
- subject
187
- end
188
-
189
- context 'optional array without default value includes optional param with default value' do
190
- before do
191
- subject.params do
192
- optional :optional_array, type: Array do
193
- optional :foo_in_optional_array, default: 'bar'
194
- end
195
- end
196
- subject.post '/optional_array' do
197
- { optional_array: params[:optional_array] }
198
- end
199
- end
200
-
201
- it 'returns nil for optional array if param is not provided' do
202
- post '/optional_array'
203
- expect(last_response.status).to eq(201)
204
- expect(last_response.body).to eq({ optional_array: nil }.to_json)
205
- end
206
- end
207
-
208
- context 'optional array with default value includes optional param with default value' do
209
- before do
210
- subject.params do
211
- optional :optional_array_with_default, type: Array, default: [] do
212
- optional :foo_in_optional_array, default: 'bar'
213
- end
214
- end
215
- subject.post '/optional_array_with_default' do
216
- { optional_array_with_default: params[:optional_array_with_default] }
217
- end
218
- end
219
-
220
- it 'sets default value for optional array if param is not provided' do
221
- post '/optional_array_with_default'
222
- expect(last_response.status).to eq(201)
223
- expect(last_response.body).to eq({ optional_array_with_default: [] }.to_json)
224
- end
225
- end
226
-
227
- context 'optional hash without default value includes optional param with default value' do
228
- before do
229
- subject.params do
230
- optional :optional_hash_without_default, type: Hash do
231
- optional :foo_in_optional_hash, default: 'bar'
232
- end
233
- end
234
- subject.post '/optional_hash_without_default' do
235
- { optional_hash_without_default: params[:optional_hash_without_default] }
236
- end
237
- end
238
-
239
- it 'returns nil for optional hash if param is not provided' do
240
- post '/optional_hash_without_default'
241
- expect(last_response.status).to eq(201)
242
- expect(last_response.body).to eq({ optional_hash_without_default: nil }.to_json)
243
- end
244
-
245
- it 'does not fail even if invalid params is passed to default validator' do
246
- expect { post '/optional_hash_without_default', optional_hash_without_default: '5678' }.not_to raise_error
247
- expect(last_response.status).to eq(400)
248
- expect(last_response.body).to eq({ error: 'optional_hash_without_default is invalid' }.to_json)
249
- end
250
- end
251
-
252
- context 'optional hash with default value includes optional param with default value' do
253
- before do
254
- subject.params do
255
- optional :optional_hash_with_default, type: Hash, default: {} do
256
- optional :foo_in_optional_hash, default: 'bar'
257
- end
258
- end
259
- subject.post '/optional_hash_with_default_empty_hash' do
260
- { optional_hash_with_default: params[:optional_hash_with_default] }
261
- end
262
-
263
- subject.params do
264
- optional :optional_hash_with_default, type: Hash, default: { foo_in_optional_hash: 'parent_default' } do
265
- optional :some_param
266
- optional :foo_in_optional_hash, default: 'own_default'
267
- end
268
- end
269
- subject.post '/optional_hash_with_default_inner_params' do
270
- { foo_in_optional_hash: params[:optional_hash_with_default][:foo_in_optional_hash] }
271
- end
272
- end
273
-
274
- it 'sets default value for optional hash if param is not provided' do
275
- post '/optional_hash_with_default_empty_hash'
276
- expect(last_response.status).to eq(201)
277
- expect(last_response.body).to eq({ optional_hash_with_default: {} }.to_json)
278
- end
279
-
280
- it 'sets default value from parent defaults for inner param if parent param is not provided' do
281
- post '/optional_hash_with_default_inner_params'
282
- expect(last_response.status).to eq(201)
283
- expect(last_response.body).to eq({ foo_in_optional_hash: 'parent_default' }.to_json)
284
- end
285
-
286
- it 'sets own default value for inner param if parent param is provided' do
287
- post '/optional_hash_with_default_inner_params', optional_hash_with_default: { some_param: 'param' }
288
- expect(last_response.status).to eq(201)
289
- expect(last_response.body).to eq({ foo_in_optional_hash: 'own_default' }.to_json)
290
- end
291
- end
292
- end
293
-
294
- context 'optional with nil as value' do
295
- subject do
296
- Class.new(Grape::API) do
297
- default_format :json
298
- end
299
- end
300
-
301
- def app
302
- subject
303
- end
304
-
305
- context 'primitive types' do
306
- [
307
- [Integer, 0],
308
- [Integer, 42],
309
- [Float, 0.0],
310
- [Float, 4.2],
311
- [BigDecimal, 0.0],
312
- [BigDecimal, 4.2],
313
- [Numeric, 0],
314
- [Numeric, 42],
315
- [Date, Date.today],
316
- [DateTime, DateTime.now],
317
- [Time, Time.now],
318
- [Time, Time.at(0)],
319
- [Grape::API::Boolean, false],
320
- [String, ''],
321
- [String, 'non-empty-string'],
322
- [Symbol, :symbol],
323
- [TrueClass, true],
324
- [FalseClass, false]
325
- ].each do |type, default|
326
- it 'respects the default value' do
327
- subject.params do
328
- optional :param, type: type, default: default
329
- end
330
- subject.get '/default_value' do
331
- params[:param]
332
- end
333
-
334
- get '/default_value', param: nil
335
- expect(last_response.status).to eq(200)
336
- expect(last_response.body).to eq(default.to_json)
337
- end
338
- end
339
- end
340
-
341
- context 'structures types' do
342
- [
343
- [Hash, {}],
344
- [Hash, { test: 'non-empty' }],
345
- [Array, []],
346
- [Array, ['non-empty']],
347
- [Array[Integer], []],
348
- [Set, []],
349
- [Set, [1]]
350
- ].each do |type, default|
351
- it 'respects the default value' do
352
- subject.params do
353
- optional :param, type: type, default: default
354
- end
355
- subject.get '/default_value' do
356
- params[:param]
357
- end
358
-
359
- get '/default_value', param: nil
360
- expect(last_response.status).to eq(200)
361
- expect(last_response.body).to eq(default.to_json)
362
- end
363
- end
364
- end
365
-
366
- context 'special types' do
367
- [
368
- [JSON, ''],
369
- [JSON, { test: 'non-empty-string' }.to_json],
370
- [Array[JSON], []],
371
- [Array[JSON], [{ test: 'non-empty-string' }.to_json]],
372
- [::File, ''],
373
- [::File, { test: 'non-empty-string' }.to_json],
374
- [Rack::Multipart::UploadedFile, ''],
375
- [Rack::Multipart::UploadedFile, { test: 'non-empty-string' }.to_json]
376
- ].each do |type, default|
377
- it 'respects the default value' do
378
- subject.params do
379
- optional :param, type: type, default: default
380
- end
381
- subject.get '/default_value' do
382
- params[:param]
383
- end
384
-
385
- get '/default_value', param: nil
386
- expect(last_response.status).to eq(200)
387
- expect(last_response.body).to eq(default.to_json)
388
- end
389
- end
390
- end
391
-
392
- context 'variant-member-type collections' do
393
- [
394
- [Array[Integer, String], [0, '']],
395
- [Array[Integer, String], [42, 'non-empty-string']],
396
- [[Integer, String, Array[Integer, String]], [0, '', [0, '']]],
397
- [[Integer, String, Array[Integer, String]], [42, 'non-empty-string', [42, 'non-empty-string']]]
398
- ].each do |type, default|
399
- it 'respects the default value' do
400
- subject.params do
401
- optional :param, type: type, default: default
402
- end
403
- subject.get '/default_value' do
404
- params[:param]
405
- end
406
-
407
- get '/default_value', param: nil
408
- expect(last_response.status).to eq(200)
409
- expect(last_response.body).to eq(default.to_json)
410
- end
411
- end
412
- end
413
- end
414
-
415
- context 'array with default values and given conditions' do
416
- subject do
417
- Class.new(Grape::API) do
418
- default_format :json
419
- end
420
- end
421
-
422
- def app
423
- subject
424
- end
425
-
426
- it 'applies the default values only if the conditions are met' do
427
- subject.params do
428
- requires :ary, type: Array do
429
- requires :has_value, type: Grape::API::Boolean
430
- given has_value: ->(has_value) { has_value } do
431
- optional :type, type: String, values: %w[str int], default: 'str'
432
- given type: ->(type) { type == 'str' } do
433
- optional :str, type: String, default: 'a'
434
- end
435
- given type: ->(type) { type == 'int' } do
436
- optional :int, type: Integer, default: 1
437
- end
438
- end
439
- end
440
- end
441
- subject.post('/nested_given_and_default') { declared(self.params) }
442
-
443
- params = {
444
- ary: [
445
- { has_value: false },
446
- { has_value: true, type: 'int', int: 123 },
447
- { has_value: true, type: 'str', str: 'b' }
448
- ]
449
- }
450
- expected = {
451
- 'ary' => [
452
- { 'has_value' => false, 'type' => nil, 'int' => nil, 'str' => nil },
453
- { 'has_value' => true, 'type' => 'int', 'int' => 123, 'str' => nil },
454
- { 'has_value' => true, 'type' => 'str', 'int' => nil, 'str' => 'b' }
455
- ]
456
- }
457
-
458
- post '/nested_given_and_default', params
459
- expect(last_response.status).to eq(201)
460
- expect(JSON.parse(last_response.body)).to eq(expected)
461
- end
462
- end
463
- end
@@ -1,233 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Validators::ExactlyOneOfValidator do
4
- let_it_be(:app) do
5
- Class.new(Grape::API) do
6
- rescue_from Grape::Exceptions::ValidationErrors do |e|
7
- error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
8
- end
9
-
10
- params do
11
- optional :beer
12
- optional :wine
13
- optional :grapefruit
14
- exactly_one_of :beer, :wine, :grapefruit
15
- end
16
- post do
17
- end
18
-
19
- params do
20
- optional :beer
21
- optional :wine
22
- optional :grapefruit
23
- optional :other
24
- exactly_one_of :beer, :wine, :grapefruit
25
- end
26
- post 'mixed-params' do
27
- end
28
-
29
- params do
30
- optional :beer
31
- optional :wine
32
- optional :grapefruit
33
- exactly_one_of :beer, :wine, :grapefruit, message: 'you should choose one'
34
- end
35
- post '/custom-message' do
36
- end
37
-
38
- params do
39
- requires :item, type: Hash do
40
- optional :beer
41
- optional :wine
42
- optional :grapefruit
43
- exactly_one_of :beer, :wine, :grapefruit
44
- end
45
- end
46
- post '/nested-hash' do
47
- end
48
-
49
- params do
50
- optional :item, type: Hash do
51
- optional :beer
52
- optional :wine
53
- optional :grapefruit
54
- exactly_one_of :beer, :wine, :grapefruit
55
- end
56
- end
57
- post '/nested-optional-hash' do
58
- end
59
-
60
- params do
61
- requires :items, type: Array do
62
- optional :beer
63
- optional :wine
64
- optional :grapefruit
65
- exactly_one_of :beer, :wine, :grapefruit
66
- end
67
- end
68
- post '/nested-array' do
69
- end
70
-
71
- params do
72
- requires :items, type: Array do
73
- requires :nested_items, type: Array do
74
- optional :beer, :wine, :grapefruit, type: Grape::API::Boolean
75
- exactly_one_of :beer, :wine, :grapefruit
76
- end
77
- end
78
- end
79
- post '/deeply-nested-array' do
80
- end
81
- end
82
- end
83
-
84
- describe '#validate!' do
85
- subject(:validate) { post path, params }
86
-
87
- context 'when all params are present' do
88
- let(:path) { '/' }
89
- let(:params) { { beer: true, wine: true, grapefruit: true } }
90
-
91
- it 'returns a validation error' do
92
- validate
93
- expect(last_response.status).to eq 400
94
- expect(JSON.parse(last_response.body)).to eq(
95
- 'beer,wine,grapefruit' => ['are mutually exclusive']
96
- )
97
- end
98
-
99
- context 'mixed with other params' do
100
- let(:path) { '/mixed-params' }
101
- let(:params) { { beer: true, wine: true, grapefruit: true, other: true } }
102
-
103
- it 'returns a validation error' do
104
- validate
105
- expect(last_response.status).to eq 400
106
- expect(JSON.parse(last_response.body)).to eq(
107
- 'beer,wine,grapefruit' => ['are mutually exclusive']
108
- )
109
- end
110
- end
111
- end
112
-
113
- context 'when a subset of params are present' do
114
- let(:path) { '/' }
115
- let(:params) { { beer: true, grapefruit: true } }
116
-
117
- it 'returns a validation error' do
118
- validate
119
- expect(last_response.status).to eq 400
120
- expect(JSON.parse(last_response.body)).to eq(
121
- 'beer,grapefruit' => ['are mutually exclusive']
122
- )
123
- end
124
- end
125
-
126
- context 'when custom message is specified' do
127
- let(:path) { '/custom-message' }
128
- let(:params) { { beer: true, wine: true } }
129
-
130
- it 'returns a validation error' do
131
- validate
132
- expect(last_response.status).to eq 400
133
- expect(JSON.parse(last_response.body)).to eq(
134
- 'beer,wine' => ['you should choose one']
135
- )
136
- end
137
- end
138
-
139
- context 'when exacly one param is present' do
140
- let(:path) { '/' }
141
- let(:params) { { beer: true, somethingelse: true } }
142
-
143
- it 'does not return a validation error' do
144
- validate
145
- expect(last_response.status).to eq 201
146
- end
147
- end
148
-
149
- context 'when none of the params are present' do
150
- let(:path) { '/' }
151
- let(:params) { { somethingelse: true } }
152
-
153
- it 'returns a validation error' do
154
- validate
155
- expect(last_response.status).to eq 400
156
- expect(JSON.parse(last_response.body)).to eq(
157
- 'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
158
- )
159
- end
160
- end
161
-
162
- context 'when params are nested inside required hash' do
163
- let(:path) { '/nested-hash' }
164
- let(:params) { { item: { beer: true, wine: true } } }
165
-
166
- it 'returns a validation error with full names of the params' do
167
- validate
168
- expect(last_response.status).to eq 400
169
- expect(JSON.parse(last_response.body)).to eq(
170
- 'item[beer],item[wine]' => ['are mutually exclusive']
171
- )
172
- end
173
- end
174
-
175
- context 'when params are nested inside optional hash' do
176
- let(:path) { '/nested-optional-hash' }
177
-
178
- context 'when params are passed' do
179
- let(:params) { { item: { beer: true, wine: true } } }
180
-
181
- it 'returns a validation error with full names of the params' do
182
- validate
183
- expect(last_response.status).to eq 400
184
- expect(JSON.parse(last_response.body)).to eq(
185
- 'item[beer],item[wine]' => ['are mutually exclusive']
186
- )
187
- end
188
- end
189
-
190
- context 'when params are empty' do
191
- let(:params) { { other: true } }
192
-
193
- it 'does not return a validation error' do
194
- validate
195
- expect(last_response.status).to eq 201
196
- end
197
- end
198
- end
199
-
200
- context 'when params are nested inside array' do
201
- let(:path) { '/nested-array' }
202
- let(:params) { { items: [{ beer: true, wine: true }, { wine: true, grapefruit: true }] } }
203
-
204
- it 'returns a validation error with full names of the params' do
205
- validate
206
- expect(last_response.status).to eq 400
207
- expect(JSON.parse(last_response.body)).to eq(
208
- 'items[0][beer],items[0][wine]' => [
209
- 'are mutually exclusive'
210
- ],
211
- 'items[1][wine],items[1][grapefruit]' => [
212
- 'are mutually exclusive'
213
- ]
214
- )
215
- end
216
- end
217
-
218
- context 'when params are deeply nested' do
219
- let(:path) { '/deeply-nested-array' }
220
- let(:params) { { items: [{ nested_items: [{ beer: true, wine: true }] }] } }
221
-
222
- it 'returns a validation error with full names of the params' do
223
- validate
224
- expect(last_response.status).to eq 400
225
- expect(JSON.parse(last_response.body)).to eq(
226
- 'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => [
227
- 'are mutually exclusive'
228
- ]
229
- )
230
- end
231
- end
232
- end
233
- end