Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: Correctly handle given in Array params #1625

Merged
merged 3 commits into from
Jun 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#### Fixes

* [#1615](https://github.com/ruby-grape/grape/pull/1615): Fix default and type validator when values is a Hash with no value attribute - [@jlfaber](https://github.com/jlfaber).
* [#1625](https://github.com/ruby-grape/grape/pull/1625): Handle `given` correctly when nested in Array params - [@rnubel](https://github.com/rnubel), [@avellable](https://github.com/avellable).
* Your contribution here.

### 0.19.2 (4/12/2017)
Expand Down
34 changes: 21 additions & 13 deletions lib/grape/validations/params_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,37 +42,45 @@ def should_validate?(parameters)
return false if @optional && (params(parameters).blank? ||
any_element_blank?(parameters))

return true if parent.nil?
parent.should_validate?(parameters)
end

def meets_dependency?(params)
return true unless @dependent_on

params = params.with_indifferent_access

@dependent_on.each do |dependency|
if dependency.is_a?(Hash)
dependency_key = dependency.keys[0]
proc = dependency.values[0]
return false unless proc.call(params(parameters).try(:[], dependency_key))
elsif params(parameters).try(:[], dependency).blank?
return false unless proc.call(params.try(:[], dependency_key))
elsif params.respond_to?(:key?) && params.try(:[], dependency).blank?
return false
end
end if @dependent_on
end

return true if parent.nil?
parent.should_validate?(parameters)
true
end

# @return [String] the proper attribute name, with nesting considered.
def full_name(name)
def full_name(name, index: nil)
if nested?
# Find our containing element's name, and append ours.
"#{@parent.full_name(@element)}#{array_index}[#{name}]"
[@parent.full_name(@element), [@index || index, name].map(&method(:brackets))].compact.join
elsif lateral?
# Find the name of the element as if it was at the
# same nesting level as our parent.
@parent.full_name(name)
# Find the name of the element as if it was at the same nesting level
# as our parent. We need to forward our index upward to achieve this.
@parent.full_name(name, index: @index)
else
# We must be the root scope, so no prefix needed.
name.to_s
end
end

def array_index
"[#{@index}]" if @index.present?
def brackets(val)
"[#{val}]" if val
end

# @return [Boolean] whether or not this scope is the root-level scope
Expand Down Expand Up @@ -187,7 +195,7 @@ def new_lateral_scope(options, &block)
element: nil,
parent: self,
options: @optional,
type: Hash,
type: type == Array ? Array : Hash,
dependent_on: options[:dependent_on],
&block
)
Expand Down
1 change: 1 addition & 0 deletions lib/grape/validations/validators/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def validate!(params)
array_errors = []
attributes.each do |resource_params, attr_name|
next unless @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
next unless @scope.meets_dependency?(resource_params)

begin
validate_param!(attr_name, resource_params)
Expand Down
23 changes: 23 additions & 0 deletions spec/grape/validations/params_scope_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,29 @@ def initialize(value)
end
end

context 'when validations are dependent on a parameter within an array param' do
before do
subject.params do
requires :foos, type: Array do
optional :foo_type, :baz_type
given :foo_type do
requires :bar
end
end
end
subject.post('/test') { declared(params).to_json }
end

it 'applies the constraint within each value' do
post '/test',
{ foos: [{ foo_type: 'a' }, { baz_type: 'c' }] }.to_json,
'CONTENT_TYPE' => 'application/json'

expect(last_response.status).to eq(400)
expect(last_response.body).to eq('foos[0][bar] is missing')
end
end

context 'when validations are dependent on a parameter with specific value' do
# build test cases from all combinations of declarations and options
a_decls = %i(optional requires)
Expand Down