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

Fix: Support nested query parameters defined with square brackets #65

Closed
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
44 changes: 37 additions & 7 deletions lib/openapi_parser/parameter_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,29 @@ class << self
# @param [OpenAPIParser::SchemaValidator::Options] options
# @param [Boolean] is_header is header or not (ignore params key case)
def validate_parameter(parameters_hash, params, object_reference, options, is_header = false)
return validate_header_parameter(parameters_hash, params, object_reference, options) if is_header

no_exist_required_key = []

params_key_converted = params.keys.map { |k| [convert_key(k, is_header), k] }.to_h
parameters_hash.each do |k, v|
key = params_key_converted[convert_key(k, is_header)]
if params.include?(key)
coerced = v.validate_params(params[key], options)
params[key] = coerced if options.coerce_value
path = k.scan(/\w+/m)
last_key = path.pop
parent_params = path.empty? ? params : params.dig(*path)

if parent_params && parent_params.include?(last_key)
coerced = v.validate_params(parent_params[last_key], options)
if options.coerce_value
parent_params[last_key] = coerced

until path.empty?
last_key = path.pop
prev_params = parent_params
parent_params = (path.empty? ? params : params.dig(*path))
parent_params[last_key] = prev_params
end

params = parent_params
end
elsif v.required
no_exist_required_key << k
end
Expand All @@ -26,8 +41,23 @@ def validate_parameter(parameters_hash, params, object_reference, options, is_he

private

def convert_key(k, is_header)
is_header ? k&.downcase : k
def validate_header_parameter(parameters_hash, params, object_reference, options)
no_exist_required_key = []

params_key_converted = params.keys.map { |k| [k.downcase, k] }.to_h
parameters_hash.each do |k, v|
key = params_key_converted[k.downcase]
if params.include?(key)
coerced = v.validate_params(params[key], options)
params[key] = coerced if options.coerce_value
elsif v.required
no_exist_required_key << k
end
end

raise OpenAPIParser::NotExistRequiredKey.new(no_exist_required_key, object_reference) unless no_exist_required_key.empty?

params
end
end
end
37 changes: 37 additions & 0 deletions spec/data/normal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,43 @@ paths:
type: array
items:
type: string
/validate-nested:
post:
description: validate test data with query params
parameters:
- in: query
name: nested_object
required: true
schema:
type: object
properties:
name:
type: string
value:
type: string
format: date-time
- in: query
name: nested_but_flat_object[name]
required: true
schema:
type: string
- in: query
name: nested_but_flat_object[value]
required: true
schema:
type: string
format: date-time

responses:
'200':
description: success
content:
application/json:
schema:
type: object
properties:
string:
type: string
/validate:
get:
description: get characters
Expand Down
69 changes: 69 additions & 0 deletions spec/openapi_parser/parameter_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,74 @@
end
end
end

context 'nested query params in post request' do
let(:http_method) { :post }
let(:request_path) { '/validate-nested' }
let(:config) do
{
coerce_value: true,
datetime_coerce_class: DateTime
}
end

context 'with required params, nested' do
let(:params) do
{
'nested_object' => {
'name' => 'hello',
'value' => '2016-04-01T16:00:00+09:00'
},
'nested_but_flat_object' => {
'name' => 'hello',
'value' => '2016-04-01T16:00:00+09:00'
}
}
end

it { expect(subject['nested_but_flat_object']['name']).to eq('hello') }
it { expect(subject['nested_but_flat_object']['value']).to be_an_instance_of(DateTime) }
it { expect(subject['nested_object']['value']).to be_an_instance_of(DateTime) }
end

context 'without required nested param' do
let(:params) do
{
'nested_object' => {
'name' => 'hello',
'value' => '2016-04-01T16:00:00+09:00'
},
'nested_but_flat_object' => {
'value' => '2016-04-01T16:00:00+09:00'
}
}
end

it do
expect { subject }.to raise_error do |e|
expect(e).to be_kind_of(OpenAPIParser::NotExistRequiredKey)
expect(e.message).to end_with('missing required parameters: nested_but_flat_object[name]')
end
end
end

context 'without required parent param' do
let(:params) do
{
'nested_object' => {
'name' => 'hello',
'value' => '2016-04-01T16:00:00+09:00'
}
}
end

it do
expect { subject }.to raise_error do |e|
expect(e).to be_kind_of(OpenAPIParser::NotExistRequiredKey)
expect(e.message).to end_with('missing required parameters: nested_but_flat_object[name], nested_but_flat_object[value]')
end
end
end
end
end
end