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,1420 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::ParamsScope do
4
- subject do
5
- Class.new(Grape::API)
6
- end
7
-
8
- def app
9
- subject
10
- end
11
-
12
- context 'when using custom types' do
13
- module ParamsScopeSpec
14
- class CustomType
15
- attr_reader :value
16
-
17
- def self.parse(value)
18
- raise if value == 'invalid'
19
-
20
- new(value)
21
- end
22
-
23
- def initialize(value)
24
- @value = value
25
- end
26
- end
27
- end
28
-
29
- it 'coerces the parameter via the type\'s parse method' do
30
- subject.params do
31
- requires :foo, type: ParamsScopeSpec::CustomType
32
- end
33
- subject.get('/types') { params[:foo].value }
34
-
35
- get '/types', foo: 'valid'
36
- expect(last_response.status).to eq(200)
37
- expect(last_response.body).to eq('valid')
38
-
39
- get '/types', foo: 'invalid'
40
- expect(last_response.status).to eq(400)
41
- expect(last_response.body).to match(/foo is invalid/)
42
- end
43
- end
44
-
45
- context 'param renaming' do
46
- it do
47
- subject.params do
48
- requires :foo, as: :bar
49
- optional :super, as: :hiper
50
- end
51
- subject.get('/renaming') { "#{declared(params)['bar']}-#{declared(params)['hiper']}" }
52
- get '/renaming', foo: 'any', super: 'any2'
53
-
54
- expect(last_response.status).to eq(200)
55
- expect(last_response.body).to eq('any-any2')
56
- end
57
-
58
- it do
59
- subject.params do
60
- requires :foo, as: :bar, type: String, coerce_with: ->(c) { c.strip }
61
- end
62
- subject.get('/renaming-coerced') { "#{params['bar']}-#{params['foo']}" }
63
- get '/renaming-coerced', foo: ' there we go '
64
-
65
- expect(last_response.status).to eq(200)
66
- expect(last_response.body).to eq('-there we go')
67
- end
68
-
69
- it do
70
- subject.params do
71
- requires :foo, as: :bar, allow_blank: false
72
- end
73
- subject.get('/renaming-not-blank') {}
74
- get '/renaming-not-blank', foo: ''
75
-
76
- expect(last_response.status).to eq(400)
77
- expect(last_response.body).to eq('foo is empty')
78
- end
79
-
80
- it do
81
- subject.params do
82
- requires :foo, as: :bar, allow_blank: false
83
- end
84
- subject.get('/renaming-not-blank-with-value') {}
85
- get '/renaming-not-blank-with-value', foo: 'any'
86
-
87
- expect(last_response.status).to eq(200)
88
- end
89
-
90
- it do
91
- subject.params do
92
- requires :foo, as: :baz, type: Hash do
93
- requires :bar, as: :qux
94
- end
95
- end
96
- subject.get('/nested-renaming') { declared(params).to_json }
97
- get '/nested-renaming', foo: { bar: 'any' }
98
-
99
- expect(last_response.status).to eq(200)
100
- expect(last_response.body).to eq('{"baz":{"qux":"any"}}')
101
- end
102
-
103
- it 'renaming can be defined before default' do
104
- subject.params do
105
- optional :foo, as: :bar, default: 'before'
106
- end
107
- subject.get('/rename-before-default') { declared(params)[:bar] }
108
- get '/rename-before-default'
109
-
110
- expect(last_response.status).to eq(200)
111
- expect(last_response.body).to eq('before')
112
- end
113
-
114
- it 'renaming can be defined after default' do
115
- subject.params do
116
- optional :foo, default: 'after', as: :bar
117
- end
118
- subject.get('/rename-after-default') { declared(params)[:bar] }
119
- get '/rename-after-default'
120
-
121
- expect(last_response.status).to eq(200)
122
- expect(last_response.body).to eq('after')
123
- end
124
- end
125
-
126
- context 'array without coerce type explicitly given' do
127
- it 'sets the type based on first element' do
128
- subject.params do
129
- requires :periods, type: Array, values: -> { %w[day month] }
130
- end
131
- subject.get('/required') { 'required works' }
132
-
133
- get '/required', periods: %w[day month]
134
- expect(last_response.status).to eq(200)
135
- expect(last_response.body).to eq('required works')
136
- end
137
-
138
- it 'fails to call API without Array type' do
139
- subject.params do
140
- requires :periods, type: Array, values: -> { %w[day month] }
141
- end
142
- subject.get('/required') { 'required works' }
143
-
144
- get '/required', periods: 'day'
145
- expect(last_response.status).to eq(400)
146
- expect(last_response.body).to eq('periods is invalid')
147
- end
148
-
149
- it 'raises exception when values are of different type' do
150
- expect do
151
- subject.params { requires :numbers, type: Array, values: [1, 'definitely not a number', 3] }
152
- end.to raise_error Grape::Exceptions::IncompatibleOptionValues
153
- end
154
-
155
- it 'raises exception when range values have different endpoint types' do
156
- expect do
157
- subject.params { requires :numbers, type: Array, values: 0.0..10 }
158
- end.to raise_error Grape::Exceptions::IncompatibleOptionValues
159
- end
160
- end
161
-
162
- context 'coercing values validation with proc' do
163
- it 'allows the proc to pass validation without checking' do
164
- subject.params { requires :numbers, type: Integer, values: -> { [0, 1, 2] } }
165
-
166
- subject.post('/required') { 'coercion with proc works' }
167
- post '/required', numbers: '1'
168
- expect(last_response.status).to eq(201)
169
- expect(last_response.body).to eq('coercion with proc works')
170
- end
171
-
172
- it 'allows the proc to pass validation without checking in value' do
173
- subject.params { requires :numbers, type: Integer, values: { value: -> { [0, 1, 2] } } }
174
-
175
- subject.post('/required') { 'coercion with proc works' }
176
- post '/required', numbers: '1'
177
- expect(last_response.status).to eq(201)
178
- expect(last_response.body).to eq('coercion with proc works')
179
- end
180
-
181
- it 'allows the proc to pass validation without checking in except' do
182
- subject.params { requires :numbers, type: Integer, values: { except: -> { [0, 1, 2] } } }
183
-
184
- subject.post('/required') { 'coercion with proc works' }
185
- post '/required', numbers: '10'
186
- expect(last_response.status).to eq(201)
187
- expect(last_response.body).to eq('coercion with proc works')
188
- end
189
- end
190
-
191
- context 'with range values' do
192
- context "when left range endpoint isn't #kind_of? the type" do
193
- it 'raises exception' do
194
- expect do
195
- subject.params { requires :latitude, type: Integer, values: -90.0..90 }
196
- end.to raise_error Grape::Exceptions::IncompatibleOptionValues
197
- end
198
- end
199
-
200
- context "when right range endpoint isn't #kind_of? the type" do
201
- it 'raises exception' do
202
- expect do
203
- subject.params { requires :latitude, type: Integer, values: -90..90.0 }
204
- end.to raise_error Grape::Exceptions::IncompatibleOptionValues
205
- end
206
- end
207
-
208
- context 'when the default is an array' do
209
- context 'and is the entire range of allowed values' do
210
- it 'does not raise an exception' do
211
- expect do
212
- subject.params { optional :numbers, type: Array[Integer], values: 0..2, default: 0..2 }
213
- end.not_to raise_error
214
- end
215
- end
216
-
217
- context 'and is a subset of allowed values' do
218
- it 'does not raise an exception' do
219
- expect do
220
- subject.params { optional :numbers, type: Array[Integer], values: [0, 1, 2], default: [1, 0] }
221
- end.not_to raise_error
222
- end
223
- end
224
- end
225
-
226
- context 'when both range endpoints are #kind_of? the type' do
227
- it 'accepts values in the range' do
228
- subject.params do
229
- requires :letter, type: String, values: 'a'..'z'
230
- end
231
- subject.get('/letter') { params[:letter] }
232
-
233
- get '/letter', letter: 'j'
234
- expect(last_response.status).to eq(200)
235
- expect(last_response.body).to eq('j')
236
- end
237
-
238
- it 'rejects values outside the range' do
239
- subject.params do
240
- requires :letter, type: String, values: 'a'..'z'
241
- end
242
- subject.get('/letter') { params[:letter] }
243
-
244
- get '/letter', letter: 'J'
245
- expect(last_response.status).to eq(400)
246
- expect(last_response.body).to eq('letter does not have a valid value')
247
- end
248
- end
249
- end
250
-
251
- context 'parameters in group' do
252
- it 'errors when no type is provided' do
253
- expect do
254
- subject.params do
255
- group :a do
256
- requires :b
257
- end
258
- end
259
- end.to raise_error Grape::Exceptions::MissingGroupType
260
-
261
- expect do
262
- subject.params do
263
- optional :a do
264
- requires :b
265
- end
266
- end
267
- end.to raise_error Grape::Exceptions::MissingGroupType
268
- end
269
-
270
- it 'allows Hash as type' do
271
- subject.params do
272
- group :a, type: Hash do
273
- requires :b
274
- end
275
- end
276
- subject.get('/group') { 'group works' }
277
- get '/group', a: { b: true }
278
- expect(last_response.status).to eq(200)
279
- expect(last_response.body).to eq('group works')
280
-
281
- subject.params do
282
- optional :a, type: Hash do
283
- requires :b
284
- end
285
- end
286
- get '/optional_type_hash'
287
- end
288
-
289
- it 'allows Array as type' do
290
- subject.params do
291
- group :a, type: Array do
292
- requires :b
293
- end
294
- end
295
- subject.get('/group') { 'group works' }
296
- get '/group', a: [{ b: true }]
297
- expect(last_response.status).to eq(200)
298
- expect(last_response.body).to eq('group works')
299
-
300
- subject.params do
301
- optional :a, type: Array do
302
- requires :b
303
- end
304
- end
305
- get '/optional_type_array'
306
- end
307
-
308
- it 'handles missing optional Array type' do
309
- subject.params do
310
- optional :a, type: Array do
311
- requires :b
312
- end
313
- end
314
- subject.get('/test') { declared(params).to_json }
315
- get '/test'
316
- expect(last_response.status).to eq(200)
317
- expect(last_response.body).to eq('{"a":[]}')
318
- end
319
-
320
- it 'errors with an unsupported type' do
321
- expect do
322
- subject.params do
323
- group :a, type: Set do
324
- requires :b
325
- end
326
- end
327
- end.to raise_error Grape::Exceptions::UnsupportedGroupType
328
-
329
- expect do
330
- subject.params do
331
- optional :a, type: Set do
332
- requires :b
333
- end
334
- end
335
- end.to raise_error Grape::Exceptions::UnsupportedGroupType
336
- end
337
- end
338
-
339
- context 'when validations are dependent on a parameter' do
340
- before do
341
- subject.params do
342
- optional :a
343
- given :a do
344
- requires :b
345
- end
346
- end
347
- subject.get('/test') { declared(params).to_json }
348
- end
349
-
350
- it 'applies the validations only if the parameter is present' do
351
- get '/test'
352
- expect(last_response.status).to eq(200)
353
-
354
- get '/test', a: true
355
- expect(last_response.status).to eq(400)
356
- expect(last_response.body).to eq('b is missing')
357
-
358
- get '/test', a: true, b: true
359
- expect(last_response.status).to eq(200)
360
- end
361
-
362
- it 'applies the validations of multiple parameters' do
363
- subject.params do
364
- optional :a, :b
365
- given :a, :b do
366
- requires :c
367
- end
368
- end
369
- subject.get('/multiple') { declared(params).to_json }
370
-
371
- get '/multiple'
372
- expect(last_response.status).to eq(200)
373
-
374
- get '/multiple', a: true
375
- expect(last_response.status).to eq(200)
376
-
377
- get '/multiple', b: true
378
- expect(last_response.status).to eq(200)
379
-
380
- get '/multiple', a: true, b: true
381
- expect(last_response.status).to eq(400)
382
- expect(last_response.body).to eq('c is missing')
383
-
384
- get '/multiple', a: true, b: true, c: true
385
- expect(last_response.status).to eq(200)
386
- end
387
-
388
- it 'applies only the appropriate validation' do
389
- subject.params do
390
- optional :a
391
- optional :b
392
- mutually_exclusive :a, :b
393
- given :a do
394
- requires :c, type: String
395
- end
396
- given :b do
397
- requires :c, type: Integer
398
- end
399
- end
400
- subject.get('/multiple') { declared(params).to_json }
401
-
402
- get '/multiple'
403
- expect(last_response.status).to eq(200)
404
-
405
- get '/multiple', a: true, c: 'test'
406
- expect(last_response.status).to eq(200)
407
- expect(JSON.parse(last_response.body).symbolize_keys).to eq a: 'true', b: nil, c: 'test'
408
-
409
- get '/multiple', b: true, c: '3'
410
- expect(last_response.status).to eq(200)
411
- expect(JSON.parse(last_response.body).symbolize_keys).to eq a: nil, b: 'true', c: 3
412
-
413
- get '/multiple', a: true
414
- expect(last_response.status).to eq(400)
415
- expect(last_response.body).to eq('c is missing')
416
-
417
- get '/multiple', b: true
418
- expect(last_response.status).to eq(400)
419
- expect(last_response.body).to eq('c is missing')
420
-
421
- get '/multiple', a: true, b: true, c: 'test'
422
- expect(last_response.status).to eq(400)
423
- expect(last_response.body).to eq('a, b are mutually exclusive, c is invalid')
424
- end
425
-
426
- it 'raises an error if the dependent parameter was never specified' do
427
- expect do
428
- subject.params do
429
- given :c do
430
- end
431
- end
432
- end.to raise_error(Grape::Exceptions::UnknownParameter)
433
- end
434
-
435
- it 'does not raise an error if the dependent parameter is a Hash' do
436
- expect do
437
- subject.params do
438
- optional :a, type: Hash do
439
- requires :b
440
- end
441
- given :a do
442
- requires :c
443
- end
444
- end
445
- end.not_to raise_error
446
- end
447
-
448
- it 'does not raise an error if when using nested given' do
449
- expect do
450
- subject.params do
451
- optional :a, type: Hash do
452
- requires :b
453
- end
454
- given :a do
455
- requires :c
456
- given :c do
457
- requires :d
458
- end
459
- end
460
- end
461
- end.not_to raise_error
462
- end
463
-
464
- it 'allows nested dependent parameters' do
465
- subject.params do
466
- optional :a
467
- given a: ->(val) { val == 'a' } do
468
- optional :b
469
- given b: ->(val) { val == 'b' } do
470
- optional :c
471
- given c: ->(val) { val == 'c' } do
472
- requires :d
473
- end
474
- end
475
- end
476
- end
477
- subject.get('/') { declared(params).to_json }
478
-
479
- get '/'
480
- expect(last_response.status).to eq 200
481
-
482
- get '/', a: 'a', b: 'b', c: 'c'
483
- expect(last_response.status).to eq 400
484
- expect(last_response.body).to eq 'd is missing'
485
-
486
- get '/', a: 'a', b: 'b', c: 'c', d: 'd'
487
- expect(last_response.status).to eq 200
488
- expect(last_response.body).to eq({ a: 'a', b: 'b', c: 'c', d: 'd' }.to_json)
489
- end
490
-
491
- it 'allows renaming of dependent parameters' do
492
- subject.params do
493
- optional :a
494
- given :a do
495
- requires :b, as: :c
496
- end
497
- end
498
-
499
- subject.get('/multiple') { declared(params).to_json }
500
-
501
- get '/multiple', a: 'a', b: 'b'
502
-
503
- body = JSON.parse(last_response.body)
504
-
505
- expect(body.keys).to include('c')
506
- expect(body.keys).not_to include('b')
507
- end
508
-
509
- it 'allows renaming of dependent on parameter' do
510
- subject.params do
511
- optional :a, as: :b
512
- given a: ->(val) { val == 'x' } do
513
- requires :c
514
- end
515
- end
516
- subject.get('/') { declared(params) }
517
-
518
- get '/', a: 'x'
519
- expect(last_response.status).to eq 400
520
- expect(last_response.body).to eq 'c is missing'
521
-
522
- get '/', a: 'y'
523
- expect(last_response.status).to eq 200
524
- end
525
-
526
- it 'does not raise if the dependent parameter is not the renamed one' do
527
- expect do
528
- subject.params do
529
- optional :a, as: :b
530
- given :a do
531
- requires :c
532
- end
533
- end
534
- end.not_to raise_error
535
- end
536
-
537
- it 'raises an error if the dependent parameter is the renamed one' do
538
- expect do
539
- subject.params do
540
- optional :a, as: :b
541
- given :b do
542
- requires :c
543
- end
544
- end
545
- end.to raise_error(Grape::Exceptions::UnknownParameter)
546
- end
547
-
548
- it 'does not validate nested requires when given is false' do
549
- subject.params do
550
- requires :a, type: String, allow_blank: false, values: %w[x y z]
551
- given a: ->(val) { val == 'x' } do
552
- requires :inner1, type: Hash, allow_blank: false do
553
- requires :foo, type: Integer, allow_blank: false
554
- end
555
- end
556
- given a: ->(val) { val == 'y' } do
557
- requires :inner2, type: Hash, allow_blank: false do
558
- requires :bar, type: Integer, allow_blank: false
559
- requires :baz, type: Array, allow_blank: false do
560
- requires :baz_category, type: String, allow_blank: false
561
- end
562
- end
563
- end
564
- given a: ->(val) { val == 'z' } do
565
- requires :inner3, type: Array, allow_blank: false do
566
- requires :bar, type: Integer, allow_blank: false
567
- requires :baz, type: Array, allow_blank: false do
568
- requires :baz_category, type: String, allow_blank: false
569
- end
570
- end
571
- end
572
- end
573
- subject.get('/varying') { declared(params).to_json }
574
-
575
- get '/varying', a: 'x', inner1: { foo: 1 }
576
- expect(last_response.status).to eq(200)
577
-
578
- get '/varying', a: 'y', inner2: { bar: 2, baz: [{ baz_category: 'barstools' }] }
579
- expect(last_response.status).to eq(200)
580
-
581
- get '/varying', a: 'y', inner2: { bar: 2, baz: [{ unrelated: 'yep' }] }
582
- expect(last_response.status).to eq(400)
583
-
584
- get '/varying', a: 'z', inner3: [{ bar: 3, baz: [{ baz_category: 'barstools' }] }]
585
- expect(last_response.status).to eq(200)
586
- end
587
-
588
- it 'detect unmet nested dependency' do
589
- subject.params do
590
- requires :a, type: String, allow_blank: false, values: %w[x y z]
591
- given a: ->(val) { val == 'z' } do
592
- requires :inner3, type: Array, allow_blank: false do
593
- requires :bar, type: String, allow_blank: false
594
- given bar: ->(val) { val == 'b' } do
595
- requires :baz, type: Array do
596
- optional :baz_category, type: String
597
- end
598
- end
599
- given bar: ->(val) { val == 'c' } do
600
- requires :baz, type: Array do
601
- requires :baz_category, type: String
602
- end
603
- end
604
- end
605
- end
606
- end
607
- subject.get('/nested-dependency') { declared(params).to_json }
608
-
609
- get '/nested-dependency', a: 'z', inner3: [{ bar: 'c', baz: [{ unrelated: 'nope' }] }]
610
- expect(last_response.status).to eq(400)
611
- expect(last_response.body).to eq 'inner3[0][baz][0][baz_category] is missing'
612
- end
613
-
614
- it 'includes the parameter within #declared(params)' do
615
- get '/test', a: true, b: true
616
-
617
- expect(JSON.parse(last_response.body)).to eq('a' => 'true', 'b' => 'true')
618
- end
619
-
620
- it 'returns a sensible error message within a nested context' do
621
- subject.params do
622
- requires :bar, type: Hash do
623
- optional :a
624
- given :a do
625
- requires :b
626
- end
627
- end
628
- end
629
- subject.get('/nested') { 'worked' }
630
-
631
- get '/nested', bar: { a: true }
632
- expect(last_response.status).to eq(400)
633
- expect(last_response.body).to eq('bar[b] is missing')
634
- end
635
-
636
- it 'includes the nested parameter within #declared(params)' do
637
- subject.params do
638
- requires :bar, type: Hash do
639
- optional :a
640
- given :a do
641
- requires :b
642
- end
643
- end
644
- end
645
- subject.get('/nested') { declared(params).to_json }
646
-
647
- get '/nested', bar: { a: true, b: 'yes' }
648
- expect(JSON.parse(last_response.body)).to eq('bar' => { 'a' => 'true', 'b' => 'yes' })
649
- end
650
-
651
- it 'includes level 2 nested parameters outside the given within #declared(params)' do
652
- subject.params do
653
- requires :bar, type: Hash do
654
- optional :a
655
- given :a do
656
- requires :c, type: Hash do
657
- requires :b
658
- end
659
- end
660
- end
661
- end
662
- subject.get('/nested') { declared(params).to_json }
663
-
664
- get '/nested', bar: { a: true, c: { b: 'yes' } }
665
- expect(JSON.parse(last_response.body)).to eq('bar' => { 'a' => 'true', 'c' => { 'b' => 'yes' } })
666
- end
667
-
668
- context 'when the dependent parameter is not present #declared(params)' do
669
- context 'lateral parameter' do
670
- before do
671
- [true, false].each do |evaluate_given|
672
- subject.params do
673
- optional :a
674
- given :a do
675
- optional :b
676
- end
677
- end
678
- subject.get("/evaluate_given_#{evaluate_given}") { declared(params, evaluate_given: evaluate_given).to_json }
679
- end
680
- end
681
-
682
- it 'evaluate_given_false' do
683
- get '/evaluate_given_false', b: 'b'
684
- expect(JSON.parse(last_response.body)).to eq('a' => nil, 'b' => 'b')
685
- end
686
-
687
- it 'evaluate_given_true' do
688
- get '/evaluate_given_true', b: 'b'
689
- expect(JSON.parse(last_response.body)).to eq('a' => nil)
690
- end
691
- end
692
-
693
- context 'lateral hash parameter' do
694
- before do
695
- [true, false].each do |evaluate_given|
696
- subject.params do
697
- optional :a, values: %w[x y]
698
- given a: ->(a) { a == 'x' } do
699
- optional :b, type: Hash do
700
- optional :c
701
- end
702
- optional :e
703
- end
704
- given a: ->(a) { a == 'y' } do
705
- optional :b, type: Hash do
706
- optional :d
707
- end
708
- optional :f
709
- end
710
- end
711
- subject.get("/evaluate_given_#{evaluate_given}") { declared(params, evaluate_given: evaluate_given).to_json }
712
- end
713
- end
714
-
715
- it 'evaluate_given_false' do
716
- get '/evaluate_given_false', a: 'x'
717
- expect(JSON.parse(last_response.body)).to eq('a' => 'x', 'b' => { 'd' => nil }, 'e' => nil, 'f' => nil)
718
-
719
- get '/evaluate_given_false', a: 'y'
720
- expect(JSON.parse(last_response.body)).to eq('a' => 'y', 'b' => { 'd' => nil }, 'e' => nil, 'f' => nil)
721
- end
722
-
723
- it 'evaluate_given_true' do
724
- get '/evaluate_given_true', a: 'x'
725
- expect(JSON.parse(last_response.body)).to eq('a' => 'x', 'b' => { 'c' => nil }, 'e' => nil)
726
-
727
- get '/evaluate_given_true', a: 'y'
728
- expect(JSON.parse(last_response.body)).to eq('a' => 'y', 'b' => { 'd' => nil }, 'f' => nil)
729
- end
730
- end
731
-
732
- context 'lateral parameter within lateral hash parameter' do
733
- before do
734
- [true, false].each do |evaluate_given|
735
- subject.params do
736
- optional :a, values: %w[x y]
737
- given a: ->(a) { a == 'x' } do
738
- optional :b, type: Hash do
739
- optional :c
740
- given :c do
741
- optional :g
742
- optional :e, type: Hash do
743
- optional :h
744
- end
745
- end
746
- end
747
- end
748
- given a: ->(a) { a == 'y' } do
749
- optional :b, type: Hash do
750
- optional :d
751
- given :d do
752
- optional :f
753
- optional :e, type: Hash do
754
- optional :i
755
- end
756
- end
757
- end
758
- end
759
- end
760
- subject.get("/evaluate_given_#{evaluate_given}") { declared(params, evaluate_given: evaluate_given).to_json }
761
- end
762
- end
763
-
764
- it 'evaluate_given_false' do
765
- get '/evaluate_given_false', a: 'x'
766
- expect(JSON.parse(last_response.body)).to eq('a' => 'x', 'b' => { 'd' => nil, 'f' => nil, 'e' => { 'i' => nil } })
767
-
768
- get '/evaluate_given_false', a: 'x', b: { c: 'c' }
769
- expect(JSON.parse(last_response.body)).to eq('a' => 'x', 'b' => { 'd' => nil, 'f' => nil, 'e' => { 'i' => nil } })
770
-
771
- get '/evaluate_given_false', a: 'y'
772
- expect(JSON.parse(last_response.body)).to eq('a' => 'y', 'b' => { 'd' => nil, 'f' => nil, 'e' => { 'i' => nil } })
773
-
774
- get '/evaluate_given_false', a: 'y', b: { d: 'd' }
775
- expect(JSON.parse(last_response.body)).to eq('a' => 'y', 'b' => { 'd' => 'd', 'f' => nil, 'e' => { 'i' => nil } })
776
- end
777
-
778
- it 'evaluate_given_true' do
779
- get '/evaluate_given_true', a: 'x'
780
- expect(JSON.parse(last_response.body)).to eq('a' => 'x', 'b' => { 'c' => nil })
781
-
782
- get '/evaluate_given_true', a: 'x', b: { c: 'c' }
783
- expect(JSON.parse(last_response.body)).to eq('a' => 'x', 'b' => { 'c' => 'c', 'g' => nil, 'e' => { 'h' => nil } })
784
-
785
- get '/evaluate_given_true', a: 'y'
786
- expect(JSON.parse(last_response.body)).to eq('a' => 'y', 'b' => { 'd' => nil })
787
-
788
- get '/evaluate_given_true', a: 'y', b: { d: 'd' }
789
- expect(JSON.parse(last_response.body)).to eq('a' => 'y', 'b' => { 'd' => 'd', 'f' => nil, 'e' => { 'i' => nil } })
790
- end
791
- end
792
-
793
- context 'lateral parameter within an array param' do
794
- before do
795
- [true, false].each do |evaluate_given|
796
- subject.params do
797
- optional :array, type: Array do
798
- optional :a
799
- given :a do
800
- optional :b
801
- end
802
- end
803
- end
804
- subject.post("/evaluate_given_#{evaluate_given}") do
805
- declared(params, evaluate_given: evaluate_given).to_json
806
- end
807
- end
808
- end
809
-
810
- it 'evaluate_given_false' do
811
- post '/evaluate_given_false', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json'
812
- expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil, 'b' => 'b' }, { 'a' => 'a', 'b' => 'b' }])
813
- end
814
-
815
- it 'evaluate_given_true' do
816
- post '/evaluate_given_true', { array: [{ b: 'b' }, { a: 'a', b: 'b' }] }.to_json, 'CONTENT_TYPE' => 'application/json'
817
- expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => nil }, { 'a' => 'a', 'b' => 'b' }])
818
- end
819
- end
820
-
821
- context 'nested given parameter' do
822
- before do
823
- [true, false].each do |evaluate_given|
824
- subject.params do
825
- optional :a
826
- optional :c
827
- given :a do
828
- given :c do
829
- optional :b
830
- end
831
- end
832
- end
833
- subject.post("/evaluate_given_#{evaluate_given}") do
834
- declared(params, evaluate_given: evaluate_given).to_json
835
- end
836
- end
837
- end
838
-
839
- it 'evaluate_given_false' do
840
- post '/evaluate_given_false', { a: 'a', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json'
841
- expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'b' => 'b', 'c' => nil)
842
-
843
- post '/evaluate_given_false', { c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json'
844
- expect(JSON.parse(last_response.body)).to eq('a' => nil, 'b' => 'b', 'c' => 'c')
845
-
846
- post '/evaluate_given_false', { a: 'a', c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json'
847
- expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'b' => 'b', 'c' => 'c')
848
- end
849
-
850
- it 'evaluate_given_true' do
851
- post '/evaluate_given_true', { a: 'a', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json'
852
- expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'c' => nil)
853
-
854
- post '/evaluate_given_true', { c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json'
855
- expect(JSON.parse(last_response.body)).to eq('a' => nil, 'c' => 'c')
856
-
857
- post '/evaluate_given_true', { a: 'a', c: 'c', b: 'b' }.to_json, 'CONTENT_TYPE' => 'application/json'
858
- expect(JSON.parse(last_response.body)).to eq('a' => 'a', 'b' => 'b', 'c' => 'c')
859
- end
860
- end
861
-
862
- context 'nested given parameter within an array param' do
863
- before do
864
- [true, false].each do |evaluate_given|
865
- subject.params do
866
- optional :array, type: Array do
867
- optional :a
868
- optional :c
869
- given :a do
870
- given :c do
871
- optional :b
872
- end
873
- end
874
- end
875
- end
876
- subject.post("/evaluate_given_#{evaluate_given}") do
877
- declared(params, evaluate_given: evaluate_given).to_json
878
- end
879
- end
880
- end
881
-
882
- let :evaluate_given_params do
883
- {
884
- array: [
885
- { a: 'a', b: 'b' },
886
- { c: 'c', b: 'b' },
887
- { a: 'a', c: 'c', b: 'b' }
888
- ]
889
- }
890
- end
891
-
892
- it 'evaluate_given_false' do
893
- post '/evaluate_given_false', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json'
894
- expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'a', 'b' => 'b', 'c' => nil }, { 'a' => nil, 'b' => 'b', 'c' => 'c' }, { 'a' => 'a', 'b' => 'b', 'c' => 'c' }])
895
- end
896
-
897
- it 'evaluate_given_true' do
898
- post '/evaluate_given_true', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json'
899
- expect(JSON.parse(last_response.body)).to eq('array' => [{ 'a' => 'a', 'c' => nil }, { 'a' => nil, 'c' => 'c' }, { 'a' => 'a', 'b' => 'b', 'c' => 'c' }])
900
- end
901
- end
902
-
903
- context 'nested given parameter within a nested given parameter within an array param' do
904
- before do
905
- [true, false].each do |evaluate_given|
906
- subject.params do
907
- optional :array, type: Array do
908
- optional :a
909
- optional :c
910
- given :a do
911
- given :c do
912
- optional :array, type: Array do
913
- optional :a
914
- optional :c
915
- given :a do
916
- given :c do
917
- optional :b
918
- end
919
- end
920
- end
921
- end
922
- end
923
- end
924
- end
925
- subject.post("/evaluate_given_#{evaluate_given}") do
926
- declared(params, evaluate_given: evaluate_given).to_json
927
- end
928
- end
929
- end
930
-
931
- let :evaluate_given_params do
932
- {
933
- array: [{
934
- a: 'a',
935
- c: 'c',
936
- array: [
937
- { a: 'a', b: 'b' },
938
- { c: 'c', b: 'b' },
939
- { a: 'a', c: 'c', b: 'b' }
940
- ]
941
- }]
942
- }
943
- end
944
-
945
- it 'evaluate_given_false' do
946
- expected_response_hash = {
947
- 'array' => [{
948
- 'a' => 'a',
949
- 'c' => 'c',
950
- 'array' => [
951
- { 'a' => 'a', 'b' => 'b', 'c' => nil },
952
- { 'a' => nil, 'c' => 'c', 'b' => 'b' },
953
- { 'a' => 'a', 'c' => 'c', 'b' => 'b' }
954
- ]
955
- }]
956
- }
957
- post '/evaluate_given_false', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json'
958
- expect(JSON.parse(last_response.body)).to eq(expected_response_hash)
959
- end
960
-
961
- it 'evaluate_given_true' do
962
- expected_response_hash = {
963
- 'array' => [{
964
- 'a' => 'a',
965
- 'c' => 'c',
966
- 'array' => [
967
- { 'a' => 'a', 'c' => nil },
968
- { 'a' => nil, 'c' => 'c' },
969
- { 'a' => 'a', 'b' => 'b', 'c' => 'c' }
970
- ]
971
- }]
972
- }
973
- post '/evaluate_given_true', evaluate_given_params.to_json, 'CONTENT_TYPE' => 'application/json'
974
- expect(JSON.parse(last_response.body)).to eq(expected_response_hash)
975
- end
976
- end
977
- end
978
- end
979
-
980
- context 'default value in given block' do
981
- before do
982
- subject.params do
983
- optional :a, values: %w[a b]
984
- given a: ->(val) { val == 'a' } do
985
- optional :b, default: 'default'
986
- end
987
- end
988
- subject.get('/') { params.to_json }
989
- end
990
-
991
- context 'when dependency meets' do
992
- it 'sets default value for dependent parameter' do
993
- get '/', a: 'a'
994
- expect(last_response.body).to eq({ a: 'a', b: 'default' }.to_json)
995
- end
996
- end
997
-
998
- context 'when dependency does not meet' do
999
- it 'does not set default value for dependent parameter' do
1000
- get '/', a: 'b'
1001
- expect(last_response.body).to eq({ a: 'b' }.to_json)
1002
- end
1003
- end
1004
- end
1005
-
1006
- context 'when validations are dependent on a parameter within an array param' do
1007
- before do
1008
- subject.params do
1009
- requires :foos, type: Array do
1010
- optional :foo
1011
- given :foo do
1012
- requires :bar
1013
- end
1014
- end
1015
- end
1016
- subject.get('/test') { 'ok' }
1017
- end
1018
-
1019
- it 'passes none Hash params' do
1020
- get '/test', foos: ['']
1021
- expect(last_response.status).to eq(200)
1022
- expect(last_response.body).to eq('ok')
1023
- end
1024
- end
1025
-
1026
- context 'when validations are dependent on a parameter within an array param within #declared(params).to_json' do
1027
- before do
1028
- subject.params do
1029
- requires :foos, type: Array do
1030
- optional :foo_type, :baz_type
1031
- given :foo_type do
1032
- requires :bar
1033
- end
1034
- end
1035
- end
1036
- subject.post('/test') { declared(params).to_json }
1037
- end
1038
-
1039
- it 'applies the constraint within each value' do
1040
- post '/test',
1041
- { foos: [{ foo_type: 'a' }, { baz_type: 'c' }] }.to_json,
1042
- 'CONTENT_TYPE' => 'application/json'
1043
-
1044
- expect(last_response.status).to eq(400)
1045
- expect(last_response.body).to eq('foos[0][bar] is missing')
1046
- end
1047
- end
1048
-
1049
- context 'when validations are dependent on a parameter with specific value' do
1050
- # build test cases from all combinations of declarations and options
1051
- a_decls = %i[optional requires]
1052
- a_options = [{}, { values: %w[x y z] }]
1053
- b_options = [{}, { type: String }, { allow_blank: false }, { type: String, allow_blank: false }]
1054
- combinations = a_decls.product(a_options, b_options)
1055
- combinations.each_with_index do |combination, i|
1056
- a_decl, a_opts, b_opts = combination
1057
-
1058
- context "(case #{i})" do
1059
- before do
1060
- # puts "a_decl: #{a_decl}, a_opts: #{a_opts}, b_opts: #{b_opts}"
1061
- subject.params do
1062
- send a_decl, :a, **a_opts
1063
- given(a: ->(val) { val == 'x' }) { requires :b, **b_opts }
1064
- given(a: ->(val) { val == 'y' }) { requires :c, **b_opts }
1065
- end
1066
- subject.get('/test') { declared(params).to_json }
1067
- end
1068
-
1069
- if a_decl == :optional
1070
- it 'skips validation when base param is missing' do
1071
- get '/test'
1072
- expect(last_response.status).to eq(200)
1073
- end
1074
- end
1075
-
1076
- it 'skips validation when base param does not have a specified value' do
1077
- get '/test', a: 'z'
1078
- expect(last_response.status).to eq(200)
1079
-
1080
- get '/test', a: 'z', b: ''
1081
- expect(last_response.status).to eq(200)
1082
- end
1083
-
1084
- it 'applies the validation when base param has the specific value' do
1085
- get '/test', a: 'x'
1086
- expect(last_response.status).to eq(400)
1087
- expect(last_response.body).to include('b is missing')
1088
-
1089
- get '/test', a: 'x', b: true
1090
- expect(last_response.status).to eq(200)
1091
-
1092
- get '/test', a: 'x', b: true, c: ''
1093
- expect(last_response.status).to eq(200)
1094
- end
1095
-
1096
- it 'includes the parameter within #declared(params)' do
1097
- get '/test', a: 'x', b: true
1098
- expect(JSON.parse(last_response.body)).to eq('a' => 'x', 'b' => 'true', 'c' => nil)
1099
- end
1100
- end
1101
- end
1102
- end
1103
-
1104
- it 'raises an error if the dependent parameter was never specified' do
1105
- expect do
1106
- subject.params do
1107
- given :c do
1108
- end
1109
- end
1110
- end.to raise_error(Grape::Exceptions::UnknownParameter)
1111
- end
1112
-
1113
- it 'returns a sensible error message within a nested context' do
1114
- subject.params do
1115
- requires :bar, type: Hash do
1116
- optional :a
1117
- given a: ->(val) { val == 'x' } do
1118
- requires :b
1119
- end
1120
- end
1121
- end
1122
- subject.get('/nested') { 'worked' }
1123
-
1124
- get '/nested', bar: { a: 'x' }
1125
- expect(last_response.status).to eq(400)
1126
- expect(last_response.body).to eq('bar[b] is missing')
1127
- end
1128
-
1129
- it 'includes the nested parameter within #declared(params)' do
1130
- subject.params do
1131
- requires :bar, type: Hash do
1132
- optional :a
1133
- given a: ->(val) { val == 'x' } do
1134
- requires :b
1135
- end
1136
- end
1137
- end
1138
- subject.get('/nested') { declared(params).to_json }
1139
-
1140
- get '/nested', bar: { a: 'x', b: 'yes' }
1141
- expect(JSON.parse(last_response.body)).to eq('bar' => { 'a' => 'x', 'b' => 'yes' })
1142
- end
1143
-
1144
- it 'includes level 2 nested parameters outside the given within #declared(params)' do
1145
- subject.params do
1146
- requires :bar, type: Hash do
1147
- optional :a
1148
- given a: ->(val) { val == 'x' } do
1149
- requires :c, type: Hash do
1150
- requires :b
1151
- end
1152
- end
1153
- end
1154
- end
1155
- subject.get('/nested') { declared(params).to_json }
1156
-
1157
- get '/nested', bar: { a: 'x', c: { b: 'yes' } }
1158
- expect(JSON.parse(last_response.body)).to eq('bar' => { 'a' => 'x', 'c' => { 'b' => 'yes' } })
1159
- end
1160
-
1161
- it 'includes deeply nested parameters within #declared(params)' do
1162
- subject.params do
1163
- requires :arr1, type: Array do
1164
- requires :hash1, type: Hash do
1165
- requires :arr2, type: Array do
1166
- requires :hash2, type: Hash do
1167
- requires :something, type: String
1168
- end
1169
- end
1170
- end
1171
- end
1172
- end
1173
- subject.get('/nested_deep') { declared(params).to_json }
1174
-
1175
- get '/nested_deep', arr1: [{ hash1: { arr2: [{ hash2: { something: 'value' } }] } }]
1176
- expect(last_response.status).to eq(200)
1177
- expect(JSON.parse(last_response.body)).to eq('arr1' => [{ 'hash1' => { 'arr2' => [{ 'hash2' => { 'something' => 'value' } }] } }])
1178
- end
1179
-
1180
- context 'failing fast' do
1181
- context 'when fail_fast is not defined' do
1182
- it 'does not stop validation' do
1183
- subject.params do
1184
- requires :one
1185
- requires :two
1186
- requires :three
1187
- end
1188
- subject.get('/fail-fast') { declared(params).to_json }
1189
-
1190
- get '/fail-fast'
1191
- expect(last_response.status).to eq(400)
1192
- expect(last_response.body).to eq('one is missing, two is missing, three is missing')
1193
- end
1194
- end
1195
-
1196
- context 'when fail_fast is defined it stops the validation' do
1197
- it 'of other params' do
1198
- subject.params do
1199
- requires :one, fail_fast: true
1200
- requires :two
1201
- end
1202
- subject.get('/fail-fast') { declared(params).to_json }
1203
-
1204
- get '/fail-fast'
1205
- expect(last_response.status).to eq(400)
1206
- expect(last_response.body).to eq('one is missing')
1207
- end
1208
-
1209
- it 'for a single param' do
1210
- subject.params do
1211
- requires :one, allow_blank: false, regexp: /[0-9]+/, fail_fast: true
1212
- end
1213
- subject.get('/fail-fast') { declared(params).to_json }
1214
-
1215
- get '/fail-fast', one: ''
1216
- expect(last_response.status).to eq(400)
1217
- expect(last_response.body).to eq('one is empty')
1218
- end
1219
- end
1220
- end
1221
-
1222
- context 'when params have group attributes' do
1223
- context 'with validations' do
1224
- before do
1225
- subject.params do
1226
- with(allow_blank: false) do
1227
- requires :id
1228
- optional :name
1229
- optional :address, allow_blank: true
1230
- end
1231
- end
1232
- subject.get('test')
1233
- end
1234
-
1235
- context 'when data is invalid' do
1236
- before do
1237
- get 'test', id: '', name: ''
1238
- end
1239
-
1240
- it 'returns a validation error' do
1241
- expect(last_response.status).to eq(400)
1242
- end
1243
-
1244
- it 'applies group validations for every parameter' do
1245
- expect(last_response.body).to eq('id is empty, name is empty')
1246
- end
1247
- end
1248
-
1249
- context 'when parameter has the same validator as a group' do
1250
- before do
1251
- get 'test', id: 'id', address: ''
1252
- end
1253
-
1254
- it 'returns a successful response' do
1255
- expect(last_response.status).to eq(200)
1256
- end
1257
-
1258
- it 'prioritizes parameter validation over group validation' do
1259
- expect(last_response.body).not_to include('address is empty')
1260
- end
1261
- end
1262
- end
1263
-
1264
- context 'with types' do
1265
- before do
1266
- subject.params do
1267
- with(type: Date) do
1268
- requires :created_at
1269
- end
1270
- end
1271
- subject.get('test') { params[:created_at] }
1272
- end
1273
-
1274
- context 'when invalid date provided' do
1275
- before do
1276
- get 'test', created_at: 'not_a_date'
1277
- end
1278
-
1279
- it 'responds with HTTP error' do
1280
- expect(last_response.status).to eq(400)
1281
- end
1282
-
1283
- it 'returns a validation error' do
1284
- expect(last_response.body).to eq('created_at is invalid')
1285
- end
1286
- end
1287
-
1288
- context 'when created_at receives a valid date' do
1289
- before do
1290
- get 'test', created_at: '2016-01-01'
1291
- end
1292
-
1293
- it 'returns a successful response' do
1294
- expect(last_response.status).to eq(200)
1295
- end
1296
-
1297
- it 'returns a date' do
1298
- expect(last_response.body).to eq('2016-01-01')
1299
- end
1300
- end
1301
- end
1302
-
1303
- context 'with several group attributes' do
1304
- before do
1305
- subject.params do
1306
- with(values: [1]) do
1307
- requires :id, type: Integer
1308
- end
1309
-
1310
- with(allow_blank: false) do
1311
- optional :address, type: String
1312
- end
1313
-
1314
- requires :name
1315
- end
1316
- subject.get('test')
1317
- end
1318
-
1319
- context 'when data is invalid' do
1320
- before do
1321
- get 'test', id: 2, address: ''
1322
- end
1323
-
1324
- it 'responds with HTTP error' do
1325
- expect(last_response.status).to eq(400)
1326
- end
1327
-
1328
- it 'returns a validation error' do
1329
- expect(last_response.body).to eq('id does not have a valid value, address is empty, name is missing')
1330
- end
1331
- end
1332
-
1333
- context 'when correct data is provided' do
1334
- before do
1335
- get 'test', id: 1, address: 'Some street', name: 'John'
1336
- end
1337
-
1338
- it 'returns a successful response' do
1339
- expect(last_response.status).to eq(200)
1340
- end
1341
- end
1342
- end
1343
-
1344
- context 'with nested groups' do
1345
- before do
1346
- subject.params do
1347
- with(type: Integer) do
1348
- requires :id
1349
-
1350
- with(type: Date) do
1351
- requires :created_at
1352
- optional :updated_at
1353
- end
1354
- end
1355
- end
1356
- subject.get('test')
1357
- end
1358
-
1359
- context 'when data is invalid' do
1360
- before do
1361
- get 'test', id: 'wrong', created_at: 'not_a_date', updated_at: '2016-01-01'
1362
- end
1363
-
1364
- it 'responds with HTTP error' do
1365
- expect(last_response.status).to eq(400)
1366
- end
1367
-
1368
- it 'returns a validation error' do
1369
- expect(last_response.body).to eq('id is invalid, created_at is invalid')
1370
- end
1371
- end
1372
-
1373
- context 'when correct data is provided' do
1374
- before do
1375
- get 'test', id: 1, created_at: '2016-01-01'
1376
- end
1377
-
1378
- it 'returns a successful response' do
1379
- expect(last_response.status).to eq(200)
1380
- end
1381
- end
1382
- end
1383
- end
1384
-
1385
- context 'with exactly_one_of validation for optional parameters within an Hash param' do
1386
- before do
1387
- subject.params do
1388
- optional :memo, type: Hash do
1389
- optional :text, type: String
1390
- optional :custom_body, type: Hash, coerce_with: JSON
1391
- exactly_one_of :text, :custom_body
1392
- end
1393
- end
1394
- subject.get('test')
1395
- end
1396
-
1397
- context 'when correct data is provided' do
1398
- it 'returns a successful response' do
1399
- get 'test', memo: {}
1400
- expect(last_response.status).to eq(200)
1401
-
1402
- get 'test', memo: { text: 'HOGEHOGE' }
1403
- expect(last_response.status).to eq(200)
1404
-
1405
- get 'test', memo: { custom_body: '{ "xxx": "yyy" }' }
1406
- expect(last_response.status).to eq(200)
1407
- end
1408
- end
1409
-
1410
- context 'when invalid data is provided' do
1411
- it 'returns a failure response' do
1412
- get 'test', memo: { text: 'HOGEHOGE', custom_body: '{ "xxx": "yyy" }' }
1413
- expect(last_response.status).to eq(400)
1414
-
1415
- get 'test', memo: '{ "custom_body": "HOGE" }'
1416
- expect(last_response.status).to eq(400)
1417
- end
1418
- end
1419
- end
1420
- end