Skip to content

Commit e77d878

Browse files
committed
Changed the order of param validation and coercion
Param validation will check for presence first and then handle any coercion. This fixes a bug where coercsion happens after the fact and validation icorrectly fails or passes
1 parent 5ec9c57 commit e77d878

File tree

2 files changed

+113
-63
lines changed

2 files changed

+113
-63
lines changed

lib/grape/validations.rb

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,31 @@ def validates(attrs, validations)
109109

110110
@api.document_attribute(attrs, doc_attrs)
111111

112+
# Validate for presence before any other validators
113+
if validations.has_key?(:presence) && validations[:presence]
114+
validate('presence', validations[:presence], attrs, doc_attrs)
115+
end
116+
117+
# Before we run the rest of the validators, lets handle
118+
# whatever coercion so that we are working with correctly
119+
# type casted values
120+
if validations.has_key? :coerce
121+
validate('coerce', validations[:coerce], attrs, doc_attrs)
122+
validations.delete(:coerce)
123+
end
124+
112125
validations.each do |type, options|
113-
validator_class = Validations::validators[type.to_s]
114-
if validator_class
115-
@api.settings[:validations] << validator_class.new(attrs, options, doc_attrs)
116-
else
117-
raise "unknown validator: #{type}"
118-
end
126+
validate(type, options, attrs, doc_attrs)
127+
end
128+
end
129+
130+
def validate(type, options, attrs, doc_attrs)
131+
validator_class = Validations::validators[type.to_s]
132+
if validator_class
133+
@api.settings[:validations] << validator_class.new(attrs, options, doc_attrs)
134+
else
135+
raise "unknown validator: #{type}"
119136
end
120-
121137
end
122138

123139
end

spec/grape/validations_spec.rb

Lines changed: 90 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,119 @@
11
require 'spec_helper'
22

3-
module CustomValidations
4-
class Customvalidator < Grape::Validations::Validator
5-
def validate_param!(attr_name, params)
6-
unless params[attr_name] == 'im custom'
7-
throw :error, :status => 400, :message => "#{attr_name}: is not custom!"
8-
end
9-
end
10-
end
11-
end
3+
124

135
describe Grape::API do
146
subject { Class.new(Grape::API) }
157
def app; subject end
168

179
describe 'params' do
18-
it 'validates optional parameter if present' do
19-
subject.params { optional :a_number, :regexp => /^[0-9]+$/ }
20-
subject.get '/optional' do 'optional works!'; end
10+
context 'optional' do
11+
it 'validates when params is present' do
12+
subject.params { optional :a_number, :regexp => /^[0-9]+$/ }
13+
subject.get '/optional' do 'optional works!'; end
14+
15+
get '/optional', { :a_number => 'string' }
16+
last_response.status.should == 400
17+
last_response.body.should == 'invalid parameter: a_number'
18+
19+
get '/optional', { :a_number => 45 }
20+
last_response.status.should == 200
21+
last_response.body.should == 'optional works!'
22+
end
2123

22-
get '/optional', { :a_number => 'string' }
23-
last_response.status.should == 400
24-
last_response.body.should == 'invalid parameter: a_number'
24+
it "doesn't validate when param not present" do
25+
subject.params { optional :a_number, :regexp => /^[0-9]+$/ }
26+
subject.get '/optional' do 'optional works!'; end
2527

26-
get '/optional', { :a_number => 45 }
27-
last_response.status.should == 200
28-
last_response.body.should == 'optional works!'
28+
get '/optional'
29+
last_response.status.should == 200
30+
last_response.body.should == 'optional works!'
31+
end
2932
end
3033

31-
context 'when using optional with a custom validator' do
34+
context 'required' do
3235
before do
33-
subject.params { optional :custom, :customvalidator => true }
34-
subject.get '/optional_custom' do 'optional with custom works!'; end
36+
subject.params { requires :key }
37+
subject.get '/required' do 'required works'; end
3538
end
3639

37-
it 'validates when param is present' do
38-
get '/optional_custom', { :custom => 'im custom' }
39-
last_response.status.should == 200
40-
last_response.body.should == 'optional with custom works!'
41-
42-
get '/optional_custom', { :custom => 'im wrong' }
40+
it 'errors when param not present' do
41+
get '/required'
4342
last_response.status.should == 400
44-
last_response.body.should == 'custom: is not custom!'
43+
last_response.body.should == 'missing parameter: key'
4544
end
4645

47-
it "skip validation when parameter isn't present" do
48-
get '/optional_custom'
46+
it "doesn't throw a missing param when param is present" do
47+
get '/required', { :key => 'cool' }
4948
last_response.status.should == 200
50-
last_response.body.should == 'optional with custom works!'
51-
end
52-
53-
it 'validates with custom validator when param present and incorrect type' do
54-
subject.params { optional :custom, :type => String, :customvalidator => true }
55-
56-
get '/optional_custom', { :custom => 123 }
57-
last_response.status.should == 400
58-
last_response.body.should == 'custom: is not custom!'
49+
last_response.body.should == 'required works'
5950
end
6051
end
6152

62-
context 'when using requires with a custom validator' do
63-
before do
64-
subject.params { requires :custom, :customvalidator => true }
65-
subject.get '/required_custom' do 'required with custom works!'; end
66-
end
53+
context 'custom validation' do
54+
module CustomValidations
55+
class Customvalidator < Grape::Validations::Validator
56+
def validate_param!(attr_name, params)
57+
unless params[attr_name] == 'im custom'
58+
throw :error, :status => 400, :message => "#{attr_name}: is not custom!"
59+
end
60+
end
61+
end
62+
end
6763

68-
it 'validates when param is present' do
69-
get '/required_custom', { :custom => 'im wrong, validate me' }
70-
last_response.status.should == 400
71-
last_response.body.should == 'custom: is not custom!'
64+
context 'when using optional with a custom validator' do
65+
before do
66+
subject.params { optional :custom, :customvalidator => true }
67+
subject.get '/optional_custom' do 'optional with custom works!'; end
68+
end
7269

73-
get '/required_custom', { :custom => 'im custom' }
74-
last_response.status.should == 200
75-
last_response.body.should == 'required with custom works!'
70+
it 'validates when param is present' do
71+
get '/optional_custom', { :custom => 'im custom' }
72+
last_response.status.should == 200
73+
last_response.body.should == 'optional with custom works!'
74+
75+
get '/optional_custom', { :custom => 'im wrong' }
76+
last_response.status.should == 400
77+
last_response.body.should == 'custom: is not custom!'
78+
end
79+
80+
it "skip validation when parameter isn't present" do
81+
get '/optional_custom'
82+
last_response.status.should == 200
83+
last_response.body.should == 'optional with custom works!'
84+
end
85+
86+
it 'validates with custom validator when param present and incorrect type' do
87+
subject.params { optional :custom, :type => String, :customvalidator => true }
88+
89+
get '/optional_custom', { :custom => 123 }
90+
last_response.status.should == 400
91+
last_response.body.should == 'custom: is not custom!'
92+
end
7693
end
7794

78-
it 'validates when param is not present' do
79-
get '/required_custom'
80-
last_response.status.should == 400
81-
last_response.body.should == 'custom: is not custom!'
95+
context 'when using requires with a custom validator' do
96+
before do
97+
subject.params { requires :custom, :customvalidator => true }
98+
subject.get '/required_custom' do 'required with custom works!'; end
99+
end
100+
101+
it 'validates when param is present' do
102+
get '/required_custom', { :custom => 'im wrong, validate me' }
103+
last_response.status.should == 400
104+
last_response.body.should == 'custom: is not custom!'
105+
106+
get '/required_custom', { :custom => 'im custom' }
107+
last_response.status.should == 200
108+
last_response.body.should == 'required with custom works!'
109+
end
110+
111+
it 'validates when param is not present' do
112+
get '/required_custom'
113+
last_response.status.should == 400
114+
last_response.body.should == 'missing parameter: custom'
115+
end
82116
end
83-
end
117+
end # end custom validation
84118
end
85119
end

0 commit comments

Comments
 (0)