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

Add validator 'declared_only' to reject undeclared params #1606

Closed
wants to merge 2 commits into from
Closed
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 @@ -6,6 +6,7 @@
* [#1568](https://github.com/ruby-grape/grape/pull/1568): Add `proc` option to `values` validator to allow custom checks - [@jlfaber](https://github.com/jlfaber).
* [#1575](https://github.com/ruby-grape/grape/pull/1575): Include nil values for missing nested params in declared - [@thogg4](https://github.com/thogg4).
* [#1585](https://github.com/ruby-grape/grape/pull/1585): Bugs in declared method - make sure correct options var is used and respect include missing for non children params - [@thogg4](https://github.com/thogg4).
* [#1606](https://github.com/ruby-grape/grape/pull/1606): Add `declared_only` validator to reject undeclared keys within block - [@jlfaber](https://github.com/jlfaber).
* Your contribution here.

#### Fixes
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,20 @@ params do
end
```

#### Nested `mutually_exclusive`, `exactly_one_of`, `at_least_one_of`, `all_or_none_of`
#### `declared_only`

The method 'declared_only' may be used within any block to ensure that no undeclared parameters are allowed.

```ruby
params do
optional :beer
optional :wine
optional :juice
declared_only
end
```

#### Nested `mutually_exclusive`, `exactly_one_of`, `at_least_one_of`, `all_or_none_of`, `declared_only`

All of these methods can be used at any nested level.

Expand All @@ -1274,11 +1287,13 @@ params do
optional :icecream
mutually_exclusive :cake, :icecream
end
optional :recipe, type: Hash do
optional :recipes, type: Array do
optional :oil
optional :meat
all_or_none_of :oil, :meat
declared_only
end
declared_only
end
```

Expand Down
1 change: 1 addition & 0 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ module ServeFile
require 'grape/validations/validators/default'
require 'grape/validations/validators/exactly_one_of'
require 'grape/validations/validators/mutual_exclusion'
require 'grape/validations/validators/declared_only'
require 'grape/validations/validators/presence'
require 'grape/validations/validators/regexp'
require 'grape/validations/validators/values'
Expand Down
6 changes: 6 additions & 0 deletions lib/grape/dsl/parameters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ def all_or_none_of(*attrs)
validates(attrs, all_or_none_of: { value: true, message: extract_message_option(attrs) })
end

# Require that only declared params are present.
# @param (see #mutually_exclusive)
def declared_only(*attrs)
validates(attrs, declared_only: { value: true, message: extract_message_option(attrs) })
end

# Define a block of validations which should be applied if and only if
# the given parameter is present. The parameters are not nested.
# @param attr [Symbol] the parameter which, if present, triggers the
Expand Down
1 change: 1 addition & 0 deletions lib/grape/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ en:
at_least_one: 'are missing, at least one parameter must be provided'
exactly_one: 'are missing, exactly one parameter must be provided'
all_or_none: 'provide all or none of parameters'
declared_only: 'is not a declared parameter'
missing_group_type: 'group type is required'
unsupported_group_type: 'group type must be Array, Hash, JSON or Array[JSON]'
invalid_message_body:
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/validations/params_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Grape
module Validations
class ParamsScope
attr_accessor :element, :parent, :index
attr_reader :type
attr_reader :type, :declared_params

include Grape::DSL::Parameters

Expand Down
25 changes: 25 additions & 0 deletions lib/grape/validations/validators/declared_only.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Grape
module Validations
require 'grape/validations/validators/multiple_params_base'
class DeclaredOnlyValidator < MultipleParamsBase
attr_reader :extra_params

def validate!(params)
super
if extra_params_are_present
raise Grape::Exceptions::Validation, params: extra_params, message: message(:declared_only)
end
params
end

private

def extra_params_are_present
scoped_params.any? do |resource_params|
@extra_params = undeclared_keys(resource_params)
!@extra_params.empty?
end
end
end
end
end
8 changes: 8 additions & 0 deletions lib/grape/validations/validators/multiple_params_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ def keys_in_common(resource_params)
(all_keys & resource_params.stringify_keys.keys).map(&:to_s)
end

def undeclared_keys(resource_params)
return [] unless resource_params.is_a?(Hash)
allowed_keys = @scope.declared_params.map do |k|
k.is_a?(Hash) ? k.keys.first.to_s : k.to_s
end
(resource_params.stringify_keys.keys - allowed_keys)
end

def all_keys
attrs.map(&:to_s)
end
Expand Down
Loading