rspec-openapi 0.8.1 → 0.9.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 +8 -0
- data/.rubocop.yml +2 -0
- data/.simplecov_spawn.rb +15 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +2 -0
- data/README.md +8 -1
- data/lib/rspec/openapi/minitest_hooks.rb +12 -8
- data/lib/rspec/openapi/record.rb +2 -0
- data/lib/rspec/openapi/record_builder.rb +23 -15
- data/lib/rspec/openapi/result_recorder.rb +0 -1
- data/lib/rspec/openapi/schema_builder.rb +17 -2
- data/lib/rspec/openapi/schema_cleaner.rb +1 -1
- data/lib/rspec/openapi/version.rb +1 -1
- data/lib/rspec/openapi.rb +2 -4
- data/scripts/rspec +11 -0
- data/scripts/rspec_with_simplecov +49 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d55c7178770a58a980d06669adfab735ba84b51a283302dd8e14e91af4b7b4e9
|
4
|
+
data.tar.gz: 7b0030924262f8a1cd7027df612315b8d6e3fb89e91f9b4e57c3ec83f7e9bfd6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cf846ca12d2062ae17c0b5d1829a7887c206425330c015f5fdde310c59678cb802b4cadb60f63c1ca6d55063c3d4f1cc2ac7813db10c6d8611a0fa3af975192
|
7
|
+
data.tar.gz: 7501fac5fbfd8b0c4cec6cb196feb9ff09a640c4aff7165a93a6880eebea194d5c30674eefadb53987538ebed913d8d43ab625f1a7c8a3a12ad76ba15b459687
|
data/.github/workflows/test.yml
CHANGED
@@ -26,11 +26,19 @@ 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
34
|
- uses: actions/checkout@v2
|
33
35
|
- name: bundle install
|
34
36
|
run: bundle install -j$(nproc) --retry 3
|
35
37
|
- run: bundle exec rspec
|
36
38
|
timeout-minutes: 1
|
39
|
+
- name: Upload coverage reports
|
40
|
+
uses: codecov/codecov-action@v3
|
41
|
+
if: matrix.coverage == 'coverage'
|
42
|
+
with:
|
43
|
+
fail_ci_if_error: true
|
44
|
+
files: ./coverage/coverage.xml
|
data/.rubocop.yml
CHANGED
data/.simplecov_spawn.rb
ADDED
@@ -0,0 +1,15 @@
|
|
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
|
+
add_filter '/spec/'
|
13
|
+
add_filter '/scripts/'
|
14
|
+
end
|
15
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## v0.9.0
|
2
|
+
- bugfix: Fix engine path resolution
|
3
|
+
[#113](https://github.com/exoego/rspec-openapi/pull/113)
|
4
|
+
- bugfix: fix multiple uploaded files
|
5
|
+
[#117](https://github.com/exoego/rspec-openapi/pull/117), [#126](https://github.com/exoego/rspec-openapi/pull/126)
|
6
|
+
- feat: Add required_request_params to metadata
|
7
|
+
[#114](https://github.com/exoego/rspec-openapi/pull/114)
|
8
|
+
- bugfix(minitest):
|
9
|
+
[#128](https://github.com/exoego/rspec-openapi/pull/128)
|
10
|
+
- doc(minitest): Add instructions for minitest triggered yaml generation
|
11
|
+
[#116](https://github.com/exoego/rspec-openapi/pull/116)
|
12
|
+
- chore: Don't dump records into temporary file
|
13
|
+
[#127](https://github.com/exoego/rspec-openapi/pull/127)
|
14
|
+
|
1
15
|
## v0.8.1
|
2
16
|
- bugfix: Empty `required` array should not be present.
|
3
17
|
[#111](https://github.com/exoego/rspec-openapi/pull/111)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# rspec-openapi  [](https://codecov.io/gh/exoego/rspec-openapi)
|
2
2
|
|
3
3
|
Generate OpenAPI schema from RSpec request specs.
|
4
4
|
|
@@ -308,6 +308,7 @@ Some examples' attributes can be overwritten via RSpec metadata options. Example
|
|
308
308
|
summary: 'list all posts',
|
309
309
|
description: 'list all posts ordered by pub_date',
|
310
310
|
tags: %w[v1 posts],
|
311
|
+
required_request_params: %w[limit],
|
311
312
|
security: [{"MyToken" => []}],
|
312
313
|
} do
|
313
314
|
# ...
|
@@ -344,6 +345,12 @@ It should work with both classes inheriting from `ActionDispatch::IntegrationTes
|
|
344
345
|
|
345
346
|
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
347
|
|
348
|
+
Run minitest with OPENAPI=1 to generate `doc/openapi.yaml` for your request specs.
|
349
|
+
|
350
|
+
```bash
|
351
|
+
$ OPENAPI=1 bundle exec rails t
|
352
|
+
```
|
353
|
+
|
347
354
|
## Links
|
348
355
|
|
349
356
|
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,
|
@@ -61,13 +64,21 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
61
64
|
metadata = example.metadata[:openapi] || {}
|
62
65
|
summary = metadata[:summary]
|
63
66
|
tags = metadata[:tags]
|
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.
|
@@ -75,7 +86,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
75
86
|
raw_path_params = raw_path_params.slice(*(raw_path_params.keys - %i[controller action format]))
|
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
|
@@ -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 +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
|
@@ -191,10 +193,23 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
191
193
|
|
192
194
|
def adjust_params(value)
|
193
195
|
value.each do |key, v|
|
194
|
-
|
196
|
+
case v
|
197
|
+
when ActionDispatch::Http::UploadedFile
|
195
198
|
value[key] = v.original_filename
|
196
|
-
|
199
|
+
when Hash
|
197
200
|
adjust_params(v)
|
201
|
+
when Array
|
202
|
+
result = v.map do |item|
|
203
|
+
case item
|
204
|
+
when ActionDispatch::Http::UploadedFile
|
205
|
+
item.original_filename
|
206
|
+
when Hash
|
207
|
+
adjust_params(item)
|
208
|
+
else
|
209
|
+
item
|
210
|
+
end
|
211
|
+
end
|
212
|
+
value[key] = result
|
198
213
|
end
|
199
214
|
end
|
200
215
|
end
|
@@ -47,7 +47,7 @@ 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
|
|
data/lib/rspec/openapi.rb
CHANGED
@@ -10,10 +10,8 @@ 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'
|
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,49 @@
|
|
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
|
+
# Flaky :(
|
40
|
+
# enable_coverage :branch
|
41
|
+
end
|
42
|
+
end
|
43
|
+
rescue LoadError
|
44
|
+
# simplecov is not available
|
45
|
+
ensure
|
46
|
+
$VERBOSE = old_verbose
|
47
|
+
end
|
48
|
+
|
49
|
+
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.9.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-10-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- ".rspec"
|
55
55
|
- ".rubocop.yml"
|
56
56
|
- ".rubocop_todo.yml"
|
57
|
+
- ".simplecov_spawn.rb"
|
57
58
|
- CHANGELOG.md
|
58
59
|
- Gemfile
|
59
60
|
- LICENSE.txt
|
@@ -76,6 +77,8 @@ files:
|
|
76
77
|
- lib/rspec/openapi/schema_merger.rb
|
77
78
|
- lib/rspec/openapi/version.rb
|
78
79
|
- rspec-openapi.gemspec
|
80
|
+
- scripts/rspec
|
81
|
+
- scripts/rspec_with_simplecov
|
79
82
|
- test.png
|
80
83
|
homepage: https://github.com/exoego/rspec-openapi
|
81
84
|
licenses:
|