grape 1.7.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +30 -25
  5. data/UPGRADING.md +35 -0
  6. data/grape.gemspec +3 -6
  7. data/lib/grape/api.rb +2 -2
  8. data/lib/grape/content_types.rb +2 -8
  9. data/lib/grape/dsl/desc.rb +1 -1
  10. data/lib/grape/dsl/inside_route.rb +11 -11
  11. data/lib/grape/dsl/request_response.rb +2 -1
  12. data/lib/grape/dsl/settings.rb +2 -6
  13. data/lib/grape/endpoint.rb +28 -18
  14. data/lib/grape/error_formatter/base.rb +1 -1
  15. data/lib/grape/exceptions/base.rb +2 -2
  16. data/lib/grape/exceptions/missing_group_type.rb +1 -6
  17. data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
  18. data/lib/grape/exceptions/validation_errors.rb +1 -6
  19. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
  20. data/lib/grape/extensions/hash.rb +4 -7
  21. data/lib/grape/extensions/hashie/mash.rb +3 -3
  22. data/lib/grape/formatter/serializable_hash.rb +7 -7
  23. data/lib/grape/http/headers.rb +12 -2
  24. data/lib/grape/middleware/auth/base.rb +1 -1
  25. data/lib/grape/middleware/auth/strategies.rb +1 -2
  26. data/lib/grape/middleware/error.rb +5 -5
  27. data/lib/grape/middleware/formatter.rb +6 -6
  28. data/lib/grape/middleware/versioner/header.rb +11 -19
  29. data/lib/grape/railtie.rb +9 -0
  30. data/lib/grape/request.rb +8 -2
  31. data/lib/grape/router/route.rb +1 -3
  32. data/lib/grape/util/lazy_value.rb +3 -11
  33. data/lib/grape/util/strict_hash_configuration.rb +3 -4
  34. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  35. data/lib/grape/validations/params_scope.rb +8 -2
  36. data/lib/grape/validations/single_attribute_iterator.rb +3 -1
  37. data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
  38. data/lib/grape/validations/validators/base.rb +9 -20
  39. data/lib/grape/validations/validators/default_validator.rb +2 -20
  40. data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
  41. data/lib/grape/validations/validators/values_validator.rb +14 -5
  42. data/lib/grape/version.rb +1 -1
  43. data/lib/grape.rb +26 -5
  44. metadata +13 -253
  45. data/lib/grape/config.rb +0 -34
  46. data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
  47. data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
  48. data/spec/grape/api/custom_validations_spec.rb +0 -256
  49. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  50. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  51. data/spec/grape/api/documentation_spec.rb +0 -59
  52. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  53. data/spec/grape/api/instance_spec.rb +0 -103
  54. data/spec/grape/api/invalid_format_spec.rb +0 -45
  55. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  56. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  57. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  58. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  59. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  60. data/spec/grape/api/recognize_path_spec.rb +0 -21
  61. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  62. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  63. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  64. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  65. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  66. data/spec/grape/api_remount_spec.rb +0 -473
  67. data/spec/grape/api_spec.rb +0 -4347
  68. data/spec/grape/config_spec.rb +0 -17
  69. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  70. data/spec/grape/dsl/desc_spec.rb +0 -101
  71. data/spec/grape/dsl/headers_spec.rb +0 -62
  72. data/spec/grape/dsl/helpers_spec.rb +0 -100
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -535
  74. data/spec/grape/dsl/logger_spec.rb +0 -24
  75. data/spec/grape/dsl/middleware_spec.rb +0 -60
  76. data/spec/grape/dsl/parameters_spec.rb +0 -180
  77. data/spec/grape/dsl/request_response_spec.rb +0 -206
  78. data/spec/grape/dsl/routing_spec.rb +0 -275
  79. data/spec/grape/dsl/settings_spec.rb +0 -261
  80. data/spec/grape/dsl/validations_spec.rb +0 -55
  81. data/spec/grape/endpoint/declared_spec.rb +0 -846
  82. data/spec/grape/endpoint_spec.rb +0 -1085
  83. data/spec/grape/entity_spec.rb +0 -336
  84. data/spec/grape/exceptions/base_spec.rb +0 -81
  85. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
  86. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  87. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  88. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  89. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  90. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
  91. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  92. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  93. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  94. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  95. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
  96. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  97. data/spec/grape/exceptions/validation_spec.rb +0 -19
  98. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  99. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  100. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  101. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  102. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  103. data/spec/grape/integration/rack_spec.rb +0 -51
  104. data/spec/grape/loading_spec.rb +0 -44
  105. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  106. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  107. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  108. data/spec/grape/middleware/base_spec.rb +0 -221
  109. data/spec/grape/middleware/error_spec.rb +0 -85
  110. data/spec/grape/middleware/exception_spec.rb +0 -294
  111. data/spec/grape/middleware/formatter_spec.rb +0 -461
  112. data/spec/grape/middleware/globals_spec.rb +0 -30
  113. data/spec/grape/middleware/stack_spec.rb +0 -155
  114. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  115. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  116. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  117. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  118. data/spec/grape/middleware/versioner_spec.rb +0 -21
  119. data/spec/grape/named_api_spec.rb +0 -19
  120. data/spec/grape/parser_spec.rb +0 -86
  121. data/spec/grape/path_spec.rb +0 -252
  122. data/spec/grape/presenters/presenter_spec.rb +0 -71
  123. data/spec/grape/request_spec.rb +0 -136
  124. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  125. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  126. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  127. data/spec/grape/util/stackable_values_spec.rb +0 -128
  128. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  129. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  130. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  131. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
  132. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  133. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
  134. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  135. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  136. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  137. data/spec/grape/validations/types_spec.rb +0 -111
  138. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  139. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  140. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  141. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  142. data/spec/grape/validations/validators/default_spec.rb +0 -463
  143. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  144. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  145. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  146. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  147. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  148. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  149. data/spec/grape/validations/validators/values_spec.rb +0 -696
  150. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  151. data/spec/grape/validations_spec.rb +0 -2029
  152. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  153. data/spec/integration/multi_json/json_spec.rb +0 -7
  154. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  155. data/spec/shared/versioning_examples.rb +0 -215
  156. data/spec/spec_helper.rb +0 -52
  157. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  158. data/spec/support/chunks.rb +0 -14
  159. data/spec/support/content_type_helpers.rb +0 -15
  160. data/spec/support/endpoint_faker.rb +0 -25
  161. data/spec/support/file_streamer.rb +0 -13
  162. data/spec/support/integer_helpers.rb +0 -13
  163. data/spec/support/versioned_helpers.rb +0 -55
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::SingleAttributeIterator do
4
- describe '#each' do
5
- subject(:iterator) { described_class.new(validator, scope, params) }
6
-
7
- let(:scope) { Grape::Validations::ParamsScope.new(api: Class.new(Grape::API)) }
8
- let(:validator) { double(attrs: %i[first second]) }
9
-
10
- context 'when params is a hash' do
11
- let(:params) do
12
- { first: 'string', second: 'string' }
13
- end
14
-
15
- it 'yields params and every single attribute from the list' do
16
- expect { |b| iterator.each(&b) }
17
- .to yield_successive_args([params, :first, false, false], [params, :second, false, false])
18
- end
19
- end
20
-
21
- context 'when params is an array' do
22
- let(:params) do
23
- [{ first: 'string1', second: 'string1' }, { first: 'string2', second: 'string2' }]
24
- end
25
-
26
- it 'yields every single attribute from the list for each of the array elements' do
27
- expect { |b| iterator.each(&b) }.to yield_successive_args(
28
- [params[0], :first, false, false], [params[0], :second, false, false],
29
- [params[1], :first, false, false], [params[1], :second, false, false]
30
- )
31
- end
32
-
33
- context 'empty values' do
34
- let(:params) { [{}, '', 10] }
35
-
36
- it 'marks params with empty values' do
37
- expect { |b| iterator.each(&b) }.to yield_successive_args(
38
- [params[0], :first, true, false], [params[0], :second, true, false],
39
- [params[1], :first, true, false], [params[1], :second, true, false],
40
- [params[2], :first, false, false], [params[2], :second, false, false]
41
- )
42
- end
43
- end
44
-
45
- context 'when missing optional value' do
46
- let(:params) { [Grape::DSL::Parameters::EmptyOptionalValue, 10] }
47
-
48
- it 'marks params with skipped values' do
49
- expect { |b| iterator.each(&b) }.to yield_successive_args(
50
- [params[0], :first, false, true], [params[0], :second, false, true],
51
- [params[1], :first, false, false], [params[1], :second, false, false]
52
- )
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Types::ArrayCoercer do
4
- subject { described_class.new(type) }
5
-
6
- describe '#call' do
7
- context 'an array of primitives' do
8
- let(:type) { Array[String] }
9
-
10
- it 'coerces elements in the array' do
11
- expect(subject.call([10, 20])).to eq(%w[10 20])
12
- end
13
- end
14
-
15
- context 'an array of arrays' do
16
- let(:type) { Array[Array[Integer]] }
17
-
18
- it 'coerces elements in the nested array' do
19
- expect(subject.call([%w[10 20]])).to eq([[10, 20]])
20
- expect(subject.call([['10'], ['20']])).to eq([[10], [20]])
21
- end
22
- end
23
-
24
- context 'an array of sets' do
25
- let(:type) { Array[Set[Integer]] }
26
-
27
- it 'coerces elements in the nested set' do
28
- expect(subject.call([%w[10 20]])).to eq([Set[10, 20]])
29
- expect(subject.call([['10'], ['20']])).to eq([Set[10], Set[20]])
30
- end
31
- end
32
- end
33
- end
@@ -1,150 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Types::PrimitiveCoercer do
4
- subject { described_class.new(type, strict) }
5
-
6
- let(:strict) { false }
7
-
8
- describe '#call' do
9
- context 'BigDecimal' do
10
- let(:type) { BigDecimal }
11
-
12
- it 'coerces to BigDecimal' do
13
- expect(subject.call(5)).to eq(BigDecimal('5'))
14
- end
15
-
16
- it 'coerces an empty string to nil' do
17
- expect(subject.call('')).to be_nil
18
- end
19
- end
20
-
21
- context 'Boolean' do
22
- let(:type) { Grape::API::Boolean }
23
-
24
- [true, 'true', 1].each do |val|
25
- it "coerces '#{val}' to true" do
26
- expect(subject.call(val)).to be(true)
27
- end
28
- end
29
-
30
- [false, 'false', 0].each do |val|
31
- it "coerces '#{val}' to false" do
32
- expect(subject.call(val)).to be(false)
33
- end
34
- end
35
-
36
- it 'returns an error when the given value cannot be coerced' do
37
- expect(subject.call(123)).to be_instance_of(Grape::Validations::Types::InvalidValue)
38
- end
39
-
40
- it 'coerces an empty string to nil' do
41
- expect(subject.call('')).to be_nil
42
- end
43
- end
44
-
45
- context 'DateTime' do
46
- let(:type) { DateTime }
47
-
48
- it 'coerces an empty string to nil' do
49
- expect(subject.call('')).to be_nil
50
- end
51
- end
52
-
53
- context 'Float' do
54
- let(:type) { Float }
55
-
56
- it 'coerces an empty string to nil' do
57
- expect(subject.call('')).to be_nil
58
- end
59
- end
60
-
61
- context 'Integer' do
62
- let(:type) { Integer }
63
-
64
- it 'coerces an empty string to nil' do
65
- expect(subject.call('')).to be_nil
66
- end
67
-
68
- it 'accepts non-nil value' do
69
- expect(subject.call(42)).to be_a(Integer)
70
- end
71
- end
72
-
73
- context 'Numeric' do
74
- let(:type) { Numeric }
75
-
76
- it 'coerces an empty string to nil' do
77
- expect(subject.call('')).to be_nil
78
- end
79
-
80
- it 'accepts a non-nil value' do
81
- expect(subject.call(42)).to be_a(Numeric) # in fact Integer
82
- end
83
- end
84
-
85
- context 'Time' do
86
- let(:type) { Time }
87
-
88
- it 'coerces an empty string to nil' do
89
- expect(subject.call('')).to be_nil
90
- end
91
- end
92
-
93
- context 'String' do
94
- let(:type) { String }
95
-
96
- it 'coerces to String' do
97
- expect(subject.call(10)).to eq('10')
98
- end
99
-
100
- it 'does not coerce an empty string to nil' do
101
- expect(subject.call('')).to eq('')
102
- end
103
- end
104
-
105
- context 'Symbol' do
106
- let(:type) { Symbol }
107
-
108
- it 'coerces an empty string to nil' do
109
- expect(subject.call('')).to be_nil
110
- end
111
- end
112
-
113
- context 'a type unknown in Dry-types' do
114
- let(:type) { Complex }
115
-
116
- it 'raises error on init' do
117
- expect(DryTypes::Params.constants).not_to include(type.name.to_sym)
118
- expect { subject }.to raise_error(/type Complex should support coercion/)
119
- end
120
- end
121
-
122
- context 'the strict mode' do
123
- let(:strict) { true }
124
-
125
- context 'Boolean' do
126
- let(:type) { Grape::API::Boolean }
127
-
128
- it 'returns an error when the given value is not Boolean' do
129
- expect(subject.call(1)).to be_instance_of(Grape::Validations::Types::InvalidValue)
130
- end
131
-
132
- it 'returns a value as it is when the given value is Boolean' do
133
- expect(subject.call(true)).to be(true)
134
- end
135
- end
136
-
137
- context 'BigDecimal' do
138
- let(:type) { BigDecimal }
139
-
140
- it 'returns an error when the given value is not BigDecimal' do
141
- expect(subject.call(1)).to be_instance_of(Grape::Validations::Types::InvalidValue)
142
- end
143
-
144
- it 'returns a value as it is when the given value is BigDecimal' do
145
- expect(subject.call(BigDecimal('0'))).to eq(BigDecimal('0'))
146
- end
147
- end
148
- end
149
- end
150
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Types::SetCoercer do
4
- subject { described_class.new(type) }
5
-
6
- describe '#call' do
7
- context 'a set of primitives' do
8
- let(:type) { Set[String] }
9
-
10
- it 'coerces elements to the set' do
11
- expect(subject.call([10, 20])).to eq(Set['10', '20'])
12
- end
13
- end
14
-
15
- context 'a set of sets' do
16
- let(:type) { Set[Set[Integer]] }
17
-
18
- it 'coerces elements in the nested set' do
19
- expect(subject.call([%w[10 20]])).to eq(Set[Set[10, 20]])
20
- expect(subject.call([['10'], ['20']])).to eq(Set[Set[10], Set[20]])
21
- end
22
- end
23
-
24
- context 'a set of sets of arrays' do
25
- let(:type) { Set[Set[Array[Integer]]] }
26
-
27
- it 'coerces elements in the nested set' do
28
- expect(subject.call([[['10'], ['20']]])).to eq(Set[Set[Array[10], Array[20]]])
29
- end
30
- end
31
- end
32
- end
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Types do
4
- module TypesSpec
5
- class FooType
6
- def self.parse(_); end
7
- end
8
-
9
- class BarType
10
- def self.parse; end
11
- end
12
- end
13
-
14
- describe '::primitive?' do
15
- [
16
- Integer, Float, Numeric, BigDecimal,
17
- Grape::API::Boolean, String, Symbol,
18
- Date, DateTime, Time
19
- ].each do |type|
20
- it "recognizes #{type} as a primitive" do
21
- expect(described_class).to be_primitive(type)
22
- end
23
- end
24
-
25
- it 'identifies unknown types' do
26
- expect(described_class).not_to be_primitive(Object)
27
- expect(described_class).not_to be_primitive(TypesSpec::FooType)
28
- end
29
- end
30
-
31
- describe '::structure?' do
32
- [
33
- Hash, Array, Set
34
- ].each do |type|
35
- it "recognizes #{type} as a structure" do
36
- expect(described_class).to be_structure(type)
37
- end
38
- end
39
- end
40
-
41
- describe '::special?' do
42
- [
43
- JSON, Array[JSON], File, Rack::Multipart::UploadedFile
44
- ].each do |type|
45
- it "provides special handling for #{type.inspect}" do
46
- expect(described_class).to be_special(type)
47
- end
48
- end
49
- end
50
-
51
- describe 'special types' do
52
- subject { described_class::SPECIAL[type] }
53
-
54
- context 'when JSON' do
55
- let(:type) { JSON }
56
-
57
- it { is_expected.to eq(Grape::Validations::Types::Json) }
58
- end
59
-
60
- context 'when Array[JSON]' do
61
- let(:type) { Array[JSON] }
62
-
63
- it { is_expected.to eq(Grape::Validations::Types::JsonArray) }
64
- end
65
-
66
- context 'when File' do
67
- let(:type) { File }
68
-
69
- it { is_expected.to eq(Grape::Validations::Types::File) }
70
- end
71
-
72
- context 'when Rack::Multipart::UploadedFile' do
73
- let(:type) { Rack::Multipart::UploadedFile }
74
-
75
- it { is_expected.to eq(Grape::Validations::Types::File) }
76
- end
77
- end
78
-
79
- describe '::custom?' do
80
- it 'returns false if the type does not respond to :parse' do
81
- expect(described_class).not_to be_custom(Object)
82
- end
83
-
84
- it 'returns true if the type responds to :parse with one argument' do
85
- expect(described_class).to be_custom(TypesSpec::FooType)
86
- end
87
-
88
- it 'returns false if the type\'s #parse method takes other than one argument' do
89
- expect(described_class).not_to be_custom(TypesSpec::BarType)
90
- end
91
- end
92
-
93
- describe '::build_coercer' do
94
- it 'has internal cache variables' do
95
- expect(described_class.instance_variable_get(:@__cache)).to be_a(Hash)
96
- expect(described_class.instance_variable_get(:@__cache_write_lock)).to be_a(Mutex)
97
- end
98
-
99
- it 'caches the result of the build_coercer method' do
100
- original_cache = described_class.instance_variable_get(:@__cache)
101
- described_class.instance_variable_set(:@__cache, {})
102
-
103
- a_coercer = described_class.build_coercer(Array[String])
104
- b_coercer = described_class.build_coercer(Array[String])
105
-
106
- expect(a_coercer.object_id).to eq(b_coercer.object_id)
107
-
108
- described_class.instance_variable_set(:@__cache, original_cache)
109
- end
110
- end
111
- end
@@ -1,162 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Validators::AllOrNoneOfValidator do
4
- let_it_be(:app) do
5
- Class.new(Grape::API) do
6
- rescue_from Grape::Exceptions::ValidationErrors do |e|
7
- error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
8
- end
9
-
10
- params do
11
- optional :beer, :wine, type: Grape::API::Boolean
12
- all_or_none_of :beer, :wine
13
- end
14
- post do
15
- end
16
-
17
- params do
18
- optional :beer, :wine, :other, type: Grape::API::Boolean
19
- all_or_none_of :beer, :wine
20
- end
21
- post 'mixed-params' do
22
- end
23
-
24
- params do
25
- optional :beer, :wine, type: Grape::API::Boolean
26
- all_or_none_of :beer, :wine, message: 'choose all or none'
27
- end
28
- post '/custom-message' do
29
- end
30
-
31
- params do
32
- requires :item, type: Hash do
33
- optional :beer, :wine, type: Grape::API::Boolean
34
- all_or_none_of :beer, :wine
35
- end
36
- end
37
- post '/nested-hash' do
38
- end
39
-
40
- params do
41
- requires :items, type: Array do
42
- optional :beer, :wine, type: Grape::API::Boolean
43
- all_or_none_of :beer, :wine
44
- end
45
- end
46
- post '/nested-array' do
47
- end
48
-
49
- params do
50
- requires :items, type: Array do
51
- requires :nested_items, type: Array do
52
- optional :beer, :wine, type: Grape::API::Boolean
53
- all_or_none_of :beer, :wine
54
- end
55
- end
56
- end
57
- post '/deeply-nested-array' do
58
- end
59
- end
60
- end
61
-
62
- describe '#validate!' do
63
- subject(:validate) { post path, params }
64
-
65
- context 'when all restricted params are present' do
66
- let(:path) { '/' }
67
- let(:params) { { beer: true, wine: true } }
68
-
69
- it 'does not return a validation error' do
70
- validate
71
- expect(last_response.status).to eq 201
72
- end
73
-
74
- context 'mixed with other params' do
75
- let(:path) { '/mixed-params' }
76
- let(:params) { { beer: true, wine: true, other: true } }
77
-
78
- it 'does not return a validation error' do
79
- validate
80
- expect(last_response.status).to eq 201
81
- end
82
- end
83
- end
84
-
85
- context 'when a subset of restricted params are present' do
86
- let(:path) { '/' }
87
- let(:params) { { beer: true } }
88
-
89
- it 'returns a validation error' do
90
- validate
91
- expect(last_response.status).to eq 400
92
- expect(JSON.parse(last_response.body)).to eq(
93
- 'beer,wine' => ['provide all or none of parameters']
94
- )
95
- end
96
- end
97
-
98
- context 'when custom message is specified' do
99
- let(:path) { '/custom-message' }
100
- let(:params) { { beer: true } }
101
-
102
- it 'returns a validation error' do
103
- validate
104
- expect(last_response.status).to eq 400
105
- expect(JSON.parse(last_response.body)).to eq(
106
- 'beer,wine' => ['choose all or none']
107
- )
108
- end
109
- end
110
-
111
- context 'when no restricted params are present' do
112
- let(:path) { '/' }
113
- let(:params) { { somethingelse: true } }
114
-
115
- it 'does not return a validation error' do
116
- validate
117
- expect(last_response.status).to eq 201
118
- end
119
- end
120
-
121
- context 'when restricted params are nested inside required hash' do
122
- let(:path) { '/nested-hash' }
123
- let(:params) { { item: { beer: true } } }
124
-
125
- it 'returns a validation error with full names of the params' do
126
- validate
127
- expect(last_response.status).to eq 400
128
- expect(JSON.parse(last_response.body)).to eq(
129
- 'item[beer],item[wine]' => ['provide all or none of parameters']
130
- )
131
- end
132
- end
133
-
134
- context 'when mutually exclusive params are nested inside array' do
135
- let(:path) { '/nested-array' }
136
- let(:params) { { items: [{ beer: true, wine: true }, { wine: true }] } }
137
-
138
- it 'returns a validation error with full names of the params' do
139
- validate
140
- expect(last_response.status).to eq 400
141
- expect(JSON.parse(last_response.body)).to eq(
142
- 'items[1][beer],items[1][wine]' => ['provide all or none of parameters']
143
- )
144
- end
145
- end
146
-
147
- context 'when mutually exclusive params are deeply nested' do
148
- let(:path) { '/deeply-nested-array' }
149
- let(:params) { { items: [{ nested_items: [{ beer: true }] }] } }
150
-
151
- it 'returns a validation error with full names of the params' do
152
- validate
153
- expect(last_response.status).to eq 400
154
- expect(JSON.parse(last_response.body)).to eq(
155
- 'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => [
156
- 'provide all or none of parameters'
157
- ]
158
- )
159
- end
160
- end
161
- end
162
- end