grape 1.3.0 → 1.5.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +119 -1
- data/LICENSE +1 -1
- data/README.md +123 -29
- data/UPGRADING.md +265 -39
- data/lib/grape/api/instance.rb +32 -31
- data/lib/grape/api.rb +5 -5
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/helpers.rb +2 -1
- data/lib/grape/dsl/inside_route.rb +77 -43
- data/lib/grape/dsl/parameters.rb +12 -8
- data/lib/grape/dsl/routing.rb +12 -11
- data/lib/grape/dsl/validations.rb +18 -1
- data/lib/grape/eager_load.rb +1 -1
- data/lib/grape/endpoint.rb +8 -6
- data/lib/grape/exceptions/base.rb +0 -4
- data/lib/grape/exceptions/validation.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +12 -13
- data/lib/grape/http/headers.rb +26 -0
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/base.rb +4 -5
- data/lib/grape/middleware/error.rb +11 -13
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/stack.rb +10 -2
- data/lib/grape/middleware/versioner/header.rb +4 -4
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +12 -2
- data/lib/grape/path.rb +13 -3
- data/lib/grape/request.rb +13 -8
- data/lib/grape/router/attribute_translator.rb +26 -5
- data/lib/grape/router/pattern.rb +17 -16
- data/lib/grape/router/route.rb +5 -24
- data/lib/grape/router.rb +26 -30
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
- data/lib/grape/util/base_inheritable.rb +15 -8
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +1 -0
- data/lib/grape/util/reverse_stackable_values.rb +2 -0
- data/lib/grape/util/stackable_values.rb +7 -20
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +10 -8
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/array_coercer.rb +14 -5
- data/lib/grape/validations/types/build_coercer.rb +5 -8
- data/lib/grape/validations/types/custom_type_coercer.rb +16 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
- data/lib/grape/validations/types/file.rb +15 -12
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +40 -36
- data/lib/grape/validations/types/primitive_coercer.rb +15 -6
- data/lib/grape/validations/types/set_coercer.rb +6 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
- data/lib/grape/validations/types.rb +7 -9
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/as.rb +1 -1
- data/lib/grape/validations/validators/base.rb +8 -8
- data/lib/grape/validations/validators/coerce.rb +11 -15
- data/lib/grape/validations/validators/default.rb +3 -5
- data/lib/grape/validations/validators/exactly_one_of.rb +4 -2
- data/lib/grape/validations/validators/except_values.rb +1 -1
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +5 -5
- data/spec/grape/api/instance_spec.rb +50 -0
- data/spec/grape/api_remount_spec.rb +9 -4
- data/spec/grape/api_spec.rb +82 -6
- data/spec/grape/dsl/inside_route_spec.rb +182 -33
- data/spec/grape/endpoint/declared_spec.rb +601 -0
- data/spec/grape/endpoint_spec.rb +0 -521
- data/spec/grape/entity_spec.rb +7 -1
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +12 -8
- data/spec/grape/middleware/auth/strategies_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -1
- data/spec/grape/middleware/formatter_spec.rb +3 -3
- data/spec/grape/middleware/stack_spec.rb +10 -0
- data/spec/grape/path_spec.rb +4 -4
- data/spec/grape/request_spec.rb +1 -1
- data/spec/grape/validations/instance_behaivour_spec.rb +1 -1
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
- data/spec/grape/validations/params_scope_spec.rb +26 -0
- data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +1 -1
- data/spec/grape/validations/validators/coerce_spec.rb +366 -86
- data/spec/grape/validations/validators/default_spec.rb +170 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +12 -12
- data/spec/grape/validations/validators/except_values_spec.rb +1 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +298 -30
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/shared/versioning_examples.rb +20 -20
- data/spec/spec_helper.rb +3 -10
- data/spec/support/chunks.rb +14 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/versioned_helpers.rb +4 -6
- metadata +27 -10
- data/lib/grape/util/content_types.rb +0 -28
@@ -9,16 +9,23 @@ describe Grape::Validations do
|
|
9
9
|
subject
|
10
10
|
end
|
11
11
|
|
12
|
+
def declared_params
|
13
|
+
subject.namespace_stackable(:declared_params).flatten
|
14
|
+
end
|
15
|
+
|
12
16
|
describe 'params' do
|
13
17
|
context 'optional' do
|
14
|
-
|
18
|
+
before do
|
15
19
|
subject.params do
|
16
20
|
optional :a_number, regexp: /^[0-9]+$/
|
21
|
+
optional :attachment, type: File
|
17
22
|
end
|
18
23
|
subject.get '/optional' do
|
19
24
|
'optional works!'
|
20
25
|
end
|
26
|
+
end
|
21
27
|
|
28
|
+
it 'validates when params is present' do
|
22
29
|
get '/optional', a_number: 'string'
|
23
30
|
expect(last_response.status).to eq(400)
|
24
31
|
expect(last_response.body).to eq('a_number is invalid')
|
@@ -29,14 +36,7 @@ describe Grape::Validations do
|
|
29
36
|
end
|
30
37
|
|
31
38
|
it "doesn't validate when param not present" do
|
32
|
-
|
33
|
-
optional :a_number, regexp: /^[0-9]+$/
|
34
|
-
end
|
35
|
-
subject.get '/optional' do
|
36
|
-
'optional works!'
|
37
|
-
end
|
38
|
-
|
39
|
-
get '/optional'
|
39
|
+
get '/optional', a_number: nil, attachment: nil
|
40
40
|
expect(last_response.status).to eq(200)
|
41
41
|
expect(last_response.body).to eq('optional works!')
|
42
42
|
end
|
@@ -45,7 +45,7 @@ describe Grape::Validations do
|
|
45
45
|
subject.params do
|
46
46
|
optional :some_param
|
47
47
|
end
|
48
|
-
expect(
|
48
|
+
expect(declared_params).to eq([:some_param])
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -65,7 +65,7 @@ describe Grape::Validations do
|
|
65
65
|
|
66
66
|
it 'adds entity documentation to declared params' do
|
67
67
|
define_optional_using
|
68
|
-
expect(
|
68
|
+
expect(declared_params).to eq(%i[field_a field_b])
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'works when field_a and field_b are not present' do
|
@@ -112,7 +112,7 @@ describe Grape::Validations do
|
|
112
112
|
subject.params do
|
113
113
|
requires :some_param
|
114
114
|
end
|
115
|
-
expect(
|
115
|
+
expect(declared_params).to eq([:some_param])
|
116
116
|
end
|
117
117
|
|
118
118
|
it 'works when required field is present but nil' do
|
@@ -197,7 +197,7 @@ describe Grape::Validations do
|
|
197
197
|
|
198
198
|
it 'adds entity documentation to declared params' do
|
199
199
|
define_requires_all
|
200
|
-
expect(
|
200
|
+
expect(declared_params).to eq(%i[required_field optional_field])
|
201
201
|
end
|
202
202
|
|
203
203
|
it 'errors when required_field is not present' do
|
@@ -232,7 +232,7 @@ describe Grape::Validations do
|
|
232
232
|
|
233
233
|
it 'adds entity documentation to declared params' do
|
234
234
|
define_requires_none
|
235
|
-
expect(
|
235
|
+
expect(declared_params).to eq(%i[required_field optional_field])
|
236
236
|
end
|
237
237
|
|
238
238
|
it 'errors when required_field is not present' do
|
@@ -262,7 +262,7 @@ describe Grape::Validations do
|
|
262
262
|
|
263
263
|
it 'adds only the entity documentation to declared params, nothing more' do
|
264
264
|
define_requires_all
|
265
|
-
expect(
|
265
|
+
expect(declared_params).to eq(%i[required_field optional_field])
|
266
266
|
end
|
267
267
|
end
|
268
268
|
|
@@ -328,7 +328,7 @@ describe Grape::Validations do
|
|
328
328
|
requires :key
|
329
329
|
end
|
330
330
|
end
|
331
|
-
expect(
|
331
|
+
expect(declared_params).to eq([items: [:key]])
|
332
332
|
end
|
333
333
|
end
|
334
334
|
|
@@ -400,7 +400,7 @@ describe Grape::Validations do
|
|
400
400
|
requires :key
|
401
401
|
end
|
402
402
|
end
|
403
|
-
expect(
|
403
|
+
expect(declared_params).to eq([items: [:key]])
|
404
404
|
end
|
405
405
|
end
|
406
406
|
|
@@ -463,7 +463,7 @@ describe Grape::Validations do
|
|
463
463
|
requires :key
|
464
464
|
end
|
465
465
|
end
|
466
|
-
expect(
|
466
|
+
expect(declared_params).to eq([items: [:key]])
|
467
467
|
end
|
468
468
|
end
|
469
469
|
|
@@ -578,7 +578,7 @@ describe Grape::Validations do
|
|
578
578
|
# NOTE: with body parameters in json or XML or similar this
|
579
579
|
# should actually fail with: children[parents][name] is missing.
|
580
580
|
expect(last_response.status).to eq(400)
|
581
|
-
expect(last_response.body).to eq('children[1][parents] is missing')
|
581
|
+
expect(last_response.body).to eq('children[1][parents] is missing, children[0][parents][1][name] is missing, children[0][parents][1][name] is empty')
|
582
582
|
end
|
583
583
|
|
584
584
|
it 'errors when a parameter is not present in array within array' do
|
@@ -619,7 +619,7 @@ describe Grape::Validations do
|
|
619
619
|
|
620
620
|
get '/within_array', children: [name: 'Jay']
|
621
621
|
expect(last_response.status).to eq(400)
|
622
|
-
expect(last_response.body).to eq('children[0][parents] is missing')
|
622
|
+
expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing, children[0][parents][0][name] is empty')
|
623
623
|
end
|
624
624
|
|
625
625
|
it 'errors when param is not an Array' do
|
@@ -767,7 +767,7 @@ describe Grape::Validations do
|
|
767
767
|
expect(last_response.status).to eq(200)
|
768
768
|
put_with_json '/within_array', children: [name: 'Jay']
|
769
769
|
expect(last_response.status).to eq(400)
|
770
|
-
expect(last_response.body).to eq('children[0][parents] is missing')
|
770
|
+
expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing')
|
771
771
|
end
|
772
772
|
end
|
773
773
|
|
@@ -817,7 +817,7 @@ describe Grape::Validations do
|
|
817
817
|
requires :key
|
818
818
|
end
|
819
819
|
end
|
820
|
-
expect(
|
820
|
+
expect(declared_params).to eq([items: [:key]])
|
821
821
|
end
|
822
822
|
end
|
823
823
|
|
@@ -842,7 +842,7 @@ describe Grape::Validations do
|
|
842
842
|
it 'does internal validations if the outer group is present' do
|
843
843
|
get '/nested_optional_group', items: [{ key: 'foo' }]
|
844
844
|
expect(last_response.status).to eq(400)
|
845
|
-
expect(last_response.body).to eq('items[0][required_subitems] is missing')
|
845
|
+
expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
|
846
846
|
|
847
847
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
848
848
|
expect(last_response.status).to eq(200)
|
@@ -862,7 +862,7 @@ describe Grape::Validations do
|
|
862
862
|
it 'handles validation within arrays' do
|
863
863
|
get '/nested_optional_group', items: [{ key: 'foo' }]
|
864
864
|
expect(last_response.status).to eq(400)
|
865
|
-
expect(last_response.body).to eq('items[0][required_subitems] is missing')
|
865
|
+
expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
|
866
866
|
|
867
867
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
868
868
|
expect(last_response.status).to eq(200)
|
@@ -881,7 +881,275 @@ describe Grape::Validations do
|
|
881
881
|
requires(:required_subitems, type: Array) { requires :value }
|
882
882
|
end
|
883
883
|
end
|
884
|
-
expect(
|
884
|
+
expect(declared_params).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
|
885
|
+
end
|
886
|
+
|
887
|
+
context <<~DESC do
|
888
|
+
Issue occurs whenever:
|
889
|
+
* param structure with at least three levels
|
890
|
+
* 1st level item is a required Array that has >1 entry with an optional item present and >1 entry with an optional item missing
|
891
|
+
* 2nd level is an optional Array or Hash
|
892
|
+
* 3rd level is a required item (can be any type)
|
893
|
+
* additional levels do not effect the issue from occuring
|
894
|
+
DESC
|
895
|
+
|
896
|
+
it "example based off actual real world use case" do
|
897
|
+
subject.params do
|
898
|
+
requires :orders, type: Array do
|
899
|
+
requires :id, type: Integer
|
900
|
+
optional :drugs, type: Array do
|
901
|
+
requires :batches, type: Array do
|
902
|
+
requires :batch_no, type: String
|
903
|
+
end
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
subject.get '/validate_required_arrays_under_optional_arrays' do
|
909
|
+
'validate_required_arrays_under_optional_arrays works!'
|
910
|
+
end
|
911
|
+
|
912
|
+
data = {
|
913
|
+
orders: [
|
914
|
+
{ id: 77, drugs: [{batches: [{batch_no: "A1234567"}]}]},
|
915
|
+
{ id: 70 }
|
916
|
+
]
|
917
|
+
}
|
918
|
+
|
919
|
+
get '/validate_required_arrays_under_optional_arrays', data
|
920
|
+
expect(last_response.body).to eq("validate_required_arrays_under_optional_arrays works!")
|
921
|
+
expect(last_response.status).to eq(200)
|
922
|
+
end
|
923
|
+
|
924
|
+
it "simplest example using Array -> Array -> Hash -> String" do
|
925
|
+
subject.params do
|
926
|
+
requires :orders, type: Array do
|
927
|
+
requires :id, type: Integer
|
928
|
+
optional :drugs, type: Array do
|
929
|
+
requires :batch_no, type: String
|
930
|
+
end
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
subject.get '/validate_required_arrays_under_optional_arrays' do
|
935
|
+
'validate_required_arrays_under_optional_arrays works!'
|
936
|
+
end
|
937
|
+
|
938
|
+
data = {
|
939
|
+
orders: [
|
940
|
+
{ id: 77, drugs: [{batch_no: "A1234567"}]},
|
941
|
+
{ id: 70 }
|
942
|
+
]
|
943
|
+
}
|
944
|
+
|
945
|
+
get '/validate_required_arrays_under_optional_arrays', data
|
946
|
+
expect(last_response.body).to eq("validate_required_arrays_under_optional_arrays works!")
|
947
|
+
expect(last_response.status).to eq(200)
|
948
|
+
end
|
949
|
+
|
950
|
+
it "simplest example using Array -> Hash -> String" do
|
951
|
+
subject.params do
|
952
|
+
requires :orders, type: Array do
|
953
|
+
requires :id, type: Integer
|
954
|
+
optional :drugs, type: Hash do
|
955
|
+
requires :batch_no, type: String
|
956
|
+
end
|
957
|
+
end
|
958
|
+
end
|
959
|
+
|
960
|
+
subject.get '/validate_required_arrays_under_optional_arrays' do
|
961
|
+
'validate_required_arrays_under_optional_arrays works!'
|
962
|
+
end
|
963
|
+
|
964
|
+
data = {
|
965
|
+
orders: [
|
966
|
+
{ id: 77, drugs: {batch_no: "A1234567"}},
|
967
|
+
{ id: 70 }
|
968
|
+
]
|
969
|
+
}
|
970
|
+
|
971
|
+
get '/validate_required_arrays_under_optional_arrays', data
|
972
|
+
expect(last_response.body).to eq("validate_required_arrays_under_optional_arrays works!")
|
973
|
+
expect(last_response.status).to eq(200)
|
974
|
+
end
|
975
|
+
|
976
|
+
it "correctly indexes invalida data" do
|
977
|
+
subject.params do
|
978
|
+
requires :orders, type: Array do
|
979
|
+
requires :id, type: Integer
|
980
|
+
optional :drugs, type: Array do
|
981
|
+
requires :batch_no, type: String
|
982
|
+
requires :quantity, type: Integer
|
983
|
+
end
|
984
|
+
end
|
985
|
+
end
|
986
|
+
|
987
|
+
subject.get '/correctly_indexes' do
|
988
|
+
'correctly_indexes works!'
|
989
|
+
end
|
990
|
+
|
991
|
+
data = {
|
992
|
+
orders: [
|
993
|
+
{ id: 70 },
|
994
|
+
{ id: 77, drugs: [{batch_no: "A1234567", quantity: 12}, {batch_no: "B222222"}]}
|
995
|
+
]
|
996
|
+
}
|
997
|
+
|
998
|
+
get '/correctly_indexes', data
|
999
|
+
expect(last_response.body).to eq("orders[1][drugs][1][quantity] is missing")
|
1000
|
+
expect(last_response.status).to eq(400)
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
context "multiple levels of optional and requires settings" do
|
1004
|
+
before do
|
1005
|
+
subject.params do
|
1006
|
+
requires :top, type: Array do
|
1007
|
+
requires :top_id, type: Integer, allow_blank: false
|
1008
|
+
optional :middle_1, type: Array do
|
1009
|
+
requires :middle_1_id, type: Integer, allow_blank: false
|
1010
|
+
optional :middle_2, type: Array do
|
1011
|
+
requires :middle_2_id, type: String, allow_blank: false
|
1012
|
+
optional :bottom, type: Array do
|
1013
|
+
requires :bottom_id, type: Integer, allow_blank: false
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
subject.get '/multi_level' do
|
1021
|
+
'multi_level works!'
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
it "with valid data" do
|
1026
|
+
data = {
|
1027
|
+
top: [
|
1028
|
+
{ top_id: 1, middle_1: [
|
1029
|
+
{middle_1_id: 11}, {middle_1_id: 12, middle_2: [
|
1030
|
+
{middle_2_id: 121}, {middle_2_id: 122, bottom: [{bottom_id: 1221}]}]}]},
|
1031
|
+
{ top_id: 2, middle_1: [
|
1032
|
+
{middle_1_id: 21}, {middle_1_id: 22, middle_2: [
|
1033
|
+
{middle_2_id: 221}]}]},
|
1034
|
+
{ top_id: 3, middle_1: [
|
1035
|
+
{middle_1_id: 31}, {middle_1_id: 32}]},
|
1036
|
+
{ top_id: 4 }
|
1037
|
+
]
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
get '/multi_level', data
|
1041
|
+
expect(last_response.body).to eq("multi_level works!")
|
1042
|
+
expect(last_response.status).to eq(200)
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
it "with invalid data" do
|
1046
|
+
data = {
|
1047
|
+
top: [
|
1048
|
+
{ top_id: 1, middle_1: [
|
1049
|
+
{middle_1_id: 11}, {middle_1_id: 12, middle_2: [
|
1050
|
+
{middle_2_id: 121}, {middle_2_id: 122, bottom: [{bottom_id: nil}]}]}]},
|
1051
|
+
{ top_id: 2, middle_1: [
|
1052
|
+
{middle_1_id: 21}, {middle_1_id: 22, middle_2: [{middle_2_id: nil}]}]},
|
1053
|
+
{ top_id: 3, middle_1: [
|
1054
|
+
{middle_1_id: nil}, {middle_1_id: 32}]},
|
1055
|
+
{ top_id: nil, missing_top_id: 4 }
|
1056
|
+
]
|
1057
|
+
}
|
1058
|
+
# debugger
|
1059
|
+
get '/multi_level', data
|
1060
|
+
expect(last_response.body.split(", ")).to match_array([
|
1061
|
+
"top[3][top_id] is empty",
|
1062
|
+
"top[2][middle_1][0][middle_1_id] is empty",
|
1063
|
+
"top[1][middle_1][1][middle_2][0][middle_2_id] is empty",
|
1064
|
+
"top[0][middle_1][1][middle_2][1][bottom][0][bottom_id] is empty"
|
1065
|
+
])
|
1066
|
+
expect(last_response.status).to eq(400)
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
it "exactly_one_of" do
|
1072
|
+
subject.params do
|
1073
|
+
requires :orders, type: Array do
|
1074
|
+
requires :id, type: Integer
|
1075
|
+
optional :drugs, type: Hash do
|
1076
|
+
optional :batch_no, type: String
|
1077
|
+
optional :batch_id, type: String
|
1078
|
+
exactly_one_of :batch_no, :batch_id
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
subject.get '/exactly_one_of' do
|
1084
|
+
'exactly_one_of works!'
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
data = {
|
1088
|
+
orders: [
|
1089
|
+
{ id: 77, drugs: {batch_no: "A1234567"}},
|
1090
|
+
{ id: 70 }
|
1091
|
+
]
|
1092
|
+
}
|
1093
|
+
|
1094
|
+
get '/exactly_one_of', data
|
1095
|
+
expect(last_response.body).to eq("exactly_one_of works!")
|
1096
|
+
expect(last_response.status).to eq(200)
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
it "at_least_one_of" do
|
1100
|
+
subject.params do
|
1101
|
+
requires :orders, type: Array do
|
1102
|
+
requires :id, type: Integer
|
1103
|
+
optional :drugs, type: Hash do
|
1104
|
+
optional :batch_no, type: String
|
1105
|
+
optional :batch_id, type: String
|
1106
|
+
at_least_one_of :batch_no, :batch_id
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
subject.get '/at_least_one_of' do
|
1112
|
+
'at_least_one_of works!'
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
data = {
|
1116
|
+
orders: [
|
1117
|
+
{ id: 77, drugs: {batch_no: "A1234567"}},
|
1118
|
+
{ id: 70 }
|
1119
|
+
]
|
1120
|
+
}
|
1121
|
+
|
1122
|
+
get '/at_least_one_of', data
|
1123
|
+
expect(last_response.body).to eq("at_least_one_of works!")
|
1124
|
+
expect(last_response.status).to eq(200)
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
it "all_or_none_of" do
|
1128
|
+
subject.params do
|
1129
|
+
requires :orders, type: Array do
|
1130
|
+
requires :id, type: Integer
|
1131
|
+
optional :drugs, type: Hash do
|
1132
|
+
optional :batch_no, type: String
|
1133
|
+
optional :batch_id, type: String
|
1134
|
+
all_or_none_of :batch_no, :batch_id
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
subject.get '/all_or_none_of' do
|
1140
|
+
'all_or_none_of works!'
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
data = {
|
1144
|
+
orders: [
|
1145
|
+
{ id: 77, drugs: {batch_no: "A1234567", batch_id: "12"}},
|
1146
|
+
{ id: 70 }
|
1147
|
+
]
|
1148
|
+
}
|
1149
|
+
|
1150
|
+
get '/all_or_none_of', data
|
1151
|
+
expect(last_response.body).to eq("all_or_none_of works!")
|
1152
|
+
expect(last_response.status).to eq(200)
|
885
1153
|
end
|
886
1154
|
end
|
887
1155
|
|
@@ -1126,14 +1394,14 @@ describe Grape::Validations do
|
|
1126
1394
|
subject.params do
|
1127
1395
|
use :pagination
|
1128
1396
|
end
|
1129
|
-
expect(
|
1397
|
+
expect(declared_params).to eq %i[page per_page]
|
1130
1398
|
end
|
1131
1399
|
|
1132
1400
|
it 'by #use with multiple params' do
|
1133
1401
|
subject.params do
|
1134
1402
|
use :pagination, :period
|
1135
1403
|
end
|
1136
|
-
expect(
|
1404
|
+
expect(declared_params).to eq %i[page per_page start_date end_date]
|
1137
1405
|
end
|
1138
1406
|
end
|
1139
1407
|
|
@@ -1422,7 +1690,7 @@ describe Grape::Validations do
|
|
1422
1690
|
it 'errors when two or more are present' do
|
1423
1691
|
get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
1424
1692
|
expect(last_response.status).to eq(400)
|
1425
|
-
expect(last_response.body).to eq 'beer, wine
|
1693
|
+
expect(last_response.body).to eq 'beer, wine are missing, exactly one parameter is required'
|
1426
1694
|
end
|
1427
1695
|
end
|
1428
1696
|
|
@@ -1441,7 +1709,7 @@ describe Grape::Validations do
|
|
1441
1709
|
it 'errors when two or more are present' do
|
1442
1710
|
get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
1443
1711
|
expect(last_response.status).to eq(400)
|
1444
|
-
expect(last_response.body).to eq 'beer, wine
|
1712
|
+
expect(last_response.body).to eq 'beer, wine are mutually exclusive'
|
1445
1713
|
end
|
1446
1714
|
end
|
1447
1715
|
|
@@ -1481,7 +1749,7 @@ describe Grape::Validations do
|
|
1481
1749
|
it 'errors when two or more are present' do
|
1482
1750
|
get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
|
1483
1751
|
expect(last_response.status).to eq(400)
|
1484
|
-
expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2]
|
1752
|
+
expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2] are mutually exclusive'
|
1485
1753
|
end
|
1486
1754
|
end
|
1487
1755
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
|
4
|
+
require 'grape'
|
5
|
+
|
6
|
+
describe Grape do
|
7
|
+
it 'eager_load!' do
|
8
|
+
require 'grape/eager_load'
|
9
|
+
expect { Grape.eager_load! }.to_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'compile!' do
|
13
|
+
expect { Class.new(Grape::API).compile! }.to_not raise_error
|
14
|
+
end
|
15
|
+
end
|
@@ -7,7 +7,7 @@ shared_examples_for 'versioning' do
|
|
7
7
|
subject.get :hello do
|
8
8
|
"Version: #{request.env['api.version']}"
|
9
9
|
end
|
10
|
-
versioned_get '/hello', 'v1', macro_options
|
10
|
+
versioned_get '/hello', 'v1', **macro_options
|
11
11
|
expect(last_response.body).to eql 'Version: v1'
|
12
12
|
end
|
13
13
|
|
@@ -18,7 +18,7 @@ shared_examples_for 'versioning' do
|
|
18
18
|
subject.get :hello do
|
19
19
|
"Version: #{request.env['api.version']}"
|
20
20
|
end
|
21
|
-
versioned_get '/hello', 'v1', macro_options.merge(prefix: 'api')
|
21
|
+
versioned_get '/hello', 'v1', **macro_options.merge(prefix: 'api')
|
22
22
|
expect(last_response.body).to eql 'Version: v1'
|
23
23
|
end
|
24
24
|
|
@@ -34,14 +34,14 @@ shared_examples_for 'versioning' do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
versioned_get '/awesome', 'v1', macro_options
|
37
|
+
versioned_get '/awesome', 'v1', **macro_options
|
38
38
|
expect(last_response.status).to eql 404
|
39
39
|
|
40
|
-
versioned_get '/awesome', 'v2', macro_options
|
40
|
+
versioned_get '/awesome', 'v2', **macro_options
|
41
41
|
expect(last_response.status).to eql 200
|
42
|
-
versioned_get '/legacy', 'v1', macro_options
|
42
|
+
versioned_get '/legacy', 'v1', **macro_options
|
43
43
|
expect(last_response.status).to eql 200
|
44
|
-
versioned_get '/legacy', 'v2', macro_options
|
44
|
+
versioned_get '/legacy', 'v2', **macro_options
|
45
45
|
expect(last_response.status).to eql 404
|
46
46
|
end
|
47
47
|
|
@@ -51,11 +51,11 @@ shared_examples_for 'versioning' do
|
|
51
51
|
'I exist'
|
52
52
|
end
|
53
53
|
|
54
|
-
versioned_get '/awesome', 'v1', macro_options
|
54
|
+
versioned_get '/awesome', 'v1', **macro_options
|
55
55
|
expect(last_response.status).to eql 200
|
56
|
-
versioned_get '/awesome', 'v2', macro_options
|
56
|
+
versioned_get '/awesome', 'v2', **macro_options
|
57
57
|
expect(last_response.status).to eql 200
|
58
|
-
versioned_get '/awesome', 'v3', macro_options
|
58
|
+
versioned_get '/awesome', 'v3', **macro_options
|
59
59
|
expect(last_response.status).to eql 404
|
60
60
|
end
|
61
61
|
|
@@ -74,10 +74,10 @@ shared_examples_for 'versioning' do
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
versioned_get '/version', 'v2', macro_options
|
77
|
+
versioned_get '/version', 'v2', **macro_options
|
78
78
|
expect(last_response.status).to eq(200)
|
79
79
|
expect(last_response.body).to eq('v2')
|
80
|
-
versioned_get '/version', 'v1', macro_options
|
80
|
+
versioned_get '/version', 'v1', **macro_options
|
81
81
|
expect(last_response.status).to eq(200)
|
82
82
|
expect(last_response.body).to eq('version v1')
|
83
83
|
end
|
@@ -98,11 +98,11 @@ shared_examples_for 'versioning' do
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
versioned_get '/version', 'v1', macro_options.merge(prefix: subject.prefix)
|
101
|
+
versioned_get '/version', 'v1', **macro_options.merge(prefix: subject.prefix)
|
102
102
|
expect(last_response.status).to eq(200)
|
103
103
|
expect(last_response.body).to eq('version v1')
|
104
104
|
|
105
|
-
versioned_get '/version', 'v2', macro_options.merge(prefix: subject.prefix)
|
105
|
+
versioned_get '/version', 'v2', **macro_options.merge(prefix: subject.prefix)
|
106
106
|
expect(last_response.status).to eq(200)
|
107
107
|
expect(last_response.body).to eq('v2')
|
108
108
|
end
|
@@ -131,11 +131,11 @@ shared_examples_for 'versioning' do
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
-
versioned_get '/version', 'v1', macro_options.merge(prefix: subject.prefix)
|
134
|
+
versioned_get '/version', 'v1', **macro_options.merge(prefix: subject.prefix)
|
135
135
|
expect(last_response.status).to eq(200)
|
136
136
|
expect(last_response.body).to eq('v1-version')
|
137
137
|
|
138
|
-
versioned_get '/version', 'v2', macro_options.merge(prefix: subject.prefix)
|
138
|
+
versioned_get '/version', 'v2', **macro_options.merge(prefix: subject.prefix)
|
139
139
|
expect(last_response.status).to eq(200)
|
140
140
|
expect(last_response.body).to eq('v2-version')
|
141
141
|
end
|
@@ -148,7 +148,7 @@ shared_examples_for 'versioning' do
|
|
148
148
|
subject.get :api_version_with_version_param do
|
149
149
|
params[:version]
|
150
150
|
end
|
151
|
-
versioned_get '/api_version_with_version_param?version=1', 'v1', macro_options
|
151
|
+
versioned_get '/api_version_with_version_param?version=1', 'v1', **macro_options
|
152
152
|
expect(last_response.body).to eql '1'
|
153
153
|
end
|
154
154
|
|
@@ -183,13 +183,13 @@ shared_examples_for 'versioning' do
|
|
183
183
|
|
184
184
|
context 'v1' do
|
185
185
|
it 'finds endpoint' do
|
186
|
-
versioned_get '/version', 'v1', macro_options
|
186
|
+
versioned_get '/version', 'v1', **macro_options
|
187
187
|
expect(last_response.status).to eq(200)
|
188
188
|
expect(last_response.body).to eq('v1')
|
189
189
|
end
|
190
190
|
|
191
191
|
it 'finds catch all' do
|
192
|
-
versioned_get '/whatever', 'v1', macro_options
|
192
|
+
versioned_get '/whatever', 'v1', **macro_options
|
193
193
|
expect(last_response.status).to eq(200)
|
194
194
|
expect(last_response.body).to end_with 'whatever'
|
195
195
|
end
|
@@ -197,13 +197,13 @@ shared_examples_for 'versioning' do
|
|
197
197
|
|
198
198
|
context 'v2' do
|
199
199
|
it 'finds endpoint' do
|
200
|
-
versioned_get '/version', 'v2', macro_options
|
200
|
+
versioned_get '/version', 'v2', **macro_options
|
201
201
|
expect(last_response.status).to eq(200)
|
202
202
|
expect(last_response.body).to eq('v2')
|
203
203
|
end
|
204
204
|
|
205
205
|
it 'finds catch all' do
|
206
|
-
versioned_get '/whatever', 'v2', macro_options
|
206
|
+
versioned_get '/whatever', 'v2', **macro_options
|
207
207
|
expect(last_response.status).to eq(200)
|
208
208
|
expect(last_response.body).to end_with 'whatever'
|
209
209
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -14,25 +14,18 @@ Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
|
|
14
14
|
require file
|
15
15
|
end
|
16
16
|
|
17
|
+
eager_load!
|
18
|
+
|
17
19
|
# The default value for this setting is true in a standard Rails app,
|
18
20
|
# so it should be set to true here as well to reflect that.
|
19
21
|
I18n.enforce_available_locales = true
|
20
22
|
|
21
|
-
module Chunks
|
22
|
-
def read_chunks(body)
|
23
|
-
buffer = []
|
24
|
-
body.each { |chunk| buffer << chunk }
|
25
|
-
|
26
|
-
buffer
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
23
|
RSpec.configure do |config|
|
31
|
-
config.include Chunks
|
32
24
|
config.include Rack::Test::Methods
|
33
25
|
config.include Spec::Support::Helpers
|
34
26
|
config.raise_errors_for_deprecations!
|
35
27
|
config.filter_run_when_matching :focus
|
28
|
+
config.warnings = true
|
36
29
|
|
37
30
|
config.before(:each) { Grape::Util::InheritableSetting.reset_global! }
|
38
31
|
|