grape 1.5.3 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +23 -3
- data/UPGRADING.md +43 -1
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +13 -17
- data/lib/grape/api.rb +5 -12
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/helpers.rb +6 -4
- data/lib/grape/dsl/inside_route.rb +17 -8
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +3 -3
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +2 -2
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/endpoint.rb +20 -35
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/validation.rb +1 -2
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/middleware/base.rb +2 -0
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +1 -0
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +6 -0
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -2
- data/lib/grape/validations/params_scope.rb +88 -55
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +3 -3
- data/lib/grape/validations/validators/all_or_none.rb +1 -0
- data/lib/grape/validations/validators/as.rb +4 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -0
- data/lib/grape/validations/validators/base.rb +4 -1
- data/lib/grape/validations/validators/coerce.rb +1 -5
- data/lib/grape/validations/validators/default.rb +1 -0
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -0
- data/lib/grape/validations/validators/presence.rb +1 -0
- data/lib/grape/validations/validators/regexp.rb +1 -0
- data/lib/grape/validations/validators/same_as.rb +1 -0
- data/lib/grape/validations/validators/values.rb +3 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +1 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api_spec.rb +126 -37
- data/spec/grape/dsl/callbacks_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +1 -1
- data/spec/grape/dsl/parameters_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint/declared_spec.rb +247 -0
- data/spec/grape/endpoint_spec.rb +5 -5
- data/spec/grape/entity_spec.rb +9 -9
- data/spec/grape/middleware/auth/dsl_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +2 -2
- data/spec/grape/middleware/stack_spec.rb +3 -1
- data/spec/grape/validations/params_scope_spec.rb +37 -3
- data/spec/grape/validations/single_attribute_iterator_spec.rb +1 -1
- data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
- data/spec/grape/validations/validators/coerce_spec.rb +13 -10
- data/spec/grape/validations/validators/except_values_spec.rb +2 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -11
- data/spec/grape/validations_spec.rb +54 -42
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- metadata +6 -6
data/spec/grape/api_spec.rb
CHANGED
@@ -610,6 +610,7 @@ describe Grape::API do
|
|
610
610
|
subject.namespace :example do
|
611
611
|
before do
|
612
612
|
raise 'before filter ran twice' if already_run
|
613
|
+
|
613
614
|
already_run = true
|
614
615
|
header 'X-Custom-Header', 'foo'
|
615
616
|
end
|
@@ -650,12 +651,12 @@ describe Grape::API do
|
|
650
651
|
|
651
652
|
put '/example'
|
652
653
|
expect(last_response.status).to eql 405
|
653
|
-
expect(last_response.body).to eq
|
654
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
655
|
-
<error>
|
656
|
-
|
657
|
-
</error>
|
658
|
-
XML
|
654
|
+
expect(last_response.body).to eq <<~XML
|
655
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
656
|
+
<error>
|
657
|
+
<message>405 Not Allowed</message>
|
658
|
+
</error>
|
659
|
+
XML
|
659
660
|
end
|
660
661
|
end
|
661
662
|
|
@@ -749,6 +750,47 @@ XML
|
|
749
750
|
end
|
750
751
|
end
|
751
752
|
|
753
|
+
describe 'when a resource routes by POST, GET, PATCH, PUT, and DELETE' do
|
754
|
+
before do
|
755
|
+
subject.namespace :example do
|
756
|
+
get do
|
757
|
+
'example'
|
758
|
+
end
|
759
|
+
|
760
|
+
patch do
|
761
|
+
'example'
|
762
|
+
end
|
763
|
+
|
764
|
+
post do
|
765
|
+
'example'
|
766
|
+
end
|
767
|
+
|
768
|
+
delete do
|
769
|
+
'example'
|
770
|
+
end
|
771
|
+
|
772
|
+
put do
|
773
|
+
'example'
|
774
|
+
end
|
775
|
+
end
|
776
|
+
options '/example'
|
777
|
+
end
|
778
|
+
|
779
|
+
describe 'it adds an OPTIONS route for namespaced endpoints that' do
|
780
|
+
it 'returns a 204' do
|
781
|
+
expect(last_response.status).to eql 204
|
782
|
+
end
|
783
|
+
|
784
|
+
it 'has an empty body' do
|
785
|
+
expect(last_response.body).to be_blank
|
786
|
+
end
|
787
|
+
|
788
|
+
it 'has an Allow header' do
|
789
|
+
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, PATCH, POST, DELETE, PUT, HEAD'
|
790
|
+
end
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
752
794
|
describe 'adds an OPTIONS route for namespaced endpoints that' do
|
753
795
|
before do
|
754
796
|
subject.before { header 'X-Custom-Header', 'foo' }
|
@@ -2106,7 +2148,9 @@ XML
|
|
2106
2148
|
context 'custom errors' do
|
2107
2149
|
before do
|
2108
2150
|
class ConnectionError < RuntimeError; end
|
2151
|
+
|
2109
2152
|
class DatabaseError < RuntimeError; end
|
2153
|
+
|
2110
2154
|
class CommunicationError < StandardError; end
|
2111
2155
|
end
|
2112
2156
|
|
@@ -2262,6 +2306,7 @@ XML
|
|
2262
2306
|
module ApiSpec
|
2263
2307
|
module APIErrors
|
2264
2308
|
class ParentError < StandardError; end
|
2309
|
+
|
2265
2310
|
class ChildError < ParentError; end
|
2266
2311
|
end
|
2267
2312
|
end
|
@@ -3119,10 +3164,10 @@ XML
|
|
3119
3164
|
subject.get 'method'
|
3120
3165
|
|
3121
3166
|
expect(subject.routes.map(&:params)).to eq [{
|
3122
|
-
'group1'
|
3167
|
+
'group1' => { required: true, type: 'Array' },
|
3123
3168
|
'group1[param1]' => { required: false, desc: 'group1 param1 desc' },
|
3124
3169
|
'group1[param2]' => { required: true, desc: 'group1 param2 desc' },
|
3125
|
-
'group2'
|
3170
|
+
'group2' => { required: true, type: 'Array' },
|
3126
3171
|
'group2[param1]' => { required: false, desc: 'group2 param1 desc' },
|
3127
3172
|
'group2[param2]' => { required: true, desc: 'group2 param2 desc' }
|
3128
3173
|
}]
|
@@ -3335,8 +3380,8 @@ XML
|
|
3335
3380
|
mount app
|
3336
3381
|
end
|
3337
3382
|
expect(subject.routes.size).to eq(2)
|
3338
|
-
expect(subject.routes.first.path).to match(%r{
|
3339
|
-
expect(subject.routes.last.path).to match(%r{
|
3383
|
+
expect(subject.routes.first.path).to match(%r{/cool/awesome})
|
3384
|
+
expect(subject.routes.last.path).to match(%r{/cool/sauce})
|
3340
3385
|
end
|
3341
3386
|
|
3342
3387
|
it 'mounts on a path' do
|
@@ -3358,7 +3403,7 @@ XML
|
|
3358
3403
|
APP2.get '/nice' do
|
3359
3404
|
'play'
|
3360
3405
|
end
|
3361
|
-
#
|
3406
|
+
# NOTE: that the reverse won't work, mount from outside-in
|
3362
3407
|
APP3 = subject
|
3363
3408
|
APP3.mount APP1 => '/app1'
|
3364
3409
|
APP1.mount APP2 => '/app2'
|
@@ -3549,6 +3594,7 @@ XML
|
|
3549
3594
|
def self.included(base)
|
3550
3595
|
base.extend(ClassMethods)
|
3551
3596
|
end
|
3597
|
+
|
3552
3598
|
module ClassMethods
|
3553
3599
|
def my_method
|
3554
3600
|
@test = true
|
@@ -3793,12 +3839,12 @@ XML
|
|
3793
3839
|
end
|
3794
3840
|
get '/example'
|
3795
3841
|
expect(last_response.status).to eq(500)
|
3796
|
-
expect(last_response.body).to eq
|
3797
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
3798
|
-
<error>
|
3799
|
-
|
3800
|
-
</error>
|
3801
|
-
XML
|
3842
|
+
expect(last_response.body).to eq <<~XML
|
3843
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
3844
|
+
<error>
|
3845
|
+
<message>cannot convert String to xml</message>
|
3846
|
+
</error>
|
3847
|
+
XML
|
3802
3848
|
end
|
3803
3849
|
it 'hash' do
|
3804
3850
|
subject.get '/example' do
|
@@ -3809,13 +3855,13 @@ XML
|
|
3809
3855
|
end
|
3810
3856
|
get '/example'
|
3811
3857
|
expect(last_response.status).to eq(200)
|
3812
|
-
expect(last_response.body).to eq
|
3813
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
3814
|
-
<hash>
|
3815
|
-
|
3816
|
-
|
3817
|
-
</hash>
|
3818
|
-
XML
|
3858
|
+
expect(last_response.body).to eq <<~XML
|
3859
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
3860
|
+
<hash>
|
3861
|
+
<example1>example1</example1>
|
3862
|
+
<example2>example2</example2>
|
3863
|
+
</hash>
|
3864
|
+
XML
|
3819
3865
|
end
|
3820
3866
|
it 'array' do
|
3821
3867
|
subject.get '/example' do
|
@@ -3823,13 +3869,13 @@ XML
|
|
3823
3869
|
end
|
3824
3870
|
get '/example'
|
3825
3871
|
expect(last_response.status).to eq(200)
|
3826
|
-
expect(last_response.body).to eq
|
3827
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
3828
|
-
<strings type="array">
|
3829
|
-
|
3830
|
-
|
3831
|
-
</strings>
|
3832
|
-
XML
|
3872
|
+
expect(last_response.body).to eq <<~XML
|
3873
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
3874
|
+
<strings type="array">
|
3875
|
+
<string>example1</string>
|
3876
|
+
<string>example2</string>
|
3877
|
+
</strings>
|
3878
|
+
XML
|
3833
3879
|
end
|
3834
3880
|
it 'raised :error from middleware' do
|
3835
3881
|
middleware = Class.new(Grape::Middleware::Base) do
|
@@ -3842,12 +3888,12 @@ XML
|
|
3842
3888
|
end
|
3843
3889
|
get '/'
|
3844
3890
|
expect(last_response.status).to eq(42)
|
3845
|
-
expect(last_response.body).to eq
|
3846
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
3847
|
-
<error>
|
3848
|
-
|
3849
|
-
</error>
|
3850
|
-
XML
|
3891
|
+
expect(last_response.body).to eq <<~XML
|
3892
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
3893
|
+
<error>
|
3894
|
+
<message>Unauthorized</message>
|
3895
|
+
</error>
|
3896
|
+
XML
|
3851
3897
|
end
|
3852
3898
|
end
|
3853
3899
|
end
|
@@ -4040,6 +4086,49 @@ XML
|
|
4040
4086
|
end
|
4041
4087
|
end
|
4042
4088
|
|
4089
|
+
describe '.inherited' do
|
4090
|
+
context 'overriding within class' do
|
4091
|
+
let(:root_api) do
|
4092
|
+
Class.new(Grape::API) do
|
4093
|
+
@bar = 'Hello, world'
|
4094
|
+
|
4095
|
+
def self.inherited(child_api)
|
4096
|
+
super
|
4097
|
+
child_api.instance_variable_set(:@foo, @bar.dup)
|
4098
|
+
end
|
4099
|
+
end
|
4100
|
+
end
|
4101
|
+
|
4102
|
+
let(:child_api) { Class.new(root_api) }
|
4103
|
+
|
4104
|
+
it 'allows overriding the hook' do
|
4105
|
+
expect(child_api.instance_variable_get(:@foo)).to eq('Hello, world')
|
4106
|
+
end
|
4107
|
+
end
|
4108
|
+
|
4109
|
+
context 'overriding via composition' do
|
4110
|
+
module Inherited
|
4111
|
+
def inherited(api)
|
4112
|
+
super
|
4113
|
+
api.instance_variable_set(:@foo, @bar.dup)
|
4114
|
+
end
|
4115
|
+
end
|
4116
|
+
|
4117
|
+
let(:root_api) do
|
4118
|
+
Class.new(Grape::API) do
|
4119
|
+
@bar = 'Hello, world'
|
4120
|
+
extend Inherited
|
4121
|
+
end
|
4122
|
+
end
|
4123
|
+
|
4124
|
+
let(:child_api) { Class.new(root_api) }
|
4125
|
+
|
4126
|
+
it 'allows overriding the hook' do
|
4127
|
+
expect(child_api.instance_variable_get(:@foo)).to eq('Hello, world')
|
4128
|
+
end
|
4129
|
+
end
|
4130
|
+
end
|
4131
|
+
|
4043
4132
|
describe 'const_missing' do
|
4044
4133
|
subject(:grape_api) { Class.new(Grape::API) }
|
4045
4134
|
let(:mounted) do
|
@@ -598,4 +598,251 @@ describe Grape::Endpoint do
|
|
598
598
|
expect(json.key?(:artist_id)).not_to be_truthy
|
599
599
|
end
|
600
600
|
end
|
601
|
+
|
602
|
+
describe 'parameter renaming' do
|
603
|
+
context 'with a deeply nested parameter structure' do
|
604
|
+
let(:params) do
|
605
|
+
{
|
606
|
+
i_a: 'a',
|
607
|
+
i_b: {
|
608
|
+
i_c: 'c',
|
609
|
+
i_d: {
|
610
|
+
i_e: {
|
611
|
+
i_f: 'f',
|
612
|
+
i_g: 'g',
|
613
|
+
i_h: [
|
614
|
+
{
|
615
|
+
i_ha: 'ha1',
|
616
|
+
i_hb: {
|
617
|
+
i_hc: 'c'
|
618
|
+
}
|
619
|
+
},
|
620
|
+
{
|
621
|
+
i_ha: 'ha2',
|
622
|
+
i_hb: {
|
623
|
+
i_hc: 'c'
|
624
|
+
}
|
625
|
+
}
|
626
|
+
]
|
627
|
+
}
|
628
|
+
}
|
629
|
+
}
|
630
|
+
}
|
631
|
+
end
|
632
|
+
let(:declared) do
|
633
|
+
{
|
634
|
+
o_a: 'a',
|
635
|
+
o_b: {
|
636
|
+
o_c: 'c',
|
637
|
+
o_d: {
|
638
|
+
o_e: {
|
639
|
+
o_f: 'f',
|
640
|
+
o_g: 'g',
|
641
|
+
o_h: [
|
642
|
+
{
|
643
|
+
o_ha: 'ha1',
|
644
|
+
o_hb: {
|
645
|
+
o_hc: 'c'
|
646
|
+
}
|
647
|
+
},
|
648
|
+
{
|
649
|
+
o_ha: 'ha2',
|
650
|
+
o_hb: {
|
651
|
+
o_hc: 'c'
|
652
|
+
}
|
653
|
+
}
|
654
|
+
]
|
655
|
+
}
|
656
|
+
}
|
657
|
+
}
|
658
|
+
}
|
659
|
+
end
|
660
|
+
let(:params_keys) do
|
661
|
+
[
|
662
|
+
'i_a',
|
663
|
+
'i_b',
|
664
|
+
'i_b[i_c]',
|
665
|
+
'i_b[i_d]',
|
666
|
+
'i_b[i_d][i_e]',
|
667
|
+
'i_b[i_d][i_e][i_f]',
|
668
|
+
'i_b[i_d][i_e][i_g]',
|
669
|
+
'i_b[i_d][i_e][i_h]',
|
670
|
+
'i_b[i_d][i_e][i_h][i_ha]',
|
671
|
+
'i_b[i_d][i_e][i_h][i_hb]',
|
672
|
+
'i_b[i_d][i_e][i_h][i_hb][i_hc]'
|
673
|
+
]
|
674
|
+
end
|
675
|
+
|
676
|
+
before do
|
677
|
+
subject.format :json
|
678
|
+
subject.params do
|
679
|
+
optional :i_a, type: String, as: :o_a
|
680
|
+
optional :i_b, type: Hash, as: :o_b do
|
681
|
+
optional :i_c, type: String, as: :o_c
|
682
|
+
optional :i_d, type: Hash, as: :o_d do
|
683
|
+
optional :i_e, type: Hash, as: :o_e do
|
684
|
+
optional :i_f, type: String, as: :o_f
|
685
|
+
optional :i_g, type: String, as: :o_g
|
686
|
+
optional :i_h, type: Array, as: :o_h do
|
687
|
+
optional :i_ha, type: String, as: :o_ha
|
688
|
+
optional :i_hb, type: Hash, as: :o_hb do
|
689
|
+
optional :i_hc, type: String, as: :o_hc
|
690
|
+
end
|
691
|
+
end
|
692
|
+
end
|
693
|
+
end
|
694
|
+
end
|
695
|
+
end
|
696
|
+
subject.post '/test' do
|
697
|
+
declared(params, include_missing: false)
|
698
|
+
end
|
699
|
+
subject.post '/test/no-mod' do
|
700
|
+
before = params.to_h
|
701
|
+
declared(params, include_missing: false)
|
702
|
+
after = params.to_h
|
703
|
+
{ before: before, after: after }
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
it 'generates the correct parameter names for documentation' do
|
708
|
+
expect(subject.routes.first.params.keys).to match(params_keys)
|
709
|
+
end
|
710
|
+
|
711
|
+
it 'maps the renamed parameter correctly' do
|
712
|
+
post '/test', **params
|
713
|
+
expect(JSON.parse(last_response.body, symbolize_names: true)).to \
|
714
|
+
match(declared)
|
715
|
+
end
|
716
|
+
|
717
|
+
it 'maps no parameters when none are given' do
|
718
|
+
post '/test'
|
719
|
+
expect(JSON.parse(last_response.body)).to match({})
|
720
|
+
end
|
721
|
+
|
722
|
+
it 'does not modify the request params' do
|
723
|
+
post '/test/no-mod', **params
|
724
|
+
result = JSON.parse(last_response.body, symbolize_names: true)
|
725
|
+
expect(result[:before]).to match(result[:after])
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
context 'with a renamed root parameter' do
|
730
|
+
before do
|
731
|
+
subject.format :json
|
732
|
+
subject.params do
|
733
|
+
optional :email_address, type: String, regexp: /.+@.+/, as: :email
|
734
|
+
end
|
735
|
+
subject.post '/test' do
|
736
|
+
declared(params, include_missing: false)
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|
740
|
+
it 'generates the correct parameter names for documentation' do
|
741
|
+
expect(subject.routes.first.params.keys).to match(%w[email_address])
|
742
|
+
end
|
743
|
+
|
744
|
+
it 'maps the renamed parameter correctly (original name)' do
|
745
|
+
post '/test', email_address: 'test@example.com'
|
746
|
+
expect(JSON.parse(last_response.body)).to \
|
747
|
+
match('email' => 'test@example.com')
|
748
|
+
end
|
749
|
+
|
750
|
+
it 'validates the renamed parameter correctly (original name)' do
|
751
|
+
post '/test', email_address: 'bad[at]example.com'
|
752
|
+
expect(JSON.parse(last_response.body)).to \
|
753
|
+
match('error' => 'email_address is invalid')
|
754
|
+
end
|
755
|
+
|
756
|
+
it 'ignores the renamed parameter (as name)' do
|
757
|
+
post '/test', email: 'test@example.com'
|
758
|
+
expect(JSON.parse(last_response.body)).to match({})
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
context 'with a renamed hash with nested parameters' do
|
763
|
+
before do
|
764
|
+
subject.format :json
|
765
|
+
subject.params do
|
766
|
+
optional :address, type: Hash, as: :address_attributes do
|
767
|
+
optional :street, type: String, values: ['Street 1', 'Street 2'],
|
768
|
+
default: 'Street 1'
|
769
|
+
optional :city, type: String
|
770
|
+
end
|
771
|
+
end
|
772
|
+
subject.post '/test' do
|
773
|
+
declared(params, include_missing: false)
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
it 'generates the correct parameter names for documentation' do
|
778
|
+
expect(subject.routes.first.params.keys).to \
|
779
|
+
match(%w[address address[street] address[city]])
|
780
|
+
end
|
781
|
+
|
782
|
+
it 'maps the renamed parameter correctly (original name)' do
|
783
|
+
post '/test', address: { city: 'Berlin', street: 'Street 2', t: 't' }
|
784
|
+
expect(JSON.parse(last_response.body)).to \
|
785
|
+
match('address_attributes' => { 'city' => 'Berlin',
|
786
|
+
'street' => 'Street 2' })
|
787
|
+
end
|
788
|
+
|
789
|
+
it 'validates the renamed parameter correctly (original name)' do
|
790
|
+
post '/test', address: { street: 'unknown' }
|
791
|
+
expect(JSON.parse(last_response.body)).to \
|
792
|
+
match('error' => 'address[street] does not have a valid value')
|
793
|
+
end
|
794
|
+
|
795
|
+
it 'ignores the renamed parameter (as name)' do
|
796
|
+
post '/test', address_attributes: { city: 'Berlin', unknown: '1' }
|
797
|
+
expect(JSON.parse(last_response.body)).to match({})
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
context 'with a renamed hash with nested renamed parameter' do
|
802
|
+
before do
|
803
|
+
subject.format :json
|
804
|
+
subject.params do
|
805
|
+
optional :user, type: Hash, as: :user_attributes do
|
806
|
+
optional :email_address, type: String, regexp: /.+@.+/, as: :email
|
807
|
+
end
|
808
|
+
end
|
809
|
+
subject.post '/test' do
|
810
|
+
declared(params, include_missing: false)
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
it 'generates the correct parameter names for documentation' do
|
815
|
+
expect(subject.routes.first.params.keys).to \
|
816
|
+
match(%w[user user[email_address]])
|
817
|
+
end
|
818
|
+
|
819
|
+
it 'maps the renamed parameter correctly (original name)' do
|
820
|
+
post '/test', user: { email_address: 'test@example.com' }
|
821
|
+
expect(JSON.parse(last_response.body)).to \
|
822
|
+
match('user_attributes' => { 'email' => 'test@example.com' })
|
823
|
+
end
|
824
|
+
|
825
|
+
it 'validates the renamed parameter correctly (original name)' do
|
826
|
+
post '/test', user: { email_address: 'bad[at]example.com' }
|
827
|
+
expect(JSON.parse(last_response.body)).to \
|
828
|
+
match('error' => 'user[email_address] is invalid')
|
829
|
+
end
|
830
|
+
|
831
|
+
it 'ignores the renamed parameter (as name, 1)' do
|
832
|
+
post '/test', user: { email: 'test@example.com' }
|
833
|
+
expect(JSON.parse(last_response.body)).to \
|
834
|
+
match({ 'user_attributes' => {} })
|
835
|
+
end
|
836
|
+
|
837
|
+
it 'ignores the renamed parameter (as name, 2)' do
|
838
|
+
post '/test', user_attributes: { email_address: 'test@example.com' }
|
839
|
+
expect(JSON.parse(last_response.body)).to match({})
|
840
|
+
end
|
841
|
+
|
842
|
+
it 'ignores the renamed parameter (as name, 3)' do
|
843
|
+
post '/test', user_attributes: { email: 'test@example.com' }
|
844
|
+
expect(JSON.parse(last_response.body)).to match({})
|
845
|
+
end
|
846
|
+
end
|
847
|
+
end
|
601
848
|
end
|
data/spec/grape/endpoint_spec.rb
CHANGED
@@ -150,7 +150,7 @@ describe Grape::Endpoint do
|
|
150
150
|
end
|
151
151
|
it 'includes headers passed as symbols' do
|
152
152
|
env = Rack::MockRequest.env_for('/headers')
|
153
|
-
env[
|
153
|
+
env[:HTTP_SYMBOL_HEADER] = 'Goliath passes symbols'
|
154
154
|
body = read_chunks(subject.call(env)[2]).join
|
155
155
|
expect(JSON.parse(body)['Symbol-Header']).to eq('Goliath passes symbols')
|
156
156
|
end
|
@@ -212,10 +212,10 @@ describe Grape::Endpoint do
|
|
212
212
|
end
|
213
213
|
get '/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2'
|
214
214
|
expect(last_response.body).to eq('3')
|
215
|
-
cookies =
|
215
|
+
cookies = last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
|
216
216
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
217
217
|
[cookie.name, cookie]
|
218
|
-
end
|
218
|
+
end.to_h
|
219
219
|
expect(cookies.size).to eq(2)
|
220
220
|
%w[and_this delete_this_cookie].each do |cookie_name|
|
221
221
|
cookie = cookies[cookie_name]
|
@@ -236,10 +236,10 @@ describe Grape::Endpoint do
|
|
236
236
|
end
|
237
237
|
get('/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2')
|
238
238
|
expect(last_response.body).to eq('3')
|
239
|
-
cookies =
|
239
|
+
cookies = last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
|
240
240
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
241
241
|
[cookie.name, cookie]
|
242
|
-
end
|
242
|
+
end.to_h
|
243
243
|
expect(cookies.size).to eq(2)
|
244
244
|
%w[and_this delete_this_cookie].each do |cookie_name|
|
245
245
|
cookie = cookies[cookie_name]
|
data/spec/grape/entity_spec.rb
CHANGED
@@ -238,14 +238,14 @@ describe Grape::Entity do
|
|
238
238
|
get '/example'
|
239
239
|
expect(last_response.status).to eq(200)
|
240
240
|
expect(last_response.headers['Content-type']).to eq('application/xml')
|
241
|
-
expect(last_response.body).to eq
|
242
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
243
|
-
<hash>
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
</hash>
|
248
|
-
XML
|
241
|
+
expect(last_response.body).to eq <<~XML
|
242
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
243
|
+
<hash>
|
244
|
+
<example>
|
245
|
+
<name>johnnyiller</name>
|
246
|
+
</example>
|
247
|
+
</hash>
|
248
|
+
XML
|
249
249
|
end
|
250
250
|
|
251
251
|
it 'presents with json' do
|
@@ -326,7 +326,7 @@ XML
|
|
326
326
|
end
|
327
327
|
get '/example'
|
328
328
|
expect_response_json = {
|
329
|
-
'page'
|
329
|
+
'page' => 1,
|
330
330
|
'user1' => { 'name' => 'user1' },
|
331
331
|
'user2' => { 'name' => 'user2' }
|
332
332
|
}
|
@@ -20,7 +20,7 @@ describe Grape::Middleware::Formatter do
|
|
20
20
|
let(:body) { ['foo'] }
|
21
21
|
it 'calls #to_json since default format is json' do
|
22
22
|
body.instance_eval do
|
23
|
-
def to_json
|
23
|
+
def to_json(*_args)
|
24
24
|
'"bar"'
|
25
25
|
end
|
26
26
|
end
|
@@ -33,7 +33,7 @@ describe Grape::Middleware::Formatter do
|
|
33
33
|
let(:body) { { 'foos' => [{ 'bar' => 'baz' }] } }
|
34
34
|
it 'calls #to_json if the content type is jsonapi' do
|
35
35
|
body.instance_eval do
|
36
|
-
def to_json
|
36
|
+
def to_json(*_args)
|
37
37
|
'{"foos":[{"bar":"baz"}] }'
|
38
38
|
end
|
39
39
|
end
|
@@ -5,7 +5,9 @@ require 'spec_helper'
|
|
5
5
|
describe Grape::Middleware::Stack do
|
6
6
|
module StackSpec
|
7
7
|
class FooMiddleware; end
|
8
|
+
|
8
9
|
class BarMiddleware; end
|
10
|
+
|
9
11
|
class BlockMiddleware
|
10
12
|
attr_reader :block
|
11
13
|
|
@@ -15,7 +17,7 @@ describe Grape::Middleware::Stack do
|
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
|
-
let(:proc) { ->
|
20
|
+
let(:proc) { -> {} }
|
19
21
|
let(:others) { [[:use, StackSpec::BarMiddleware], [:insert_before, StackSpec::BarMiddleware, StackSpec::BlockMiddleware, proc]] }
|
20
22
|
|
21
23
|
subject { Grape::Middleware::Stack.new }
|