rspec-openapi 0.11.0 → 0.12.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 +4 -4
- data/.github/workflows/test.yml +4 -1
- data/CHANGELOG.md +7 -2
- data/lib/rspec/openapi/components_updater.rb +25 -5
- data/lib/rspec/openapi/hash_helper.rb +4 -0
- data/lib/rspec/openapi/schema_merger.rb +41 -0
- data/lib/rspec/openapi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b221d36e6ff92306e1bd720b4bb07e114839c8e047ad530f6ac4cb2a11eb4ae5
|
4
|
+
data.tar.gz: 5f5daf56dddc8a7b9477f961611d3bd4550eb498aa38d166035e5b18fe7027bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18410768ff613a19b39f910e20e58ecda87337095b354e718bdb5f6b6e2713429acd26a289fd4ffa89a493348aa226561d89340d9685d37caa30b44c54d17be8
|
7
|
+
data.tar.gz: 5542bf85a5f1d46eacbdc0936b57ae197279bf618fa1628082e7d33c5600a2583f450426999dbc6eea4d4a401ffb307ea5d207b551aa27d586eb4682e572c9fb
|
data/.github/workflows/test.yml
CHANGED
@@ -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@
|
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.
|
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
|
-
##
|
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.
|
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')
|
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).
|
60
|
-
|
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
|
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.
|
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-
|
12
|
+
date: 2024-02-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|