openapi_parameters 0.9.0.beta1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 333fd99034d125536a4a747d4c611e474ba8f2c41aa84d9c983f0cd7425b13cb
4
- data.tar.gz: '067223459914938a710d91de39125b4e8c7a7dc820b637fa5c3fa2bc1502465e'
3
+ metadata.gz: 52bca4aebd3d66fa0a3edc68d3ce71a24a223a31be135677a8fb9ea09a4055bc
4
+ data.tar.gz: '03230596b824ee0a59ca68048bcfc68e884af2cbbb3b14ea3952c1f917a7d87b'
5
5
  SHA512:
6
- metadata.gz: 653a7d0631784ed6ce34187307a973f08fee2560dceb93390d4d509f49c68acb6101d4cd1a274eb009df9020a81a0081089c24af4aeb6f64be43f4f46a9bdbfc
7
- data.tar.gz: e458b12c372881a3e2503e891348da76ad735fc197f66343c6ff74fd1dd8107d2054c553b246959c7ef3a793e52b4432ca2e3c2d955ef7b66d6f59968f2bd1ab
6
+ metadata.gz: e5bf0752ef3be5d397cd5f5cee010a5e89fc4c1a1821ec0b86411fb1a97bfae8e81be46f5fa94d5bd47940c921f0a0dab9641f591f6ceddecfc856dd881f34d7
7
+ data.tar.gz: 8244a7c1c93ef4c0c2ee88374c4bb72f3f6295a11637fff60b02ad96e9015e702b284251eff003309ebdc0bb8f6ccf38f85334a9be7d6801bcd0b6f48cc5e808
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
- - Add basic support unpacking query paramters that use deepObject and oneOf
3
+ ## [0.10.0] - 2025-11-23
4
+
5
+ - remove `OpenapiParameters::Parameter#definition` to clean up interface
6
+ - Add `OpenapiParameters::Query#unknown_values` to find unknown query parameters
7
+
8
+ ## [0.9.0] - 2025-11-07
9
+
10
+ - Add support for unpacking query parameters that use deepObject and oneOf,andOf,anyOf,if-them-else
4
11
 
5
12
  ## [0.8.0] - 2025-09-17
6
13
 
data/README.md CHANGED
@@ -28,6 +28,10 @@ query_parameters = OpenapiParameters::Query.new([{
28
28
  query_string = env['QUERY_STRING'] # => 'ids=1&ids=2'
29
29
  query_parameters.unpack(query_string) # => { 'ids' => [1, 2] }
30
30
 
31
+ # Find unknown query parameters
32
+ # Note that this does only return unknown top-level values
33
+ query_parameters.unknown_values('ids=1&ids=2&foo=bar') # => { 'foo' => 'bar' }
34
+
31
35
  path_parameters = OpenapiParameters::Path.new(parameters)
32
36
  route_params = env['route.params'] # This depends on the webframework you are using
33
37
  path_parameters.unpack(route_params) # => { 'ids' => [1, 2, 3] }
@@ -2,13 +2,7 @@
2
2
 
3
3
  module OpenapiParameters
4
4
  # @visibility private
5
- class ArrayConverter
6
- def initialize(schema)
7
- @schema = schema
8
- end
9
-
10
- attr_reader :schema
11
-
5
+ ArrayConverter = Data.define(:schema) do
12
6
  def call(value)
13
7
  return [] if value.nil? || value.empty?
14
8
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'array_converter'
4
+ require_relative 'object_converter'
4
5
 
5
6
  module OpenapiParameters
6
7
  # Tries to convert a request parameter value (string) to the type specified in the JSON Schema.
@@ -33,26 +34,18 @@ module OpenapiParameters
33
34
  value == 'false' ? false : value
34
35
  end
35
36
  when 'object'
36
- convert_object(value, schema)
37
+ ObjectConverter.new(schema).call(value)
37
38
  when 'array'
38
39
  ArrayConverter.new(schema).call(value)
39
40
  else
40
- if schema['properties']
41
- convert_object(value, schema)
41
+ if schema['properties'] || schema['oneOf'] || schema['allOf'] || schema['anyOf']
42
+ ObjectConverter.new(schema).call(value)
42
43
  else
43
44
  value
44
45
  end
45
46
  end
46
47
  end
47
48
 
48
- def convert_object(value, schema)
49
- return value unless value.is_a?(Hash)
50
-
51
- value.each_with_object({}) do |(key, val), hsh|
52
- hsh[key] = Converter.convert(val, schema['properties']&.fetch(key, nil))
53
- end
54
- end
55
-
56
49
  private
57
50
 
58
51
  def type(schema)
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenapiParameters
4
+ # @visibility private
5
+ ObjectConverter = Data.define(:schema) do
6
+ def self.get_properties(schema) # rubocop:disable Metrics
7
+ return nil if schema.nil? || schema.empty?
8
+
9
+ direct_props = schema['properties']
10
+ additional_props = schema['additionalProperties']
11
+
12
+ composition_props = []
13
+
14
+ %w[allOf oneOf anyOf].each do |keyword|
15
+ next unless (array = schema[keyword])
16
+
17
+ array.each do |sub_schema|
18
+ if (props = sub_schema['properties'])
19
+ composition_props << props
20
+ end
21
+ end
22
+ end
23
+
24
+ %w[then else].each do |keyword|
25
+ next unless (sub_schema = schema[keyword])
26
+
27
+ if (props = sub_schema['properties'])
28
+ composition_props << props
29
+ end
30
+ if (add_props = sub_schema['additionalProperties']) && add_props.is_a?(Hash) && !add_props.empty?
31
+ composition_props << add_props
32
+ end
33
+ end
34
+
35
+ composition_props << additional_props if additional_props.is_a?(Hash) && !additional_props.empty?
36
+
37
+ return direct_props if composition_props.empty? && direct_props
38
+ return nil if direct_props.nil? && composition_props.empty?
39
+
40
+ result = direct_props ? direct_props.dup : {}
41
+ composition_props.each { |props| result.merge!(props) }
42
+ result
43
+ end
44
+
45
+ def call(value)
46
+ return value unless value.is_a?(Hash)
47
+
48
+ properties = self.class.get_properties(schema)
49
+
50
+ value.each_with_object({}) do |(key, val), hsh|
51
+ hsh[key] = Converter.convert(val, properties&.fetch(key, nil))
52
+ end
53
+ end
54
+ end
55
+ end
@@ -13,7 +13,8 @@ module OpenapiParameters
13
13
  check_supported!
14
14
  end
15
15
 
16
- attr_reader :definition, :name
16
+ attr_reader :name
17
+ private attr_reader :definition
17
18
 
18
19
  def convert(value)
19
20
  @converter.call(value)
@@ -34,6 +34,24 @@ module OpenapiParameters
34
34
  end
35
35
  end
36
36
 
37
+ def unknown_values(query_string)
38
+ parsed_query = parse_query(query_string)
39
+ known_parameter_names = parameters.to_set(&:name)
40
+
41
+ unknown = parsed_query.each_with_object({}) do |(key, value), result|
42
+ # Skip parameters that are defined in the schema
43
+ next if known_parameter_names.include?(key)
44
+
45
+ # Skip deep object parameters that might belong to defined parameters
46
+ next if parameters.any? { |param| param.deep_object? && key.start_with?("#{param.name}[") }
47
+
48
+ result[key] = value
49
+ end
50
+ return if unknown.empty?
51
+
52
+ unknown
53
+ end
54
+
37
55
  attr_reader :parameters
38
56
  private attr_reader :remove_array_brackets, :parameter_property_schemas
39
57
 
@@ -49,18 +67,7 @@ module OpenapiParameters
49
67
 
50
68
  def build_properties_schema(parameter)
51
69
  schema = parameter.schema
52
- return unless schema
53
-
54
- props = schema['properties']
55
- return props if props
56
-
57
- combinations = schema.slice('allOf', 'oneOf', 'anyOf')
58
- if combinations.any?
59
- props = combinations.values.flat_map { |value| value.map { |sub| sub['properties'] }.compact }
60
- return {}.merge(*props.compact)
61
- end
62
-
63
- schema
70
+ ObjectConverter.get_properties(schema) if schema
64
71
  end
65
72
 
66
73
  DEEP_PROP = '\[([\w-]+)\]$'
@@ -69,13 +76,15 @@ module OpenapiParameters
69
76
  def parse_deep_object(parameter, parsed_query)
70
77
  name = parameter.name
71
78
  prop_regx = /^#{name}#{DEEP_PROP}/
79
+ properties_schema = build_properties_schema(parameter)
80
+
72
81
  parsed_query.each.with_object({}) do |(key, value), result|
73
82
  next unless parsed_query.key?(key)
74
83
 
75
84
  prop_key = key.match(prop_regx)&.[](1)
76
85
  next if prop_key.nil?
77
86
 
78
- is_array = parameter.schema&.dig('properties', prop_key, 'type') == 'array'
87
+ is_array = properties_schema&.dig(prop_key, 'type') == 'array'
79
88
  value = explode_value(value, parameter, is_array)
80
89
  result[prop_key] = value
81
90
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiParameters
4
- VERSION = '0.9.0.beta1'
4
+ VERSION = '0.10.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_parameters
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0.beta1
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-11-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rack
@@ -43,6 +42,7 @@ files:
43
42
  - lib/openapi_parameters/header.rb
44
43
  - lib/openapi_parameters/headers_hash.rb
45
44
  - lib/openapi_parameters/not_supported_error.rb
45
+ - lib/openapi_parameters/object_converter.rb
46
46
  - lib/openapi_parameters/parameter.rb
47
47
  - lib/openapi_parameters/path.rb
48
48
  - lib/openapi_parameters/query.rb
@@ -56,7 +56,6 @@ metadata:
56
56
  source_code_uri: https://github.com/ahx/openapi_parameters
57
57
  changelog_uri: https://github.com/ahx/openapi_parameters/blob/main/CHANGELOG.md
58
58
  rubygems_mfa_required: 'true'
59
- post_install_message:
60
59
  rdoc_options: []
61
60
  require_paths:
62
61
  - lib
@@ -64,15 +63,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
64
63
  requirements:
65
64
  - - ">="
66
65
  - !ruby/object:Gem::Version
67
- version: 3.1.0
66
+ version: 3.2.0
68
67
  required_rubygems_version: !ruby/object:Gem::Requirement
69
68
  requirements:
70
- - - ">"
69
+ - - ">="
71
70
  - !ruby/object:Gem::Version
72
- version: 1.3.1
71
+ version: '0'
73
72
  requirements: []
74
- rubygems_version: 3.4.6
75
- signing_key:
73
+ rubygems_version: 3.6.7
76
74
  specification_version: 4
77
75
  summary: openapi_parameters is an OpenAPI aware parameter parser
78
76
  test_files: []