rspec-openapi 0.14.0 → 0.16.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: 032e7acfd3a1818b7bffc9130fb58116cc8f8e2045d40e426cabb9c47d37d323
4
- data.tar.gz: 4dad6278000051a228fbe41f0210931411ddb119730055bd9262bd8dfd18ea23
3
+ metadata.gz: 61aad8140991defb871eca3436054e43a6e933d6062ac813c987faf93aa767b5
4
+ data.tar.gz: 8c3d02cf5be2e31131c1d855528f597b0bd604d306dba79e91c782448ae111a3
5
5
  SHA512:
6
- metadata.gz: 75d7e8fb034bffdb1ed6d5dafbd1acdffd45d28db52bcb39c5606dad7bac41656415b2b0473a9aaed24bc2a9577ab9dd0bc8c413f547fabaaa5e4df7fe62f806
7
- data.tar.gz: 2489d638c378f1f061ba992804761af38795a414535df628a5a3b419fcc73f11b116a23bb9d1a3f5a90f687fb30a70e220417463f75c33519194068440e155a2
6
+ metadata.gz: 2b879c9733c1704b94c2f5649de5a3200df3bbdac2b5cb76d668e94da32a45262bf40301d74359f30a86af7622851131243c8e95e3ffcfbc716bbd1d6765b47f
7
+ data.tar.gz: 16f048fae7721a7986de3dc0a293a29f1986085c55cb6fc793d596826e3fedff0540de4ab94dca2a3363a3c233c58f7d2f63fdcf8ec66793eed4ae72fe237a48
@@ -19,7 +19,7 @@ jobs:
19
19
  - name: Set up Ruby
20
20
  uses: ruby/setup-ruby@v1
21
21
  with:
22
- ruby-version: 2.6
22
+ ruby-version: 3.3
23
23
  bundler-cache: true
24
24
 
25
25
  - name: Rubocop run
@@ -16,8 +16,6 @@ jobs:
16
16
  fail-fast: false
17
17
  matrix:
18
18
  include:
19
- - ruby: ruby:2.5
20
- - ruby: ruby:2.6
21
19
  - ruby: ruby:2.7
22
20
  - ruby: ruby:3.0
23
21
  - ruby: ruby:3.1
@@ -36,11 +34,6 @@ jobs:
36
34
  - uses: actions/checkout@v4
37
35
  - name: bundle install
38
36
  run: bundle install -j$(nproc) --retry 3
39
- - name: install simplecov-fork only for minitest with coverage
40
- run: |
41
- gem install specific_install
42
- gem specific_install https://github.com/exoego/simplecov.git branch-fix
43
- if: matrix.coverage == 'coverage'
44
37
  - run: bundle exec rspec
45
38
  timeout-minutes: 1
46
39
  - run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
data/.rubocop.yml CHANGED
@@ -2,7 +2,8 @@ inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
4
  NewCops: enable
5
- TargetRubyVersion: 2.5
5
+ SuggestExtensions: false
6
+ TargetRubyVersion: 2.7
6
7
  Exclude:
7
8
  - 'spec/rails/**/*'
8
9
  - 'vendor/**/*'
data/.rubocop_todo.yml CHANGED
@@ -1,12 +1,12 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-01-13 11:12:43 UTC using RuboCop version 1.50.2.
3
+ # on 2024-03-25 05:35:37 UTC using RuboCop version 1.62.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 9
9
+ # Offense count: 11
10
10
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
11
11
  Metrics/AbcSize:
12
12
  Max: 48
@@ -14,19 +14,19 @@ Metrics/AbcSize:
14
14
  # Offense count: 2
15
15
  # Configuration parameters: CountComments, CountAsOne.
16
16
  Metrics/ClassLength:
17
- Max: 192
17
+ Max: 207
18
18
 
19
- # Offense count: 5
19
+ # Offense count: 8
20
20
  # Configuration parameters: AllowedMethods, AllowedPatterns.
21
21
  Metrics/CyclomaticComplexity:
22
22
  Max: 13
23
23
 
24
- # Offense count: 16
24
+ # Offense count: 19
25
25
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
26
26
  Metrics/MethodLength:
27
- Max: 31
27
+ Max: 34
28
28
 
29
- # Offense count: 1
29
+ # Offense count: 3
30
30
  # Configuration parameters: AllowedMethods, AllowedPatterns.
31
31
  Metrics/PerceivedComplexity:
32
32
  Max: 13
data/README.md CHANGED
@@ -29,7 +29,7 @@ $ OPENAPI=1 bundle exec rspec
29
29
 
30
30
  ### Example
31
31
 
32
- Let's say you have [a request spec](./spec/requests/rails/tables_spec.rb) like this:
32
+ Let's say you have [a request spec](https://github.com/exoego/rspec-openapi/blob/24e5c567c2e90945c7a41f19f71634ac028cc314/spec/requests/rails_spec.rb#L38) like this:
33
33
 
34
34
  ```rb
35
35
  RSpec.describe 'Tables', type: :request do
@@ -23,28 +23,28 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
23
23
  # 0 1 2 ^...............................^
24
24
  # ["components", "schema", "Table", "properties", "owner", "properties", "company", "$ref"]
25
25
  # 0 1 2 ^...........................................^
26
- needle = paths.reject { |path| path.is_a?(Integer) || path == 'oneOf' }
26
+ needle = paths.reject { |path| path.is_a?(Integer) || path == :oneOf }
27
27
  needle = needle.slice(2, needle.size - 3)
28
28
  nested_schema = fresh_schemas.dig(*needle)
29
29
 
30
30
  # Skip if the property using $ref is not found in the parent schema. The property may be removed.
31
31
  next if nested_schema.nil?
32
32
 
33
- schema_name = base.dig(*paths)&.gsub('#/components/schemas/', '')
33
+ schema_name = base.dig(*paths)&.gsub('#/components/schemas/', '')&.to_sym
34
34
  fresh_schemas[schema_name] ||= {}
35
35
  RSpec::OpenAPI::SchemaMerger.merge!(fresh_schemas[schema_name], nested_schema)
36
36
  end
37
37
 
38
- RSpec::OpenAPI::SchemaMerger.merge!(base, { 'components' => { 'schemas' => fresh_schemas } })
39
- RSpec::OpenAPI::SchemaCleaner.cleanup_components_schemas!(base, { 'components' => { 'schemas' => fresh_schemas } })
38
+ RSpec::OpenAPI::SchemaMerger.merge!(base, { components: { schemas: fresh_schemas } })
39
+ RSpec::OpenAPI::SchemaCleaner.cleanup_components_schemas!(base, { components: { schemas: fresh_schemas } })
40
40
  end
41
41
 
42
42
  private
43
43
 
44
44
  def build_fresh_schemas(references, base, fresh)
45
45
  references.inject({}) do |acc, paths|
46
- ref_link = dig_schema(base, paths)['$ref']
47
- schema_name = ref_link.gsub('#/components/schemas/', '')
46
+ ref_link = dig_schema(base, paths)[:$ref]
47
+ schema_name = ref_link.to_s.gsub('#/components/schemas/', '')
48
48
  schema_body = dig_schema(fresh, paths.reject { |path| path.is_a?(Integer) })
49
49
 
50
50
  RSpec::OpenAPI::SchemaMerger.merge!(acc, { schema_name => schema_body })
@@ -52,9 +52,11 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
52
52
  end
53
53
 
54
54
  def dig_schema(obj, paths)
55
- item_schema = obj.dig(*paths, 'schema', 'items')
56
- object_schema = obj.dig(*paths, 'schema')
57
- one_of_schema = obj.dig(*paths.take(paths.size - 1), 'schema', 'oneOf', paths.last)
55
+ # Response code can be an integer
56
+ paths = paths.map { |path| path.is_a?(Integer) ? path : path.to_sym }
57
+ item_schema = obj.dig(*paths, :schema, :items)
58
+ object_schema = obj.dig(*paths, :schema)
59
+ one_of_schema = obj.dig(*paths.take(paths.size - 1), :schema, :oneOf, paths.last)
58
60
 
59
61
  item_schema || object_schema || one_of_schema
60
62
  end
@@ -85,12 +87,12 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
85
87
  end
86
88
 
87
89
  def find_one_of_refs(base, paths)
88
- dig_schema(base, paths)&.dig('oneOf')&.map&.with_index do |schema, index|
89
- paths + [index] if schema&.dig('$ref')&.start_with?('#/components/schemas/')
90
+ dig_schema(base, paths)&.dig(:oneOf)&.map&.with_index do |schema, index|
91
+ paths + [index] if schema&.dig(:$ref)&.start_with?('#/components/schemas/')
90
92
  end&.compact
91
93
  end
92
94
 
93
95
  def find_object_refs(base, paths)
94
- [paths] if dig_schema(base, paths)&.dig('$ref')&.start_with?('#/components/schemas/')
96
+ [paths] if dig_schema(base, paths)&.dig(:$ref)&.start_with?('#/components/schemas/')
95
97
  end
96
98
  end
@@ -5,7 +5,7 @@ class << RSpec::OpenAPI::HashHelper = Object.new
5
5
  case obj
6
6
  when Hash
7
7
  obj.each.flat_map do |k, v|
8
- k = k.to_s
8
+ k = k.to_sym
9
9
  [[k]] + paths_to_all_fields(v).map { |x| [k, *x] }
10
10
  end
11
11
  when Array
@@ -18,10 +18,10 @@ class << RSpec::OpenAPI::HashHelper = Object.new
18
18
  end
19
19
 
20
20
  def matched_paths(obj, selector)
21
- selector_parts = selector.split('.').map(&:to_s)
21
+ selector_parts = selector.split('.').map(&:to_sym)
22
22
  paths_to_all_fields(obj).select do |key_parts|
23
23
  key_parts.size == selector_parts.size && key_parts.zip(selector_parts).all? do |kp, sp|
24
- kp == sp || (sp == '*' && !kp.nil?)
24
+ kp == sp || (sp == :* && !kp.nil?)
25
25
  end
26
26
  end
27
27
  end
@@ -29,7 +29,9 @@ class << RSpec::OpenAPI::HashHelper = Object.new
29
29
  def matched_paths_deeply_nested(obj, begin_selector, end_selector)
30
30
  path_depth_sizes = paths_to_all_fields(obj).map(&:size).uniq
31
31
  path_depth_sizes.map do |depth|
32
- diff = depth - begin_selector.count('.') - end_selector.count('.')
32
+ begin_selector_count = begin_selector.is_a?(Symbol) ? 0 : begin_selector.count('.')
33
+ end_selector_count = end_selector.is_a?(Symbol) ? 0 : end_selector.count('.')
34
+ diff = depth - begin_selector_count - end_selector_count
33
35
  if diff >= 0
34
36
  selector = "#{begin_selector}.#{'*.' * diff}#{end_selector}"
35
37
  matched_paths(obj, selector)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class << RSpec::OpenAPI::KeyTransformer = Object.new
4
+ def symbolize(value)
5
+ case value
6
+ when Hash
7
+ value.to_h { |k, v| [k.to_sym, symbolize(v)] }
8
+ when Array
9
+ value.map { |v| symbolize(v) }
10
+ else
11
+ value
12
+ end
13
+ end
14
+
15
+ def stringify(value)
16
+ case value
17
+ when Hash
18
+ value.to_h { |k, v| [k.to_s, stringify(v)] }
19
+ when Array
20
+ value.map { |v| stringify(v) }
21
+ else
22
+ value
23
+ end
24
+ end
25
+ end
@@ -12,7 +12,7 @@ module RSpec::OpenAPI::Minitest
12
12
  file_path = method(name).source_location.first
13
13
  human_name = name.sub(/^test_/, '').gsub('_', ' ')
14
14
  example = Example.new(self, human_name, {}, file_path)
15
- path = RSpec::OpenAPI.path.yield_self { |p| p.is_a?(Proc) ? p.call(example) : p }
15
+ path = RSpec::OpenAPI.path.then { |p| p.is_a?(Proc) ? p.call(example) : p }
16
16
  record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
17
17
  RSpec::OpenAPI.path_records[path] << record if record
18
18
  end
@@ -45,6 +45,6 @@ if ENV['OPENAPI']
45
45
  Minitest.after_run do
46
46
  result_recorder = RSpec::OpenAPI::ResultRecorder.new(RSpec::OpenAPI.path_records)
47
47
  result_recorder.record_results!
48
- puts result_record.error_message if result_recorder.errors?
48
+ puts result_recorder.error_message if result_recorder.errors?
49
49
  end
50
50
  end
@@ -33,7 +33,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
33
33
  description: description,
34
34
  security: security,
35
35
  status: response.status,
36
- response_body: safe_parse_body(response),
36
+ response_body: safe_parse_body(response, response.media_type),
37
37
  response_headers: response_headers,
38
38
  response_content_type: response.media_type,
39
39
  response_content_disposition: response.header['Content-Disposition'],
@@ -42,7 +42,10 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
42
42
 
43
43
  private
44
44
 
45
- def safe_parse_body(response)
45
+ def safe_parse_body(response, media_type)
46
+ # Use raw body, because Nokogiri-parsed HTML are modified (new lines injection, meta injection, and so on) :(
47
+ return response.body if media_type == 'text/html'
48
+
46
49
  response.parsed_body
47
50
  rescue JSON::ParserError
48
51
  nil
@@ -50,7 +53,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
50
53
 
51
54
  def extract_headers(request, response)
52
55
  request_headers = RSpec::OpenAPI.request_headers.each_with_object([]) do |header, headers_arr|
53
- header_key = header.gsub('-', '_').upcase
56
+ header_key = header.gsub('-', '_').upcase.to_sym
54
57
  header_value = request.get_header(['HTTP', header_key].join('_')) || request.get_header(header_key)
55
58
  headers_arr << [header, header_value] if header_value
56
59
  end
@@ -4,7 +4,7 @@ require 'rspec/core'
4
4
 
5
5
  RSpec.configuration.after(:each) do |example|
6
6
  if RSpec::OpenAPI.example_types.include?(example.metadata[:type]) && example.metadata[:openapi] != false
7
- path = RSpec::OpenAPI.path.yield_self { |p| p.is_a?(Proc) ? p.call(example) : p }
7
+ path = RSpec::OpenAPI.path.then { |p| p.is_a?(Proc) ? p.call(example) : p }
8
8
  record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
9
9
  RSpec::OpenAPI.path_records[path] << record if record
10
10
  end
@@ -13,24 +13,29 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
13
13
 
14
14
  if record.response_body
15
15
  disposition = normalize_content_disposition(record.response_content_disposition)
16
- response[:content] = {
17
- normalize_content_type(record.response_content_type) => {
18
- schema: build_property(record.response_body, disposition: disposition),
19
- example: response_example(record, disposition: disposition),
20
- }.compact,
21
- }
16
+
17
+ has_content = !normalize_content_type(record.response_content_type).nil?
18
+ if has_content
19
+ response[:content] = {
20
+ normalize_content_type(record.response_content_type) => {
21
+ schema: build_property(record.response_body, disposition: disposition),
22
+ example: response_example(record, disposition: disposition),
23
+ }.compact,
24
+ }
25
+ end
22
26
  end
23
27
 
28
+ http_method = record.http_method.downcase
24
29
  {
25
30
  paths: {
26
31
  normalize_path(record.path) => {
27
- record.http_method.downcase => {
32
+ http_method => {
28
33
  summary: record.summary,
29
34
  tags: record.tags,
30
35
  operationId: record.operation_id,
31
36
  security: record.security,
32
37
  parameters: build_parameters(record),
33
- requestBody: build_request_body(record),
38
+ requestBody: http_method == 'get' ? nil : build_request_body(record),
34
39
  responses: {
35
40
  record.status.to_s => response,
36
41
  },
@@ -43,7 +48,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
43
48
  private
44
49
 
45
50
  def enrich_with_required_keys(obj)
46
- obj[:required] = obj[:properties]&.keys
51
+ obj[:required] = obj[:properties]&.keys || []
47
52
  obj
48
53
  end
49
54
 
@@ -119,7 +124,6 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
119
124
 
120
125
  def build_request_body(record)
121
126
  return nil if record.request_content_type.nil?
122
- return nil if record.request_params.empty?
123
127
  return nil if record.status >= 400
124
128
 
125
129
  {
@@ -27,7 +27,7 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
27
27
  cleanup_hash!(base, spec, 'paths.*.*')
28
28
 
29
29
  # cleanup parameters
30
- cleanup_array!(base, spec, 'paths.*.*.parameters', %w[name in])
30
+ cleanup_array!(base, spec, 'paths.*.*.parameters', %i[name in])
31
31
 
32
32
  # cleanup requestBody
33
33
  cleanup_hash!(base, spec, 'paths.*.*.requestBody.content.application/json.schema.properties.*')
@@ -40,7 +40,7 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
40
40
  end
41
41
 
42
42
  def cleanup_conflicting_security_parameters!(base)
43
- security_schemes = base.dig('components', 'securitySchemes') || {}
43
+ security_schemes = base.dig(:components, :securitySchemes) || {}
44
44
 
45
45
  return if security_schemes.empty?
46
46
 
@@ -65,22 +65,22 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
65
65
  paths_to_objects.each do |path|
66
66
  parent = base.dig(*path.take(path.length - 1))
67
67
  # "required" array must not be present if empty
68
- parent.delete('required') if parent['required'] && parent['required'].empty?
68
+ parent.delete(:required) if parent[:required] && parent[:required].empty?
69
69
  end
70
70
  end
71
71
 
72
72
  private
73
73
 
74
74
  def remove_parameters_conflicting_with_security_sceheme!(path_definition, security_scheme, security_scheme_name)
75
- return unless path_definition['security']
76
- return unless path_definition['parameters']
77
- return unless path_definition.dig('security', 0).keys.include?(security_scheme_name)
75
+ return unless path_definition[:security]
76
+ return unless path_definition[:parameters]
77
+ return unless path_definition.dig(:security, 0).keys.include?(security_scheme_name)
78
78
 
79
- path_definition['parameters'].reject! do |parameter|
80
- parameter['in'] == security_scheme['in'] && # same location (ie. header)
81
- parameter['name'] == security_scheme['name'] # same name (ie. AUTHORIZATION)
79
+ path_definition[:parameters].reject! do |parameter|
80
+ parameter[:in] == security_scheme[:in] && # same location (ie. header)
81
+ parameter[:name] == security_scheme[:name] # same name (ie. AUTHORIZATION)
82
82
  end
83
- path_definition.delete('parameters') if path_definition['parameters'].empty?
83
+ path_definition.delete(:parameters) if path_definition[:parameters].empty?
84
84
  end
85
85
 
86
86
  def cleanup_array!(base, spec, selector, fields_for_identity = [])
@@ -15,7 +15,7 @@ class RSpec::OpenAPI::SchemaFile
15
15
  spec = read
16
16
  block.call(spec)
17
17
  ensure
18
- write(spec)
18
+ write(RSpec::OpenAPI::KeyTransformer.stringify(spec))
19
19
  end
20
20
 
21
21
  private
@@ -24,7 +24,7 @@ class RSpec::OpenAPI::SchemaFile
24
24
  def read
25
25
  return {} unless File.exist?(@path)
26
26
 
27
- YAML.safe_load(File.read(@path)) # this can also parse JSON
27
+ RSpec::OpenAPI::KeyTransformer.symbolize(YAML.safe_load(File.read(@path))) # this can also parse JSON
28
28
  end
29
29
 
30
30
  # @param [Hash] spec
@@ -4,32 +4,20 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
4
4
  # @param [Hash] base
5
5
  # @param [Hash] spec
6
6
  def merge!(base, spec)
7
- spec = normalize_keys(spec)
7
+ spec = RSpec::OpenAPI::KeyTransformer.symbolize(spec)
8
+ base.replace(RSpec::OpenAPI::KeyTransformer.symbolize(base))
8
9
  merge_schema!(base, spec)
9
10
  end
10
11
 
11
12
  private
12
13
 
13
- def normalize_keys(spec)
14
- case spec
15
- when Hash
16
- spec.map do |key, value|
17
- [key.to_s, normalize_keys(value)]
18
- end.to_h
19
- when Array
20
- spec.map { |s| normalize_keys(s) }
21
- else
22
- spec
23
- end
24
- end
25
-
26
14
  # Not doing `base.replace(deep_merge(base, spec))` to preserve key orders.
27
15
  # Also this needs to be aware of OpenAPI details because a Hash-like structure
28
16
  # may be an array whose Hash elements have a key name.
29
17
  #
30
18
  # TODO: Should we probably force-merge `summary` regardless of manual modifications?
31
19
  def merge_schema!(base, spec)
32
- if (options = base['oneOf'])
20
+ if (options = base[:oneOf])
33
21
  merge_closest_match!(options, spec)
34
22
 
35
23
  return base
@@ -37,13 +25,13 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
37
25
 
38
26
  spec.each do |key, value|
39
27
  if base[key].is_a?(Hash) && value.is_a?(Hash)
40
- merge_schema!(base[key], value) unless base[key].key?('$ref')
28
+ merge_schema!(base[key], value) unless base[key].key?(:$ref)
41
29
  elsif base[key].is_a?(Array) && value.is_a?(Array)
42
30
  # parameters need to be merged as if `name` and `in` were the Hash keys.
43
31
  merge_arrays(base, key, value)
44
32
  else
45
33
  # do not ADD `properties` or `required` fields if `additionalProperties` field is present
46
- base[key] = value unless base.key?('additionalProperties') && %w[properties required].include?(key)
34
+ base[key] = value unless base.key?(:additionalProperties) && %i[properties required].include?(key)
47
35
  end
48
36
  end
49
37
  base
@@ -51,9 +39,9 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
51
39
 
52
40
  def merge_arrays(base, key, value)
53
41
  base[key] = case key
54
- when 'parameters'
42
+ when :parameters
55
43
  merge_parameters(base, key, value)
56
- when 'required'
44
+ when :required
57
45
  # Preserve properties that appears in all test cases
58
46
  value & base[key]
59
47
  else
@@ -65,13 +53,13 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
65
53
  def merge_parameters(base, key, value)
66
54
  all_parameters = value | base[key]
67
55
 
68
- unique_base_parameters = base[key].index_by { |parameter| [parameter['name'], parameter['in']] }
56
+ unique_base_parameters = base[key].index_by { |parameter| [parameter[:name], parameter[:in]] }
69
57
  all_parameters = all_parameters.map do |parameter|
70
- base_parameter = unique_base_parameters[[parameter['name'], parameter['in']]] || {}
58
+ base_parameter = unique_base_parameters[[parameter[:name], parameter[:in]]] || {}
71
59
  base_parameter ? base_parameter.merge(parameter) : parameter
72
60
  end
73
61
 
74
- all_parameters.uniq! { |param| param.slice('name', 'in') }
62
+ all_parameters.uniq! { |param| param.slice(:name, :in) }
75
63
  base[key] = all_parameters
76
64
  end
77
65
 
@@ -80,7 +68,7 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
80
68
  def merge_closest_match!(options, spec)
81
69
  score, option = options.map { |option| [similarity(option, spec), option] }.max_by(&:first)
82
70
 
83
- return if option&.key?('$ref')
71
+ return if option&.key?(:$ref)
84
72
 
85
73
  if score.to_f > SIMILARITY_THRESHOLD
86
74
  merge_schema!(option, spec)
@@ -97,7 +85,7 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
97
85
  when [Array, Array]
98
86
  (first & second).size / [first.size, second.size].max.to_f
99
87
  when [Hash, Hash]
100
- return 1 if first.merge(second).key?('$ref')
88
+ return 1 if first.merge(second).key?(:$ref)
101
89
 
102
90
  intersection = first.keys & second.keys
103
91
  total_size = [first.size, second.size].max.to_f
@@ -29,7 +29,7 @@ class << RSpec::OpenAPI::SchemaSorter = Object.new
29
29
  end
30
30
 
31
31
  def deep_sort_hash!(hash)
32
- sorted = hash.entries.sort_by { |k, _| k }.to_h
32
+ sorted = hash.entries.sort_by { |k, _| k.to_s }.to_h.transform_keys(&:to_sym)
33
33
  hash.replace(sorted)
34
34
  end
35
35
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.14.0'
5
+ VERSION = '0.16.0'
6
6
  end
7
7
  end
data/lib/rspec/openapi.rb CHANGED
@@ -10,6 +10,7 @@ require 'rspec/openapi/schema_file'
10
10
  require 'rspec/openapi/schema_merger'
11
11
  require 'rspec/openapi/schema_cleaner'
12
12
  require 'rspec/openapi/schema_sorter'
13
+ require 'rspec/openapi/key_transformer'
13
14
 
14
15
  require 'rspec/openapi/minitest_hooks' if Object.const_defined?('Minitest')
15
16
  require 'rspec/openapi/rspec_hooks' if ENV['OPENAPI'] && Object.const_defined?('RSpec')
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = 'Generate OpenAPI from RSpec request specs'
13
13
  spec.homepage = 'https://github.com/exoego/rspec-openapi'
14
14
  spec.license = 'MIT'
15
- spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
16
16
 
17
17
  spec.metadata = {
18
18
  'homepage_uri' => 'https://github.com/exoego/rspec-openapi',
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-openapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-03-08 00:00:00.000000000 Z
12
+ date: 2024-03-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -67,6 +67,7 @@ files:
67
67
  - lib/rspec/openapi/components_updater.rb
68
68
  - lib/rspec/openapi/default_schema.rb
69
69
  - lib/rspec/openapi/hash_helper.rb
70
+ - lib/rspec/openapi/key_transformer.rb
70
71
  - lib/rspec/openapi/minitest_hooks.rb
71
72
  - lib/rspec/openapi/record.rb
72
73
  - lib/rspec/openapi/record_builder.rb
@@ -88,7 +89,7 @@ licenses:
88
89
  metadata:
89
90
  homepage_uri: https://github.com/exoego/rspec-openapi
90
91
  source_code_uri: https://github.com/exoego/rspec-openapi
91
- changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.14.0
92
+ changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.16.0
92
93
  rubygems_mfa_required: 'true'
93
94
  post_install_message:
94
95
  rdoc_options: []
@@ -98,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
98
99
  requirements:
99
100
  - - ">="
100
101
  - !ruby/object:Gem::Version
101
- version: 2.5.0
102
+ version: 2.7.0
102
103
  required_rubygems_version: !ruby/object:Gem::Requirement
103
104
  requirements:
104
105
  - - ">="