Skip to content
This repository was archived by the owner on Aug 12, 2026. It is now read-only.
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
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
strategy:
matrix:
ruby:
- '3.2'
- '3.4'

- "3.3"
- "3.4"
- "4.0"
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
Expand Down
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Naming/MethodParameterName:
AllCops:
NewCops: enable
SuggestExtensions: false
TargetRubyVersion: 3.2
TargetRubyVersion: 3.3

RSpec/ExampleLength:
Enabled: false
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## [Unreleased]

## [0.13.0] - 2026-06-23

- Changed: Require Ruby 3.3 or later
- Fixed: `deepObject` query parameter values were unescaped twice, corrupting percent-encoded characters (e.g. `%2B` became a space instead of `+`). ([#25](https://github.com/ahx/openapi_parameters/issues/25))

## [0.12.0] - 2026-05-18

- Add support for parameters defined with a `content` field. Values for media types matching `application/json` or `*+json` are decoded with `JSON.parse` before conversion. Works for query, path, header, and cookie parameters.
Expand Down
37 changes: 19 additions & 18 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
openapi_parameters (0.12.0)
openapi_parameters (0.12.1)
rack (>= 2.2)

GEM
Expand All @@ -12,21 +12,21 @@ GEM
benchmark-perf (0.6.0)
benchmark-trend (0.4.0)
diff-lcs (1.6.2)
json (2.17.1.2)
json (2.19.9)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
parallel (1.27.0)
parser (3.3.10.0)
parallel (2.1.0)
parser (3.3.11.1)
ast (~> 2.4.1)
racc
prism (1.6.0)
prism (1.9.0)
racc (1.8.1)
rack (3.2.4)
rack (3.2.6)
rack-test (2.2.0)
rack (>= 1.3)
rainbow (3.1.1)
rake (13.3.1)
regexp_parser (2.11.3)
rake (13.4.2)
regexp_parser (2.12.0)
rspec (3.13.2)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
Expand All @@ -41,31 +41,32 @@ GEM
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.7)
rspec-mocks (3.13.8)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.6)
rubocop (1.81.7)
rspec-support (3.13.7)
rubocop (1.88.0)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parallel (>= 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.47.1, < 2.0)
rubocop-ast (>= 1.49.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.47.1)
rubocop-ast (1.49.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-rspec (3.7.0)
prism (~> 1.7)
rubocop-rspec (3.10.2)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
regexp_parser (>= 2.0)
rubocop (~> 1.86, >= 1.86.2)
ruby-progressbar (1.13.0)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.1.0)
unicode-emoji (4.2.0)

PLATFORMS
arm64-darwin-21
Expand Down
13 changes: 9 additions & 4 deletions lib/openapi_parameters/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ def initialize(parameters, convert: true, rack_array_compat: false)
@parameters = parameters.map { Parameter.new(_1) }
@convert = convert
@remove_array_brackets = rack_array_compat
@deep_object_properties = @parameters.each_with_object({}) do |param, hsh|
hsh[param.name] = ObjectConverter.get_properties(param.schema) if param.deep_object?
@deep_object_properties = {}
@deep_object_regex = {}
@parameters.each do |param|
next unless param.deep_object?

@deep_object_properties[param.name] = ObjectConverter.get_properties(param.schema)
@deep_object_regex[param.name] = /^#{Regexp.escape(param.name)}#{DEEP_PROP}/
end
end

Expand Down Expand Up @@ -78,7 +83,7 @@ def parse_query(query_string)

def parse_deep_object(parameter, parsed_query)
name = parameter.name
prop_regx = /^#{name}#{DEEP_PROP}/
prop_regx = @deep_object_regex[name]
properties_schema = @deep_object_properties[name]

parsed_query.each.with_object({}) do |(key, value), result|
Expand All @@ -94,7 +99,7 @@ def parse_deep_object(parameter, parsed_query)
end

def explode_value(value, parameter, is_array)
value = Array(value).map! { |v| Rack::Utils.unescape(v) }
value = Array(value)
if is_array
return value if parameter.explode?

Expand Down
2 changes: 1 addition & 1 deletion lib/openapi_parameters/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module OpenapiParameters
VERSION = '0.12.0'
VERSION = '0.12.1'
end
2 changes: 1 addition & 1 deletion openapi_parameters.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
spec.description =
'This parses HTTP query/path/header/cookie parameters exactly as described in an OpenAPI API description.'
spec.homepage = 'https://github.com/ahx/openapi_parameters'
spec.required_ruby_version = '>= 3.2.0'
spec.required_ruby_version = '>= 3.3.0'
spec.licenses = ['MIT']

spec.metadata['homepage_uri'] = spec.homepage
Expand Down
56 changes: 56 additions & 0 deletions spec/openapi_parameters/query-parameter-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,23 @@
B: 150
name: ada

- description: Does not double-unescape deepObject values (issue #25)
parameter:
- in: query
name: filter
explode: true
style: deepObject
schema:
type: object
properties:
from:
type: string
format: date-time
query_string: filter[from]=2026-01-01T00%3A00%3A00.000%2B00%3A00
unpacked_value:
filter:
from: "2026-01-01T00:00:00.000+00:00"

- description: Returns the plain query parameter value if it does not look like deepObject
parameter:
- in: query
Expand Down Expand Up @@ -477,6 +494,45 @@
color:
values: [100, 255]

- description: Does not double-unescape exploded deepObject array values (issue #25)
parameter:
in: query
name: filter
explode: true
style: deepObject
schema:
type: object
properties:
tags:
type: array
items:
type: string
query_string: filter[tags]=a%2Bb&filter[tags]=c%20d
unpacked_value:
filter:
tags:
- "a+b"
- "c d"

- description: Does not double-unescape single-value deepObject array values (issue #25)
parameter:
in: query
name: filter
explode: true
style: deepObject
schema:
type: object
properties:
tags:
type: array
items:
type: string
query_string: filter[tags]=a%2Bb
unpacked_value:
filter:
tags:
- "a+b"

- description: Does does not add rack-style parameters unless described
parameter:
in: query
Expand Down
10 changes: 5 additions & 5 deletions spec/openapi_parameters/query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

describe '#unpack' do
tests.each do |test|
description, parameter, query_string, unpacked_value = test.values_at('description', 'parameter',
'query_string', 'unpacked_value')
if unpacked_value
description = test['description']
if test['unpacked_value']
it description do
parameter, query_string, unpacked_value = test.values_at('parameter', 'query_string', 'unpacked_value')
options = test['options'].to_h.transform_keys!(&:to_sym)
parameter = [parameter] unless parameter.is_a?(Array)
value = described_class.new(parameter, **options).unpack(query_string)
Expand All @@ -20,8 +20,8 @@

next unless test.key?('unknown_values')

unknown_values = test['unknown_values']
it "#{'Unknown values ' if unpacked_value}#{description}" do
it "#{'Unknown values ' if test['unpacked_value']}#{description}" do
parameter, query_string, unknown_values = test.values_at('parameter', 'query_string', 'unknown_values')
options = test['options'].to_h.transform_keys!(&:to_sym)
parameter = [parameter] unless parameter.is_a?(Array)
value = described_class.new(parameter, **options).unknown_values(query_string)
Expand Down
Loading