rspec-openapi 0.14.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="