rspec-openapi 0.11.0 → 0.13.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: 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: []