grape 1.8.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -1
  3. data/README.md +377 -334
  4. data/UPGRADING.md +231 -6
  5. data/grape.gemspec +6 -10
  6. data/lib/grape/api/instance.rb +13 -10
  7. data/lib/grape/api.rb +17 -8
  8. data/lib/grape/content_types.rb +0 -2
  9. data/lib/grape/cookies.rb +2 -1
  10. data/lib/grape/dry_types.rb +0 -2
  11. data/lib/grape/dsl/desc.rb +23 -21
  12. data/lib/grape/dsl/headers.rb +1 -1
  13. data/lib/grape/dsl/inside_route.rb +47 -22
  14. data/lib/grape/dsl/parameters.rb +4 -3
  15. data/lib/grape/dsl/routing.rb +20 -4
  16. data/lib/grape/dsl/validations.rb +13 -0
  17. data/lib/grape/endpoint.rb +15 -10
  18. data/lib/grape/{util/env.rb → env.rb} +0 -5
  19. data/lib/grape/error_formatter/txt.rb +11 -10
  20. data/lib/grape/exceptions/base.rb +3 -3
  21. data/lib/grape/exceptions/missing_group_type.rb +1 -1
  22. data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
  23. data/lib/grape/exceptions/validation.rb +0 -2
  24. data/lib/grape/exceptions/validation_array_errors.rb +1 -0
  25. data/lib/grape/exceptions/validation_errors.rb +1 -3
  26. data/lib/grape/extensions/hash.rb +5 -1
  27. data/lib/grape/http/headers.rb +18 -24
  28. data/lib/grape/{util/json.rb → json.rb} +1 -3
  29. data/lib/grape/locale/en.yml +3 -0
  30. data/lib/grape/middleware/auth/base.rb +0 -2
  31. data/lib/grape/middleware/auth/dsl.rb +0 -2
  32. data/lib/grape/middleware/auth/strategies.rb +1 -2
  33. data/lib/grape/middleware/base.rb +0 -2
  34. data/lib/grape/middleware/error.rb +55 -50
  35. data/lib/grape/middleware/formatter.rb +21 -18
  36. data/lib/grape/middleware/globals.rb +1 -3
  37. data/lib/grape/middleware/stack.rb +2 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  39. data/lib/grape/middleware/versioner/header.rb +17 -163
  40. data/lib/grape/middleware/versioner/param.rb +2 -4
  41. data/lib/grape/middleware/versioner/path.rb +1 -3
  42. data/lib/grape/namespace.rb +3 -4
  43. data/lib/grape/path.rb +24 -29
  44. data/lib/grape/railtie.rb +9 -0
  45. data/lib/grape/request.rb +3 -5
  46. data/lib/grape/router/base_route.rb +39 -0
  47. data/lib/grape/router/greedy_route.rb +20 -0
  48. data/lib/grape/router/pattern.rb +39 -30
  49. data/lib/grape/router/route.rb +22 -59
  50. data/lib/grape/router.rb +30 -36
  51. data/lib/grape/util/accept/header.rb +19 -0
  52. data/lib/grape/util/accept_header_handler.rb +105 -0
  53. data/lib/grape/util/base_inheritable.rb +4 -4
  54. data/lib/grape/util/cache.rb +0 -3
  55. data/lib/grape/util/endpoint_configuration.rb +1 -1
  56. data/lib/grape/util/header.rb +13 -0
  57. data/lib/grape/util/inheritable_values.rb +0 -2
  58. data/lib/grape/util/lazy/block.rb +29 -0
  59. data/lib/grape/util/lazy/object.rb +45 -0
  60. data/lib/grape/util/lazy/value.rb +38 -0
  61. data/lib/grape/util/lazy/value_array.rb +21 -0
  62. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  63. data/lib/grape/util/lazy/value_hash.rb +21 -0
  64. data/lib/grape/util/media_type.rb +70 -0
  65. data/lib/grape/util/reverse_stackable_values.rb +1 -6
  66. data/lib/grape/util/stackable_values.rb +1 -6
  67. data/lib/grape/util/strict_hash_configuration.rb +3 -3
  68. data/lib/grape/validations/attributes_doc.rb +38 -36
  69. data/lib/grape/validations/contract_scope.rb +71 -0
  70. data/lib/grape/validations/params_scope.rb +10 -9
  71. data/lib/grape/validations/types/array_coercer.rb +0 -2
  72. data/lib/grape/validations/types/build_coercer.rb +69 -71
  73. data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
  74. data/lib/grape/validations/types/json.rb +0 -2
  75. data/lib/grape/validations/types/primitive_coercer.rb +0 -2
  76. data/lib/grape/validations/types/set_coercer.rb +0 -3
  77. data/lib/grape/validations/types.rb +0 -3
  78. data/lib/grape/validations/validators/base.rb +2 -1
  79. data/lib/grape/validations/validators/default_validator.rb +5 -1
  80. data/lib/grape/validations/validators/length_validator.rb +42 -0
  81. data/lib/grape/validations/validators/values_validator.rb +8 -3
  82. data/lib/grape/validations.rb +3 -7
  83. data/lib/grape/version.rb +1 -1
  84. data/lib/grape/{util/xml.rb → xml.rb} +1 -1
  85. data/lib/grape.rb +38 -269
  86. metadata +33 -274
  87. data/lib/grape/eager_load.rb +0 -20
  88. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  89. data/lib/grape/router/attribute_translator.rb +0 -63
  90. data/lib/grape/util/lazy_block.rb +0 -27
  91. data/lib/grape/util/lazy_object.rb +0 -43
  92. data/lib/grape/util/lazy_value.rb +0 -91
  93. data/spec/grape/api/custom_validations_spec.rb +0 -213
  94. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  95. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  96. data/spec/grape/api/documentation_spec.rb +0 -59
  97. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  98. data/spec/grape/api/instance_spec.rb +0 -103
  99. data/spec/grape/api/invalid_format_spec.rb +0 -45
  100. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  101. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  102. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  103. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  104. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  105. data/spec/grape/api/recognize_path_spec.rb +0 -21
  106. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  107. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  108. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  109. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  110. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  111. data/spec/grape/api_remount_spec.rb +0 -509
  112. data/spec/grape/api_spec.rb +0 -4356
  113. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  114. data/spec/grape/dsl/desc_spec.rb +0 -98
  115. data/spec/grape/dsl/headers_spec.rb +0 -62
  116. data/spec/grape/dsl/helpers_spec.rb +0 -100
  117. data/spec/grape/dsl/inside_route_spec.rb +0 -531
  118. data/spec/grape/dsl/logger_spec.rb +0 -24
  119. data/spec/grape/dsl/middleware_spec.rb +0 -60
  120. data/spec/grape/dsl/parameters_spec.rb +0 -180
  121. data/spec/grape/dsl/request_response_spec.rb +0 -225
  122. data/spec/grape/dsl/routing_spec.rb +0 -275
  123. data/spec/grape/dsl/settings_spec.rb +0 -261
  124. data/spec/grape/dsl/validations_spec.rb +0 -55
  125. data/spec/grape/endpoint/declared_spec.rb +0 -846
  126. data/spec/grape/endpoint_spec.rb +0 -1085
  127. data/spec/grape/entity_spec.rb +0 -336
  128. data/spec/grape/exceptions/base_spec.rb +0 -81
  129. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
  130. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  131. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  132. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  133. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  134. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
  135. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  136. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  137. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  138. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  139. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
  140. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  141. data/spec/grape/exceptions/validation_spec.rb +0 -19
  142. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  143. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  144. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  145. data/spec/grape/grape_spec.rb +0 -9
  146. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  147. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  148. data/spec/grape/integration/rack_spec.rb +0 -51
  149. data/spec/grape/loading_spec.rb +0 -44
  150. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  151. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  152. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  153. data/spec/grape/middleware/base_spec.rb +0 -221
  154. data/spec/grape/middleware/error_spec.rb +0 -85
  155. data/spec/grape/middleware/exception_spec.rb +0 -294
  156. data/spec/grape/middleware/formatter_spec.rb +0 -461
  157. data/spec/grape/middleware/globals_spec.rb +0 -30
  158. data/spec/grape/middleware/stack_spec.rb +0 -155
  159. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  160. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  161. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  162. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  163. data/spec/grape/middleware/versioner_spec.rb +0 -21
  164. data/spec/grape/named_api_spec.rb +0 -19
  165. data/spec/grape/parser_spec.rb +0 -86
  166. data/spec/grape/path_spec.rb +0 -252
  167. data/spec/grape/presenters/presenter_spec.rb +0 -71
  168. data/spec/grape/request_spec.rb +0 -126
  169. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  170. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  171. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  172. data/spec/grape/util/stackable_values_spec.rb +0 -128
  173. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  174. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  175. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  176. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
  177. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  178. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
  179. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  180. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  181. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  182. data/spec/grape/validations/types_spec.rb +0 -111
  183. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  184. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  185. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  186. data/spec/grape/validations/validators/base_spec.rb +0 -38
  187. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  188. data/spec/grape/validations/validators/default_spec.rb +0 -463
  189. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  190. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  191. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  192. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  193. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  194. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  195. data/spec/grape/validations/validators/values_spec.rb +0 -733
  196. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  197. data/spec/grape/validations_spec.rb +0 -2030
  198. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  199. data/spec/integration/multi_json/json_spec.rb +0 -7
  200. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  201. data/spec/shared/deprecated_class_examples.rb +0 -16
  202. data/spec/shared/versioning_examples.rb +0 -215
  203. data/spec/spec_helper.rb +0 -52
  204. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  205. data/spec/support/chunks.rb +0 -14
  206. data/spec/support/content_type_helpers.rb +0 -15
  207. data/spec/support/endpoint_faker.rb +0 -25
  208. data/spec/support/file_streamer.rb +0 -13
  209. data/spec/support/integer_helpers.rb +0 -13
  210. 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