rspec-openapi 0.11.0 → 0.12.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: b221d36e6ff92306e1bd720b4bb07e114839c8e047ad530f6ac4cb2a11eb4ae5
4
+ data.tar.gz: 5f5daf56dddc8a7b9477f961611d3bd4550eb498aa38d166035e5b18fe7027bd
5
5
  SHA512:
6
- metadata.gz: 4c984f5dbd1a4587a9d34686c50e06a832051215f5e22abc8a96781ecebe57e0bdddabeef093db06f5bea23f58e70322a7574168f621e8765721ab2c2cf19c48
7
- data.tar.gz: 2877daab4753106d04275e3189b8a8e648b7268a6c6d7e422e94d5bfb230064a2e875e73531abe6930bfa6c8b7cdefe7d06ee512cdcb4e22adb618915349514b
6
+ metadata.gz: 18410768ff613a19b39f910e20e58ecda87337095b354e718bdb5f6b6e2713429acd26a289fd4ffa89a493348aa226561d89340d9685d37caa30b44c54d17be8
7
+ data.tar.gz: 5542bf85a5f1d46eacbdc0936b57ae197279bf618fa1628082e7d33c5600a2583f450426999dbc6eea4d4a401ffb307ea5d207b551aa27d586eb4682e572c9fb
@@ -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,11 @@
1
- ## v0.11.0
1
+ ## v0.12.0
2
+
3
+ - feat: Initial support of complex schema with manually-added `oneOf`
4
+ [#174](https://github.com/exoego/rspec-openapi/pull/174)
5
+ - chore: Test with Ruby 3.3 and Rails 7.1.x
6
+ [#169](https://github.com/exoego/rspec-openapi/pull/169)
2
7
 
3
- ## What's Changed
8
+ ## v0.11.0
4
9
  - feat: Allow path-based config overrides
5
10
  [#162](https://github.com/exoego/rspec-openapi/pull/162)
6
11
  - 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
@@ -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')
@@ -67,4 +73,39 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
67
73
  all_parameters.uniq! { |param| param.slice('name', 'in') }
68
74
  base[key] = all_parameters
69
75
  end
76
+
77
+ SIMILARITY_THRESHOLD = 0.5
78
+
79
+ def merge_closest_match!(options, spec)
80
+ score, option = options.map { |option| [similarity(option, spec), option] }.max_by(&:first)
81
+
82
+ return if option&.key?('$ref')
83
+
84
+ if score.to_f > SIMILARITY_THRESHOLD
85
+ merge_schema!(option, spec)
86
+ else
87
+ options.push(spec)
88
+ end
89
+ end
90
+
91
+ def similarity(first, second)
92
+ return 1 if first == second
93
+
94
+ score =
95
+ case [first.class, second.class]
96
+ when [Array, Array]
97
+ (first & second).size / [first.size, second.size].max.to_f
98
+ when [Hash, Hash]
99
+ return 1 if first.merge(second).key?('$ref')
100
+
101
+ intersection = first.keys & second.keys
102
+ total_size = [first.size, second.size].max.to_f
103
+
104
+ intersection.sum { |key| similarity(first[key], second[key]) } / total_size
105
+ else
106
+ 0
107
+ end
108
+
109
+ score.finite? ? score : 0
110
+ end
70
111
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.11.0'
5
+ VERSION = '0.12.0'
6
6
  end
7
7
  end
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.12.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-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack