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 +4 -4
- data/.github/workflows/test.yml +4 -1
- data/CHANGELOG.md +11 -2
- data/lib/rspec/openapi/components_updater.rb +25 -5
- data/lib/rspec/openapi/hash_helper.rb +4 -0
- data/lib/rspec/openapi/minitest_hooks.rb +1 -1
- data/lib/rspec/openapi/record_builder.rb +1 -1
- data/lib/rspec/openapi/schema_merger.rb +43 -1
- data/lib/rspec/openapi/version.rb +1 -1
- data/rspec-openapi.gemspec +6 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5139294ebac456aadd2c09832f55cb214b7bf4ba9893919445823455cbee18a
|
4
|
+
data.tar.gz: b77813ff2d18290e714482bfa45350f69b182a638162d083923acb9b21124cfe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58de63dbf9608beb5f313eb57e6e1e4386eefa23fd1b572d17b06319f571ca3c6b1ac172a7886b3dcd42e14feb1f5f60ebd5a532951441193f8400d66e5229ce
|
7
|
+
data.tar.gz: 95765054acd231993f058a9f3a31a596c56ff143af9f1dc576bfb2f201773e611025329069b8c49751f50d3bf14b16a4f83ef863587d2f7815209f0afcdccb4a
|
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,15 @@
|
|
1
|
-
|
1
|
+
# THIS CHANGELOG IS DEPRECATED!!
|
2
|
+
|
3
|
+
Refer https://github.com/exoego/rspec-openapi/releases instead.
|
4
|
+
|
5
|
+
## v0.12.0
|
2
6
|
|
3
|
-
|
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.
|
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
|
@@ -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(
|
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
|
-
|
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
|
data/rspec-openapi.gemspec
CHANGED
@@ -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
|
18
|
-
|
19
|
-
|
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.
|
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-
|
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/
|
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: []
|