rspec-openapi 0.11.0 → 0.13.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: 0a48ebba5d2201f35bb94f47d5bd649444bb9c58ae1f0ef91f57e315553f9ef2
4
- data.tar.gz: 71d4dec68449c2e14b4d3ce7058622b7c56629bee2814e04b1f16e5e817a7bf2
3
+ metadata.gz: a5139294ebac456aadd2c09832f55cb214b7bf4ba9893919445823455cbee18a
4
+ data.tar.gz: b77813ff2d18290e714482bfa45350f69b182a638162d083923acb9b21124cfe
5
5
  SHA512:
6
- metadata.gz: 4c984f5dbd1a4587a9d34686c50e06a832051215f5e22abc8a96781ecebe57e0bdddabeef093db06f5bea23f58e70322a7574168f621e8765721ab2c2cf19c48
7
- data.tar.gz: 2877daab4753106d04275e3189b8a8e648b7268a6c6d7e422e94d5bfb230064a2e875e73531abe6930bfa6c8b7cdefe7d06ee512cdcb4e22adb618915349514b
6
+ metadata.gz: 58de63dbf9608beb5f313eb57e6e1e4386eefa23fd1b572d17b06319f571ca3c6b1ac172a7886b3dcd42e14feb1f5f60ebd5a532951441193f8400d66e5229ce
7
+ data.tar.gz: 95765054acd231993f058a9f3a31a596c56ff143af9f1dc576bfb2f201773e611025329069b8c49751f50d3bf14b16a4f83ef863587d2f7815209f0afcdccb4a
@@ -43,9 +43,12 @@ jobs:
43
43
  if: matrix.coverage == 'coverage'
44
44
  - run: bundle exec rspec
45
45
  timeout-minutes: 1
46
+ - run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
47
+ name: codecov-action@v4 workaround
46
48
  - name: Upload coverage reports
47
- uses: codecov/codecov-action@v3
49
+ uses: codecov/codecov-action@v4
48
50
  if: matrix.coverage == 'coverage'
49
51
  with:
50
52
  fail_ci_if_error: true
51
53
  files: ./coverage/coverage.xml
54
+ token: ${{ secrets.CODECOV_TOKEN }}
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
- ## v0.11.0
1
+ # THIS CHANGELOG IS DEPRECATED!!
2
+
3
+ Refer https://github.com/exoego/rspec-openapi/releases instead.
4
+
5
+ ## v0.12.0
2
6
 
3
- ## What's Changed
7
+ - feat: Initial support of complex schema with manually-added `oneOf`
8
+ [#174](https://github.com/exoego/rspec-openapi/pull/174)
9
+ - chore: Test with Ruby 3.3 and Rails 7.1.x
10
+ [#169](https://github.com/exoego/rspec-openapi/pull/169)
11
+
12
+ ## v0.11.0
4
13
  - feat: Allow path-based config overrides
5
14
  [#162](https://github.com/exoego/rspec-openapi/pull/162)
6
15
  - enhancement: Sort HTTP methods, response status codes, and contents lexicographically
@@ -23,7 +23,8 @@ 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.slice(2, paths.size - 3)
26
+ needle = paths.reject { |path| path.is_a?(Integer) || path == 'oneOf' }
27
+ needle = needle.slice(2, needle.size - 3)
27
28
  nested_schema = fresh_schemas.dig(*needle)
28
29
 
29
30
  # Skip if the property using $ref is not found in the parent schema. The property may be removed.
@@ -44,20 +45,28 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
44
45
  references.inject({}) do |acc, paths|
45
46
  ref_link = dig_schema(base, paths)['$ref']
46
47
  schema_name = ref_link.gsub('#/components/schemas/', '')
47
- schema_body = dig_schema(fresh, paths)
48
+ schema_body = dig_schema(fresh, paths.reject { |path| path.is_a?(Integer) })
49
+
48
50
  RSpec::OpenAPI::SchemaMerger.merge!(acc, { schema_name => schema_body })
49
51
  end
50
52
  end
51
53
 
52
54
  def dig_schema(obj, paths)
53
- obj.dig(*paths, 'schema', 'items') || obj.dig(*paths, 'schema')
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)
58
+
59
+ item_schema || object_schema || one_of_schema
54
60
  end
55
61
 
56
62
  def paths_to_top_level_refs(base)
57
63
  request_bodies = RSpec::OpenAPI::HashHelper.matched_paths(base, 'paths.*.*.requestBody.content.application/json')
58
64
  responses = RSpec::OpenAPI::HashHelper.matched_paths(base, 'paths.*.*.responses.*.content.application/json')
59
- (request_bodies + responses).select do |paths|
60
- dig_schema(base, paths)&.dig('$ref')&.start_with?('#/components/schemas/')
65
+ (request_bodies + responses).flat_map do |paths|
66
+ object_paths = find_object_refs(base, paths)
67
+ one_of_paths = find_one_of_refs(base, paths)
68
+
69
+ object_paths || one_of_paths || []
61
70
  end
62
71
  end
63
72
 
@@ -65,6 +74,7 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
65
74
  nested_refs = [
66
75
  *RSpec::OpenAPI::HashHelper.matched_paths_deeply_nested(base, 'components.schemas', 'properties.*.$ref'),
67
76
  *RSpec::OpenAPI::HashHelper.matched_paths_deeply_nested(base, 'components.schemas', 'properties.*.items.$ref'),
77
+ *RSpec::OpenAPI::HashHelper.matched_paths_deeply_nested(base, 'components.schemas', 'oneOf.*.$ref'),
68
78
  ]
69
79
  # Reject already-generated schemas to reduce unnecessary loop
70
80
  nested_refs.reject do |paths|
@@ -73,4 +83,14 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
73
83
  generated_names.include?(schema_name)
74
84
  end
75
85
  end
86
+
87
+ 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
+ end&.compact
91
+ end
92
+
93
+ def find_object_refs(base, paths)
94
+ [paths] if dig_schema(base, paths)&.dig('$ref')&.start_with?('#/components/schemas/')
95
+ end
76
96
  end
@@ -8,6 +8,10 @@ class << RSpec::OpenAPI::HashHelper = Object.new
8
8
  k = k.to_s
9
9
  [[k]] + paths_to_all_fields(v).map { |x| [k, *x] }
10
10
  end
11
+ when Array
12
+ obj.flat_map.with_index do |value, i|
13
+ [[i]] + paths_to_all_fields(value).map { |x| [i, *x] }
14
+ end
11
15
  else
12
16
  []
13
17
  end
@@ -10,7 +10,7 @@ module RSpec::OpenAPI::Minitest
10
10
  result = super
11
11
  if ENV['OPENAPI'] && self.class.openapi?
12
12
  file_path = method(name).source_location.first
13
- human_name = name.sub(/^test_/, '').gsub(/_/, ' ')
13
+ human_name = name.sub(/^test_/, '').gsub('_', ' ')
14
14
  example = Example.new(self, human_name, {}, file_path)
15
15
  path = RSpec::OpenAPI.path.yield_self { |p| p.is_a?(Proc) ? p.call(example) : p }
16
16
  record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
@@ -48,7 +48,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
48
48
 
49
49
  def extract_headers(request, response)
50
50
  request_headers = RSpec::OpenAPI.request_headers.each_with_object([]) do |header, headers_arr|
51
- header_key = header.gsub(/-/, '_').upcase
51
+ header_key = header.gsub('-', '_').upcase
52
52
  header_value = request.get_header(['HTTP', header_key].join('_')) || request.get_header(header_key)
53
53
  headers_arr << [header, header_value] if header_value
54
54
  end
@@ -29,6 +29,12 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
29
29
  #
30
30
  # TODO: Should we probably force-merge `summary` regardless of manual modifications?
31
31
  def merge_schema!(base, spec)
32
+ if (options = base['oneOf'])
33
+ merge_closest_match!(options, spec)
34
+
35
+ return base
36
+ end
37
+
32
38
  spec.each do |key, value|
33
39
  if base[key].is_a?(Hash) && value.is_a?(Hash)
34
40
  merge_schema!(base[key], value) unless base[key].key?('$ref')
@@ -36,7 +42,8 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
36
42
  # parameters need to be merged as if `name` and `in` were the Hash keys.
37
43
  merge_arrays(base, key, value)
38
44
  else
39
- base[key] = value
45
+ # 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)
40
47
  end
41
48
  end
42
49
  base
@@ -67,4 +74,39 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
67
74
  all_parameters.uniq! { |param| param.slice('name', 'in') }
68
75
  base[key] = all_parameters
69
76
  end
77
+
78
+ SIMILARITY_THRESHOLD = 0.5
79
+
80
+ def merge_closest_match!(options, spec)
81
+ score, option = options.map { |option| [similarity(option, spec), option] }.max_by(&:first)
82
+
83
+ return if option&.key?('$ref')
84
+
85
+ if score.to_f > SIMILARITY_THRESHOLD
86
+ merge_schema!(option, spec)
87
+ else
88
+ options.push(spec)
89
+ end
90
+ end
91
+
92
+ def similarity(first, second)
93
+ return 1 if first == second
94
+
95
+ score =
96
+ case [first.class, second.class]
97
+ when [Array, Array]
98
+ (first & second).size / [first.size, second.size].max.to_f
99
+ when [Hash, Hash]
100
+ return 1 if first.merge(second).key?('$ref')
101
+
102
+ intersection = first.keys & second.keys
103
+ total_size = [first.size, second.size].max.to_f
104
+
105
+ intersection.sum { |key| similarity(first[key], second[key]) } / total_size
106
+ else
107
+ 0
108
+ end
109
+
110
+ score.finite? ? score : 0
111
+ end
70
112
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.11.0'
5
+ VERSION = '0.13.0'
6
6
  end
7
7
  end
@@ -14,9 +14,12 @@ Gem::Specification.new do |spec|
14
14
  spec.license = 'MIT'
15
15
  spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
16
 
17
- spec.metadata['homepage_uri'] = spec.homepage
18
- spec.metadata['source_code_uri'] = spec.homepage
19
- spec.metadata['changelog_uri'] = File.join(spec.homepage, 'blob/master/CHANGELOG.md')
17
+ spec.metadata = {
18
+ 'homepage_uri' => 'https://github.com/exoego/rspec-openapi',
19
+ 'source_code_uri' => 'https://github.com/exoego/rspec-openapi',
20
+ 'changelog_uri' => "https://github.com/exoego/rspec-openapi/releases/tag/v#{RSpec::OpenAPI::VERSION}",
21
+ 'rubygems_mfa_required' => 'true',
22
+ }
20
23
 
21
24
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
25
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
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.11.0
4
+ version: 0.13.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-01-13 00:00:00.000000000 Z
12
+ date: 2024-03-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -88,7 +88,7 @@ licenses:
88
88
  metadata:
89
89
  homepage_uri: https://github.com/exoego/rspec-openapi
90
90
  source_code_uri: https://github.com/exoego/rspec-openapi
91
- changelog_uri: https://github.com/exoego/rspec-openapi/blob/master/CHANGELOG.md
91
+ changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.13.0
92
92
  rubygems_mfa_required: 'true'
93
93
  post_install_message:
94
94
  rdoc_options: []