rspec-openapi 0.8.1 → 0.10.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/dependabot.yml +8 -0
- data/.github/workflows/codeql-analysis.yml +1 -1
- data/.github/workflows/rubocop.yml +1 -1
- data/.github/workflows/test.yml +14 -1
- data/.rubocop.yml +2 -0
- data/.simplecov_spawn.rb +16 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +2 -0
- data/README.md +25 -3
- data/lib/rspec/openapi/minitest_hooks.rb +12 -8
- data/lib/rspec/openapi/record.rb +2 -0
- data/lib/rspec/openapi/record_builder.rb +26 -18
- data/lib/rspec/openapi/result_recorder.rb +2 -2
- data/lib/rspec/openapi/schema_builder.rb +18 -2
- data/lib/rspec/openapi/schema_cleaner.rb +8 -1
- data/lib/rspec/openapi/schema_merger.rb +22 -10
- data/lib/rspec/openapi/version.rb +1 -1
- data/lib/rspec/openapi.rb +11 -5
- data/scripts/rspec +11 -0
- data/scripts/rspec_with_simplecov +48 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9749f7a6121a78737336bd436f5fda8b20381a82f4cb507f42667bf3d38a2a4b
|
4
|
+
data.tar.gz: ff0bc4a559323f1285d282ab786b5666c950b8ad0b9585fa749cf1f2b0f3613e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e412c58158ffaa1518640e3082ef04c41d50ec43de94ef032c866ebbec2468775d4ea3b2c976294f14ce6b8570b3e86df7248f17877c491efa9fc31e5a8819a
|
7
|
+
data.tar.gz: 65d7308f7d552d9ac299a6d3330933283c237345b477de260a2e2af24210268f2e41350619cec83f145f028a7374b197281cbd9752d1cb2aa3baa696f8240739
|
data/.github/workflows/test.yml
CHANGED
@@ -26,11 +26,24 @@ jobs:
|
|
26
26
|
rails: 6.1.6
|
27
27
|
- ruby: ruby:3.1
|
28
28
|
rails: 7.0.3
|
29
|
+
coverage: coverage
|
29
30
|
env:
|
30
31
|
RAILS_VERSION: ${{ matrix.rails == '' && '6.1.6' || matrix.rails }}
|
32
|
+
COVERAGE: ${{ matrix.coverage || '' }}
|
31
33
|
steps:
|
32
|
-
- uses: actions/checkout@
|
34
|
+
- uses: actions/checkout@v4
|
33
35
|
- name: bundle install
|
34
36
|
run: bundle install -j$(nproc) --retry 3
|
37
|
+
- name: install simplecov-fork only for minitest with coverage
|
38
|
+
run: |
|
39
|
+
gem install specific_install
|
40
|
+
gem specific_install https://github.com/exoego/simplecov.git branch-fix
|
41
|
+
if: matrix.coverage == 'coverage'
|
35
42
|
- run: bundle exec rspec
|
36
43
|
timeout-minutes: 1
|
44
|
+
- name: Upload coverage reports
|
45
|
+
uses: codecov/codecov-action@v3
|
46
|
+
if: matrix.coverage == 'coverage'
|
47
|
+
with:
|
48
|
+
fail_ci_if_error: true
|
49
|
+
files: ./coverage/coverage.xml
|
data/.rubocop.yml
CHANGED
data/.simplecov_spawn.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
unless ENV['COVERAGE'] && ENV['COVERAGE'].empty?
|
4
|
+
require 'simplecov'
|
5
|
+
require 'simplecov-cobertura'
|
6
|
+
|
7
|
+
SimpleCov.at_fork.call(Process.pid)
|
8
|
+
SimpleCov.formatter SimpleCov::Formatter::MultiFormatter.new([
|
9
|
+
SimpleCov::Formatter::CoberturaFormatter,
|
10
|
+
])
|
11
|
+
SimpleCov.start do
|
12
|
+
enable_coverage :branch
|
13
|
+
add_filter '/spec/'
|
14
|
+
add_filter '/scripts/'
|
15
|
+
end
|
16
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
## v0.10.0
|
2
|
+
- bugfix: Merge parameter data to preserve description in manually edited Openapi spec
|
3
|
+
[#149](https://github.com/exoego/rspec-openapi/pull/149)
|
4
|
+
- feat: Add ability to configure which path params to ignore
|
5
|
+
[#150](https://github.com/exoego/rspec-openapi/pull/150)
|
6
|
+
- feat: Add custom title
|
7
|
+
[#147](https://github.com/exoego/rspec-openapi/pull/147)
|
8
|
+
- feat: Add ability to define custom summary and tags builders
|
9
|
+
[#148](https://github.com/exoego/rspec-openapi/pull/148)
|
10
|
+
- enhancement: Sort paths lexicographically so the order of paths is more stable and predictable
|
11
|
+
[#155](https://github.com/exoego/rspec-openapi/pull/155)
|
12
|
+
- enhancement: requestBody should not merge requestBody from error examples
|
13
|
+
[#154](https://github.com/exoego/rspec-openapi/pull/154)
|
14
|
+
|
15
|
+
## v0.9.0
|
16
|
+
- bugfix: Fix engine path resolution
|
17
|
+
[#113](https://github.com/exoego/rspec-openapi/pull/113)
|
18
|
+
- bugfix: fix multiple uploaded files
|
19
|
+
[#117](https://github.com/exoego/rspec-openapi/pull/117), [#126](https://github.com/exoego/rspec-openapi/pull/126)
|
20
|
+
- feat: Add required_request_params to metadata
|
21
|
+
[#114](https://github.com/exoego/rspec-openapi/pull/114)
|
22
|
+
- bugfix(minitest):
|
23
|
+
[#128](https://github.com/exoego/rspec-openapi/pull/128)
|
24
|
+
- doc(minitest): Add instructions for minitest triggered yaml generation
|
25
|
+
[#116](https://github.com/exoego/rspec-openapi/pull/116)
|
26
|
+
- chore: Don't dump records into temporary file
|
27
|
+
[#127](https://github.com/exoego/rspec-openapi/pull/127)
|
28
|
+
|
1
29
|
## v0.8.1
|
2
30
|
- bugfix: Empty `required` array should not be present.
|
3
31
|
[#111](https://github.com/exoego/rspec-openapi/pull/111)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# rspec-openapi ](https://badge.fury.io/rb/rspec-openapi) [](https://github.com/exoego/rspec-openapi/actions/workflows/test.yml) [](https://codecov.io/gh/exoego/rspec-openapi)
|
2
2
|
|
3
3
|
Generate OpenAPI schema from RSpec request specs.
|
4
4
|
|
@@ -122,6 +122,8 @@ RSpec::OpenAPI.path = -> (example) {
|
|
122
122
|
end
|
123
123
|
}
|
124
124
|
|
125
|
+
RSpec::OpenAPI.title = 'OpenAPI Documentation'
|
126
|
+
|
125
127
|
# Disable generating `example`
|
126
128
|
RSpec::OpenAPI.enable_example = false
|
127
129
|
|
@@ -167,8 +169,21 @@ EOS
|
|
167
169
|
# Generate a custom description, given an RSpec example
|
168
170
|
RSpec::OpenAPI.description_builder = -> (example) { example.description }
|
169
171
|
|
172
|
+
# Generate a custom summary, given an RSpec example
|
173
|
+
# This example uses the summary from the example_group.
|
174
|
+
RSpec::OpenAPI.summary_builder = ->(example) { example.metadata.dig(:example_group, :openapi, :summary) }
|
175
|
+
|
176
|
+
# Generate a custom tags, given an RSpec example
|
177
|
+
# This example uses the tags from the parent_example_group
|
178
|
+
RSpec::OpenAPI.tags_builder = -> (example) { example.metadata.dig(:example_group, :parent_example_group, :openapi, :tags) }
|
179
|
+
|
170
180
|
# Change the example type(s) that will generate schema
|
171
181
|
RSpec::OpenAPI.example_types = %i[request]
|
182
|
+
|
183
|
+
# Configure which path params to ignore
|
184
|
+
# :controller and :action always exist. :format is added when routes is configured as such.
|
185
|
+
RSpec::OpenAPI.ignored_path_params = %i[controller action format]
|
186
|
+
|
172
187
|
```
|
173
188
|
|
174
189
|
### Can I use rspec-openapi with `$ref` to minimize duplication of schema?
|
@@ -199,7 +214,7 @@ paths:
|
|
199
214
|
application/json:
|
200
215
|
schema:
|
201
216
|
$ref: "#/components/schemas/User"
|
202
|
-
# Note) #/components/
|
217
|
+
# Note) #/components/schemas is not needed to be defined.
|
203
218
|
```
|
204
219
|
|
205
220
|
3. Then, re-run rspec-openapi. It will generate `#/components/schemas` with the referenced schema (`User` for example) newly-generated or updated.
|
@@ -278,7 +293,7 @@ If you find a room for improvement, open an issue.
|
|
278
293
|
|
279
294
|
### How can I add information which can't be generated from RSpec?
|
280
295
|
|
281
|
-
rspec-openapi tries to
|
296
|
+
rspec-openapi tries to preserve manual modifications as much as possible when generating specs.
|
282
297
|
You can directly edit `doc/openapi.yaml` as you like without spoiling the automatic generation capability.
|
283
298
|
|
284
299
|
### Can I exclude specific specs from OpenAPI generation?
|
@@ -308,6 +323,7 @@ Some examples' attributes can be overwritten via RSpec metadata options. Example
|
|
308
323
|
summary: 'list all posts',
|
309
324
|
description: 'list all posts ordered by pub_date',
|
310
325
|
tags: %w[v1 posts],
|
326
|
+
required_request_params: %w[limit],
|
311
327
|
security: [{"MyToken" => []}],
|
312
328
|
} do
|
313
329
|
# ...
|
@@ -344,6 +360,12 @@ It should work with both classes inheriting from `ActionDispatch::IntegrationTes
|
|
344
360
|
|
345
361
|
Please note that not all features present in the rspec integration work with minitest (yet). For example, custom per test case metadata is not supported. A custom `description_builder` will not work either.
|
346
362
|
|
363
|
+
Run minitest with OPENAPI=1 to generate `doc/openapi.yaml` for your request specs.
|
364
|
+
|
365
|
+
```bash
|
366
|
+
$ OPENAPI=1 bundle exec rails t
|
367
|
+
```
|
368
|
+
|
347
369
|
## Links
|
348
370
|
|
349
371
|
Existing RSpec plugins which have OpenAPI integration:
|
@@ -5,11 +5,7 @@ require 'minitest'
|
|
5
5
|
module RSpec::OpenAPI::Minitest
|
6
6
|
Example = Struct.new(:context, :description, :metadata, :file_path)
|
7
7
|
|
8
|
-
module
|
9
|
-
def self.prepended(base)
|
10
|
-
base.extend(ClassMethods)
|
11
|
-
end
|
12
|
-
|
8
|
+
module RunPatch
|
13
9
|
def run(*args)
|
14
10
|
result = super
|
15
11
|
if ENV['OPENAPI'] && self.class.openapi?
|
@@ -22,6 +18,12 @@ module RSpec::OpenAPI::Minitest
|
|
22
18
|
end
|
23
19
|
result
|
24
20
|
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ActivateOpenApiClassMethods
|
24
|
+
def self.prepended(base)
|
25
|
+
base.extend(ClassMethods)
|
26
|
+
end
|
25
27
|
|
26
28
|
module ClassMethods
|
27
29
|
def openapi?
|
@@ -35,10 +37,12 @@ module RSpec::OpenAPI::Minitest
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
38
|
-
Minitest::Test.prepend RSpec::OpenAPI::Minitest::
|
40
|
+
Minitest::Test.prepend RSpec::OpenAPI::Minitest::ActivateOpenApiClassMethods
|
41
|
+
|
42
|
+
if ENV['OPENAPI']
|
43
|
+
Minitest::Test.prepend RSpec::OpenAPI::Minitest::RunPatch
|
39
44
|
|
40
|
-
Minitest.after_run do
|
41
|
-
if ENV['OPENAPI']
|
45
|
+
Minitest.after_run do
|
42
46
|
result_recorder = RSpec::OpenAPI::ResultRecorder.new(RSpec::OpenAPI.path_records)
|
43
47
|
result_recorder.record_results!
|
44
48
|
puts result_record.error_message if result_recorder.errors?
|
data/lib/rspec/openapi/record.rb
CHANGED
@@ -6,10 +6,12 @@ RSpec::OpenAPI::Record = Struct.new(
|
|
6
6
|
:path_params, # @param [Hash] - {:controller=>"v1/statuses", :action=>"create", :id=>"1"}
|
7
7
|
:query_params, # @param [Hash] - {:query=>"string"}
|
8
8
|
:request_params, # @param [Hash] - {:request=>"body"}
|
9
|
+
:required_request_params, # @param [Array] - ["param1", "param2"]
|
9
10
|
:request_content_type, # @param [String] - "application/json"
|
10
11
|
:request_headers, # @param [Array] - [["header_key1", "header_value1"], ["header_key2", "header_value2"]]
|
11
12
|
:summary, # @param [String] - "v1/statuses #show"
|
12
13
|
:tags, # @param [Array] - ["Status"]
|
14
|
+
:operation_id, # @param [String] - "request-1234"
|
13
15
|
:description, # @param [String] - "returns a status"
|
14
16
|
:security, # @param [Array] - [{securityScheme1: []}]
|
15
17
|
:status, # @param [Integer] - 200
|
@@ -11,7 +11,8 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
11
11
|
request, response = extract_request_response(context)
|
12
12
|
return if request.nil?
|
13
13
|
|
14
|
-
path, summary, tags, raw_path_params, description, security =
|
14
|
+
path, summary, tags, operation_id, required_request_params, raw_path_params, description, security =
|
15
|
+
extract_request_attributes(request, example)
|
15
16
|
|
16
17
|
request_headers, response_headers = extract_headers(request, response)
|
17
18
|
|
@@ -21,10 +22,12 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
21
22
|
path_params: raw_path_params,
|
22
23
|
query_params: request.query_parameters,
|
23
24
|
request_params: raw_request_params(request),
|
25
|
+
required_request_params: required_request_params,
|
24
26
|
request_content_type: request.media_type,
|
25
27
|
request_headers: request_headers,
|
26
28
|
summary: summary,
|
27
29
|
tags: tags,
|
30
|
+
operation_id: operation_id,
|
28
31
|
description: description,
|
29
32
|
security: security,
|
30
33
|
status: response.status,
|
@@ -59,23 +62,31 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
59
62
|
|
60
63
|
def extract_request_attributes(request, example)
|
61
64
|
metadata = example.metadata[:openapi] || {}
|
62
|
-
summary = metadata[:summary]
|
63
|
-
tags = metadata[:tags]
|
65
|
+
summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
|
66
|
+
tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
|
67
|
+
operation_id = metadata[:operation_id]
|
68
|
+
required_request_params = metadata[:required_request_params] || []
|
64
69
|
security = metadata[:security]
|
65
70
|
description = metadata[:description] || RSpec::OpenAPI.description_builder.call(example)
|
66
71
|
raw_path_params = request.path_parameters
|
67
72
|
path = request.path
|
68
73
|
if rails?
|
69
|
-
|
70
|
-
|
74
|
+
# Reverse the destructive modification by Rails https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_dispatch/journey/router.rb#L33-L41
|
75
|
+
fixed_request = request.dup
|
76
|
+
fixed_request.path_info = File.join(request.script_name, request.path_info) if request.script_name.present?
|
77
|
+
|
78
|
+
route, path = find_rails_route(fixed_request)
|
79
|
+
raise "No route matched for #{fixed_request.request_method} #{fixed_request.path_info}" if route.nil?
|
80
|
+
|
81
|
+
path = path.delete_suffix('(.:format)')
|
71
82
|
summary ||= route.requirements[:action]
|
72
83
|
tags ||= [route.requirements[:controller]&.classify].compact
|
73
84
|
# :controller and :action always exist. :format is added when routes is configured as such.
|
74
85
|
# TODO: Use .except(:controller, :action, :format) when we drop support for Ruby 2.x
|
75
|
-
raw_path_params = raw_path_params.slice(*(raw_path_params.keys -
|
86
|
+
raw_path_params = raw_path_params.slice(*(raw_path_params.keys - RSpec::OpenAPI.ignored_path_params))
|
76
87
|
end
|
77
88
|
summary ||= "#{request.method} #{path}"
|
78
|
-
[path, summary, tags, raw_path_params, description, security]
|
89
|
+
[path, summary, tags, operation_id, required_request_params, raw_path_params, description, security]
|
79
90
|
end
|
80
91
|
|
81
92
|
def extract_request_response(context)
|
@@ -99,21 +110,18 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
99
110
|
end
|
100
111
|
|
101
112
|
# @param [ActionDispatch::Request] request
|
102
|
-
def find_rails_route(request, app: Rails.application,
|
103
|
-
# Reverse the destructive modification by Rails https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_dispatch/journey/router.rb#L33-L41
|
104
|
-
if fix_path && !request.script_name.empty?
|
105
|
-
request = request.dup
|
106
|
-
request.path_info = File.join(request.script_name, request.path_info)
|
107
|
-
end
|
108
|
-
|
113
|
+
def find_rails_route(request, app: Rails.application, path_prefix: '')
|
109
114
|
app.routes.router.recognize(request) do |route|
|
115
|
+
path = route.path.spec.to_s
|
110
116
|
if route.app.matches?(request)
|
111
|
-
|
112
|
-
|
113
|
-
|
117
|
+
if route.app.engine?
|
118
|
+
route, path = find_rails_route(request, app: route.app.app, path_prefix: path)
|
119
|
+
next if route.nil?
|
120
|
+
end
|
121
|
+
return [route, path_prefix + path]
|
114
122
|
end
|
115
123
|
end
|
116
|
-
|
124
|
+
nil
|
117
125
|
end
|
118
126
|
|
119
127
|
# workaround to get real request parameters
|
@@ -7,7 +7,7 @@ class RSpec::OpenAPI::ResultRecorder
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def record_results!
|
10
|
-
title =
|
10
|
+
title = RSpec::OpenAPI.title
|
11
11
|
@path_records.each do |path, records|
|
12
12
|
RSpec::OpenAPI::SchemaFile.new(path).edit do |spec|
|
13
13
|
schema = RSpec::OpenAPI::DefaultSchema.build(title)
|
@@ -15,7 +15,6 @@ class RSpec::OpenAPI::ResultRecorder
|
|
15
15
|
RSpec::OpenAPI::SchemaMerger.merge!(spec, schema)
|
16
16
|
new_from_zero = {}
|
17
17
|
records.each do |record|
|
18
|
-
File.open('/tmp/records', 'a') { |f| f.puts record.to_yaml }
|
19
18
|
begin
|
20
19
|
record_schema = RSpec::OpenAPI::SchemaBuilder.build(record)
|
21
20
|
RSpec::OpenAPI::SchemaMerger.merge!(spec, record_schema)
|
@@ -27,6 +26,7 @@ class RSpec::OpenAPI::ResultRecorder
|
|
27
26
|
RSpec::OpenAPI::SchemaCleaner.cleanup!(spec, new_from_zero)
|
28
27
|
RSpec::OpenAPI::ComponentsUpdater.update!(spec, new_from_zero)
|
29
28
|
RSpec::OpenAPI::SchemaCleaner.cleanup_empty_required_array!(spec)
|
29
|
+
RSpec::OpenAPI::SchemaCleaner.sort_paths!(spec)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -27,6 +27,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
27
27
|
record.http_method.downcase => {
|
28
28
|
summary: record.summary,
|
29
29
|
tags: record.tags,
|
30
|
+
operationId: record.operation_id,
|
30
31
|
security: record.security,
|
31
32
|
parameters: build_parameters(record),
|
32
33
|
requestBody: build_request_body(record),
|
@@ -73,6 +74,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
73
74
|
parameters << {
|
74
75
|
name: build_parameter_name(key, value),
|
75
76
|
in: 'query',
|
77
|
+
required: record.required_request_params.include?(key),
|
76
78
|
schema: build_property(try_cast(value)),
|
77
79
|
example: (try_cast(value) if example_enabled?),
|
78
80
|
}.compact
|
@@ -118,6 +120,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
118
120
|
def build_request_body(record)
|
119
121
|
return nil if record.request_content_type.nil?
|
120
122
|
return nil if record.request_params.empty?
|
123
|
+
return nil if record.status >= 400
|
121
124
|
|
122
125
|
{
|
123
126
|
content: {
|
@@ -191,10 +194,23 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
191
194
|
|
192
195
|
def adjust_params(value)
|
193
196
|
value.each do |key, v|
|
194
|
-
|
197
|
+
case v
|
198
|
+
when ActionDispatch::Http::UploadedFile
|
195
199
|
value[key] = v.original_filename
|
196
|
-
|
200
|
+
when Hash
|
197
201
|
adjust_params(v)
|
202
|
+
when Array
|
203
|
+
result = v.map do |item|
|
204
|
+
case item
|
205
|
+
when ActionDispatch::Http::UploadedFile
|
206
|
+
item.original_filename
|
207
|
+
when Hash
|
208
|
+
adjust_params(item)
|
209
|
+
else
|
210
|
+
item
|
211
|
+
end
|
212
|
+
end
|
213
|
+
value[key] = result
|
198
214
|
end
|
199
215
|
end
|
200
216
|
end
|
@@ -47,10 +47,17 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
|
|
47
47
|
paths_to_objects.each do |path|
|
48
48
|
parent = base.dig(*path.take(path.length - 1))
|
49
49
|
# "required" array must not be present if empty
|
50
|
-
parent.delete('required') if parent['required'].empty?
|
50
|
+
parent.delete('required') if parent['required'] && parent['required'].empty?
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
# Sort "paths" lexicographically to make the order more predictable
|
55
|
+
#
|
56
|
+
# @param [Hash] #
|
57
|
+
def sort_paths!(spec)
|
58
|
+
spec['paths'] = spec['paths']&.entries&.sort_by! { |path, _| path }.to_h
|
59
|
+
end
|
60
|
+
|
54
61
|
private
|
55
62
|
|
56
63
|
def cleanup_array!(base, spec, selector, fields_for_identity = [])
|
@@ -43,16 +43,28 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def merge_arrays(base, key, value)
|
46
|
-
case key
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
46
|
+
base[key] = case key
|
47
|
+
when 'parameters'
|
48
|
+
merge_parameters(base, key, value)
|
49
|
+
when 'required'
|
50
|
+
# Preserve properties that appears in all test cases
|
51
|
+
value & base[key]
|
52
|
+
else
|
53
|
+
# last one wins
|
54
|
+
value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def merge_parameters(base, key, value)
|
59
|
+
all_parameters = value | base[key]
|
60
|
+
|
61
|
+
unique_base_parameters = base[key].index_by { |parameter| [parameter['name'], parameter['in']] }
|
62
|
+
all_parameters = all_parameters.map do |parameter|
|
63
|
+
base_parameter = unique_base_parameters[[parameter['name'], parameter['in']]] || {}
|
64
|
+
base_parameter ? base_parameter.merge(parameter) : parameter
|
56
65
|
end
|
66
|
+
|
67
|
+
all_parameters.uniq! { |param| param.slice('name', 'in') }
|
68
|
+
base[key] = all_parameters
|
57
69
|
end
|
58
70
|
end
|
data/lib/rspec/openapi.rb
CHANGED
@@ -10,16 +10,17 @@ require 'rspec/openapi/schema_file'
|
|
10
10
|
require 'rspec/openapi/schema_merger'
|
11
11
|
require 'rspec/openapi/schema_cleaner'
|
12
12
|
|
13
|
-
if
|
14
|
-
|
15
|
-
require 'rspec/openapi/rspec_hooks'
|
16
|
-
end
|
13
|
+
require 'rspec/openapi/minitest_hooks' if Object.const_defined?('Minitest')
|
14
|
+
require 'rspec/openapi/rspec_hooks' if ENV['OPENAPI'] && Object.const_defined?('RSpec')
|
17
15
|
|
18
16
|
module RSpec::OpenAPI
|
19
17
|
@path = 'doc/openapi.yaml'
|
18
|
+
@title = File.basename(Dir.pwd)
|
20
19
|
@comment = nil
|
21
20
|
@enable_example = true
|
22
21
|
@description_builder = ->(example) { example.description }
|
22
|
+
@summary_builder = ->(example) { example.metadata[:summary] }
|
23
|
+
@tags_builder = ->(example) { example.metadata[:tags] }
|
23
24
|
@info = {}
|
24
25
|
@application_version = '1.0.0'
|
25
26
|
@request_headers = []
|
@@ -28,12 +29,16 @@ module RSpec::OpenAPI
|
|
28
29
|
@example_types = %i[request]
|
29
30
|
@response_headers = []
|
30
31
|
@path_records = Hash.new { |h, k| h[k] = [] }
|
32
|
+
@ignored_path_params = %i[controller action format]
|
31
33
|
|
32
34
|
class << self
|
33
35
|
attr_accessor :path,
|
36
|
+
:title,
|
34
37
|
:comment,
|
35
38
|
:enable_example,
|
36
39
|
:description_builder,
|
40
|
+
:summary_builder,
|
41
|
+
:tags_builder,
|
37
42
|
:info,
|
38
43
|
:application_version,
|
39
44
|
:request_headers,
|
@@ -41,6 +46,7 @@ module RSpec::OpenAPI
|
|
41
46
|
:security_schemes,
|
42
47
|
:example_types,
|
43
48
|
:response_headers,
|
44
|
-
:path_records
|
49
|
+
:path_records,
|
50
|
+
:ignored_path_params
|
45
51
|
end
|
46
52
|
end
|
data/scripts/rspec
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# (The MIT License)
|
5
|
+
# Copyright (c) 2012 Chad Humphries, David Chelimsky, Myron Marston
|
6
|
+
# Copyright (c) 2009 Chad Humphries, David Chelimsky
|
7
|
+
# Copyright (c) 2006 David Chelimsky, The RSpec Development Team
|
8
|
+
# Copyright (c) 2005 Steven Baker
|
9
|
+
|
10
|
+
require 'rspec/core'
|
11
|
+
RSpec::Core::Runner.invoke
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# (The MIT License)
|
5
|
+
# Copyright (c) 2012 Chad Humphries, David Chelimsky, Myron Marston
|
6
|
+
# Copyright (c) 2009 Chad Humphries, David Chelimsky
|
7
|
+
# Copyright (c) 2006 David Chelimsky, The RSpec Development Team
|
8
|
+
# Copyright (c) 2005 Steven Baker
|
9
|
+
|
10
|
+
# Turn on verbose to make sure we not generating any ruby warning
|
11
|
+
$VERBOSE = true
|
12
|
+
|
13
|
+
# So our "did they run the rspec command?" detection logic thinks
|
14
|
+
# that we run `rspec`.
|
15
|
+
$0 = 'rspec'
|
16
|
+
|
17
|
+
# This is necessary for when `--standalone` is being used.
|
18
|
+
$LOAD_PATH.unshift File.expand_path '../bundle', __dir__
|
19
|
+
|
20
|
+
# For the travis build we put the bundle directory up a directory
|
21
|
+
# so it can be shared among the repos for faster bundle installs.
|
22
|
+
$LOAD_PATH.unshift File.expand_path '../../bundle', __dir__
|
23
|
+
|
24
|
+
require 'bundler/setup'
|
25
|
+
|
26
|
+
# To use simplecov while running rspec-core's test suite, we must
|
27
|
+
# load simplecov _before_ loading any of rspec-core's files.
|
28
|
+
# So, this executable exists purely as a wrapper script that
|
29
|
+
# first loads simplecov, and then loads rspec.
|
30
|
+
begin
|
31
|
+
# Simplecov emits some ruby warnings when loaded, so silence them.
|
32
|
+
old_verbose = $VERBOSE
|
33
|
+
$VERBOSE = false
|
34
|
+
|
35
|
+
unless (ENV.fetch('COVERAGE', nil) && ENV['COVERAGE'].empty?) || RUBY_VERSION < '1.9.3'
|
36
|
+
require 'simplecov'
|
37
|
+
|
38
|
+
SimpleCov.start do
|
39
|
+
enable_coverage :branch
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
# simplecov is not available
|
44
|
+
ensure
|
45
|
+
$VERBOSE = old_verbose
|
46
|
+
end
|
47
|
+
|
48
|
+
load File.expand_path('rspec', __dir__)
|
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.10.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: 2023-
|
12
|
+
date: 2023-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -47,6 +47,7 @@ executables: []
|
|
47
47
|
extensions: []
|
48
48
|
extra_rdoc_files: []
|
49
49
|
files:
|
50
|
+
- ".github/dependabot.yml"
|
50
51
|
- ".github/workflows/codeql-analysis.yml"
|
51
52
|
- ".github/workflows/rubocop.yml"
|
52
53
|
- ".github/workflows/test.yml"
|
@@ -54,6 +55,7 @@ files:
|
|
54
55
|
- ".rspec"
|
55
56
|
- ".rubocop.yml"
|
56
57
|
- ".rubocop_todo.yml"
|
58
|
+
- ".simplecov_spawn.rb"
|
57
59
|
- CHANGELOG.md
|
58
60
|
- Gemfile
|
59
61
|
- LICENSE.txt
|
@@ -76,6 +78,8 @@ files:
|
|
76
78
|
- lib/rspec/openapi/schema_merger.rb
|
77
79
|
- lib/rspec/openapi/version.rb
|
78
80
|
- rspec-openapi.gemspec
|
81
|
+
- scripts/rspec
|
82
|
+
- scripts/rspec_with_simplecov
|
79
83
|
- test.png
|
80
84
|
homepage: https://github.com/exoego/rspec-openapi
|
81
85
|
licenses:
|