rspec-openapi 0.1.5 → 0.3.1
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 +27 -0
- data/.rspec +2 -1
- data/CHANGELOG.md +24 -0
- data/Gemfile +5 -1
- data/README.md +14 -10
- data/Rakefile +4 -2
- data/lib/rspec/openapi.rb +4 -1
- data/lib/rspec/openapi/hooks.rb +3 -2
- data/lib/rspec/openapi/record.rb +1 -2
- data/lib/rspec/openapi/record_builder.rb +37 -11
- data/lib/rspec/openapi/schema_builder.rb +13 -5
- data/lib/rspec/openapi/version.rb +1 -1
- data/rspec-openapi.gemspec +4 -3
- metadata +21 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98be98f98f98819f1d1b018ad1a155184ebbe1c226b96414895acc0f60106b18
|
4
|
+
data.tar.gz: 6985138afe53b9223647f67d8802acdab580d737c18caec6b312d625d649d7c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13906efcf4d94054aea6c6132c09990bc99f264fc3fc37842f6d4d98515a5974f91a39cc3c7d749f0b2f24107e7fc9fb6bb94cfa6f3655fa0f13429101d17a20
|
7
|
+
data.tar.gz: 10ca07bb2370cf61dee4bdbc0d19d1194d64daf558813d8fff113868226a5d1ff6036f71071f58106492592e012ab999dc430976e1e5b7a4361dc36ec1bc5fb8
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: test
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- master
|
6
|
+
pull_request:
|
7
|
+
types:
|
8
|
+
- opened
|
9
|
+
- synchronize
|
10
|
+
- reopened
|
11
|
+
jobs:
|
12
|
+
test:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
container: ${{ matrix.ruby }}
|
15
|
+
strategy:
|
16
|
+
fail-fast: false
|
17
|
+
matrix:
|
18
|
+
ruby:
|
19
|
+
- ruby:2.5
|
20
|
+
- ruby:2.6
|
21
|
+
- ruby:2.7
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v2
|
24
|
+
- name: bundle install
|
25
|
+
run: bundle install -j$(nproc) --retry 3
|
26
|
+
- run: bundle exec rspec
|
27
|
+
timeout-minutes: 1
|
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
## Unreleased
|
2
|
+
|
3
|
+
* Add `RSpec::OpenAPI.description_builder` config to dynamically generate a description [experimental]
|
4
|
+
[#6](https://github.com/k0kubun/rspec-openapi/issues/6)
|
5
|
+
|
6
|
+
## v0.3.0
|
7
|
+
|
8
|
+
* Initial support of rack-test and non-Rails apps [#5](https://github.com/k0kubun/rspec-openapi/issues/5)
|
9
|
+
|
10
|
+
## v0.2.2
|
11
|
+
|
12
|
+
* Allow disabling `example` by `RSpec::OpenAPI.enable_example = false`
|
13
|
+
|
14
|
+
## v0.2.1
|
15
|
+
|
16
|
+
* Generate `example` of request body and path / query params
|
17
|
+
[#4](https://github.com/k0kubun/rspec-openapi/issues/4)
|
18
|
+
* Remove a wrapper param created by Rails in request body
|
19
|
+
[#4](https://github.com/k0kubun/rspec-openapi/issues/4)
|
20
|
+
|
21
|
+
## v0.2.0
|
22
|
+
|
23
|
+
* Generate `example` of response body [#3](https://github.com/k0kubun/rspec-openapi/issues/3)
|
24
|
+
|
1
25
|
## v0.1.5
|
2
26
|
|
3
27
|
* Support detecting `float` type [#2](https://github.com/k0kubun/rspec-openapi/issues/2)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# rspec-openapi
|
1
|
+
# rspec-openapi 
|
2
2
|
|
3
|
-
Generate OpenAPI
|
3
|
+
Generate OpenAPI schema from RSpec request specs.
|
4
4
|
|
5
5
|
## What's this?
|
6
6
|
|
@@ -29,7 +29,7 @@ $ OPENAPI=1 rspec
|
|
29
29
|
|
30
30
|
### Example
|
31
31
|
|
32
|
-
Let's say you have [a request spec](./spec/requests/tables_spec.rb) like this:
|
32
|
+
Let's say you have [a request spec](./spec/requests/rails/tables_spec.rb) like this:
|
33
33
|
|
34
34
|
```rb
|
35
35
|
RSpec.describe 'Tables', type: :request do
|
@@ -55,7 +55,7 @@ If you run the spec with `OPENAPI=1`,
|
|
55
55
|
OPENAPI=1 rspec spec/requests/tables_spec.rb
|
56
56
|
```
|
57
57
|
|
58
|
-
It will generate [`doc/openapi.yaml` file](./spec/
|
58
|
+
It will generate [`doc/openapi.yaml` file](./spec/rails/doc/openapi.yaml) like:
|
59
59
|
|
60
60
|
```yml
|
61
61
|
openapi: 3.0.3
|
@@ -93,7 +93,7 @@ paths:
|
|
93
93
|
|
94
94
|
and the schema file can be used as an input of [Swagger UI](https://github.com/swagger-api/swagger-ui) or [Redoc](https://github.com/Redocly/redoc).
|
95
95
|
|
96
|
-

|
97
97
|
|
98
98
|
|
99
99
|
### Configuration
|
@@ -104,6 +104,9 @@ The following configurations are optional.
|
|
104
104
|
# Change the path to generate schema from `doc/openapi.yaml`
|
105
105
|
RSpec::OpenAPI.path = 'doc/schema.yaml'
|
106
106
|
|
107
|
+
# Disable generating `example`
|
108
|
+
RSpec::OpenAPI.enable_example = false
|
109
|
+
|
107
110
|
# Generate a comment on top of a schema file
|
108
111
|
RSpec::OpenAPI.comment = <<~EOS
|
109
112
|
This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi
|
@@ -111,6 +114,9 @@ RSpec::OpenAPI.comment = <<~EOS
|
|
111
114
|
When you write a spec in spec/requests, running the spec with `OPENAPI=1 rspec` will
|
112
115
|
update this file automatically. You can also manually edit this file.
|
113
116
|
EOS
|
117
|
+
|
118
|
+
# Generate a custom description, given a RSpec example
|
119
|
+
RSpec::OpenAPI.description_builder = -> (example) { example.description }
|
114
120
|
```
|
115
121
|
|
116
122
|
### How can I add information which can't be generated from RSpec?
|
@@ -138,15 +144,13 @@ end
|
|
138
144
|
|
139
145
|
## Project status
|
140
146
|
|
141
|
-
|
147
|
+
Beta
|
142
148
|
|
143
|
-
|
149
|
+
Basic features are there, and some people are already using this.
|
144
150
|
|
145
|
-
### Current
|
151
|
+
### Current limitation
|
146
152
|
|
147
153
|
* Generating a JSON file is not supported yet
|
148
|
-
* This only works for RSpec request specs
|
149
|
-
* Only Rails is supported for looking up a request route
|
150
154
|
|
151
155
|
### Other missing features with notes
|
152
156
|
|
data/Rakefile
CHANGED
data/lib/rspec/openapi.rb
CHANGED
@@ -3,8 +3,11 @@ require 'rspec/openapi/hooks' if ENV['OPENAPI']
|
|
3
3
|
|
4
4
|
module RSpec::OpenAPI
|
5
5
|
@path = 'doc/openapi.yaml'
|
6
|
+
@comment = nil
|
7
|
+
@enable_example = true
|
8
|
+
@description_builder = -> (example) { example.description }
|
6
9
|
|
7
10
|
class << self
|
8
|
-
attr_accessor :path, :comment
|
11
|
+
attr_accessor :path, :comment, :enable_example, :description_builder
|
9
12
|
end
|
10
13
|
end
|
data/lib/rspec/openapi/hooks.rb
CHANGED
@@ -8,8 +8,9 @@ require 'rspec/openapi/schema_merger'
|
|
8
8
|
records = []
|
9
9
|
|
10
10
|
RSpec.configuration.after(:each) do |example|
|
11
|
-
if example.metadata[:type] == :request && example.metadata[:openapi] != false
|
12
|
-
|
11
|
+
if example.metadata[:type] == :request && example.metadata[:openapi] != false
|
12
|
+
record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
|
13
|
+
records << record if record
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
data/lib/rspec/openapi/record.rb
CHANGED
@@ -5,8 +5,7 @@ RSpec::OpenAPI::Record = Struct.new(
|
|
5
5
|
:query_params, # @param [Hash] - {:query=>"string"}
|
6
6
|
:request_params, # @param [Hash] - {:request=>"body"}
|
7
7
|
:request_content_type, # @param [String] - "application/json"
|
8
|
-
:
|
9
|
-
:action, # @param [String] - "show"
|
8
|
+
:summary, # @param [String] - "v1/statuses #show"
|
10
9
|
:description, # @param [String] - "returns a status"
|
11
10
|
:status, # @param [Integer] - 200
|
12
11
|
:response_body, # @param [Object] - {"status" => "ok"}
|
@@ -1,25 +1,38 @@
|
|
1
|
+
require 'action_dispatch'
|
1
2
|
require 'rspec/openapi/record'
|
2
3
|
|
3
4
|
class << RSpec::OpenAPI::RecordBuilder = Object.new
|
4
5
|
# @param [RSpec::ExampleGroups::*] context
|
5
6
|
# @param [RSpec::Core::Example] example
|
6
|
-
# @return [RSpec::OpenAPI::Record]
|
7
|
+
# @return [RSpec::OpenAPI::Record,nil]
|
7
8
|
def build(context, example:)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if rack_test?(context)
|
10
|
+
request = ActionDispatch::Request.new(context.last_request.env)
|
11
|
+
response = ActionDispatch::TestResponse.new(*context.last_response.to_a)
|
12
|
+
else
|
13
|
+
request = context.request
|
14
|
+
response = context.response
|
15
|
+
end
|
16
|
+
|
17
|
+
# Generate `path` and `summary` in a framework-friendly manner when possible
|
18
|
+
if defined?(Rails) && Rails.application
|
19
|
+
route = find_rails_route(request)
|
20
|
+
path = route.path.spec.to_s.delete_suffix('(.:format)')
|
21
|
+
summary = "#{route.requirements[:controller]} ##{route.requirements[:action]}"
|
22
|
+
else
|
23
|
+
path = request.path
|
24
|
+
summary = "#{request.method} #{request.path}"
|
25
|
+
end
|
12
26
|
|
13
27
|
RSpec::OpenAPI::Record.new(
|
14
28
|
method: request.request_method,
|
15
|
-
path:
|
29
|
+
path: path,
|
16
30
|
path_params: request.path_parameters,
|
17
31
|
query_params: request.query_parameters,
|
18
|
-
request_params: request
|
32
|
+
request_params: raw_request_params(request),
|
19
33
|
request_content_type: request.content_type,
|
20
|
-
|
21
|
-
|
22
|
-
description: example.description,
|
34
|
+
summary: summary,
|
35
|
+
description: RSpec::OpenAPI.description_builder.call(example),
|
23
36
|
status: response.status,
|
24
37
|
response_body: response.parsed_body,
|
25
38
|
response_content_type: response.content_type,
|
@@ -28,11 +41,24 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
28
41
|
|
29
42
|
private
|
30
43
|
|
44
|
+
def rack_test?(context)
|
45
|
+
defined?(Rack::Test::Methods) && context.class < Rack::Test::Methods
|
46
|
+
end
|
47
|
+
|
31
48
|
# @param [ActionDispatch::Request] request
|
32
|
-
def
|
49
|
+
def find_rails_route(request)
|
33
50
|
Rails.application.routes.router.recognize(request) do |route|
|
34
51
|
return route
|
35
52
|
end
|
36
53
|
raise "No route matched for #{request.request_method} #{request.path_info}"
|
37
54
|
end
|
55
|
+
|
56
|
+
# workaround to get real request parameters
|
57
|
+
# because ActionController::ParamsWrapper overwrites request_parameters
|
58
|
+
def raw_request_params(request)
|
59
|
+
original = request.delete_header('action_dispatch.request.request_parameters')
|
60
|
+
request.request_parameters
|
61
|
+
ensure
|
62
|
+
request.set_header('action_dispatch.request.request_parameters', original)
|
63
|
+
end
|
38
64
|
end
|
@@ -6,7 +6,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
6
6
|
paths: {
|
7
7
|
normalize_path(record.path) => {
|
8
8
|
record.method.downcase => {
|
9
|
-
summary:
|
9
|
+
summary: record.summary,
|
10
10
|
parameters: build_parameters(record),
|
11
11
|
requestBody: build_request_body(record),
|
12
12
|
responses: {
|
@@ -15,7 +15,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
15
15
|
content: {
|
16
16
|
normalize_content_type(record.response_content_type) => {
|
17
17
|
schema: build_property(record.response_body),
|
18
|
-
|
18
|
+
example: (record.response_body if example_enabled?),
|
19
|
+
}.compact,
|
19
20
|
},
|
20
21
|
},
|
21
22
|
},
|
@@ -27,6 +28,10 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
27
28
|
|
28
29
|
private
|
29
30
|
|
31
|
+
def example_enabled?
|
32
|
+
RSpec::OpenAPI.enable_example
|
33
|
+
end
|
34
|
+
|
30
35
|
def build_parameters(record)
|
31
36
|
parameters = []
|
32
37
|
|
@@ -37,7 +42,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
37
42
|
in: 'path',
|
38
43
|
required: true,
|
39
44
|
schema: build_property(try_cast(value)),
|
40
|
-
|
45
|
+
example: (try_cast(value) if example_enabled?),
|
46
|
+
}.compact
|
41
47
|
end
|
42
48
|
|
43
49
|
record.query_params.each do |key, value|
|
@@ -45,7 +51,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
45
51
|
name: key.to_s,
|
46
52
|
in: 'query',
|
47
53
|
schema: build_property(try_cast(value)),
|
48
|
-
|
54
|
+
example: (try_cast(value) if example_enabled?),
|
55
|
+
}.compact
|
49
56
|
end
|
50
57
|
|
51
58
|
return nil if parameters.empty?
|
@@ -60,7 +67,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
60
67
|
content: {
|
61
68
|
normalize_content_type(record.request_content_type) => {
|
62
69
|
schema: build_property(record.request_params),
|
63
|
-
|
70
|
+
example: (record.request_params if example_enabled?),
|
71
|
+
}.compact
|
64
72
|
}
|
65
73
|
}
|
66
74
|
end
|
data/rspec-openapi.gemspec
CHANGED
@@ -6,15 +6,15 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.authors = ['Takashi Kokubun']
|
7
7
|
spec.email = ['takashikkbn@gmail.com']
|
8
8
|
|
9
|
-
spec.summary = %q{Generate OpenAPI
|
10
|
-
spec.description = %q{Generate OpenAPI
|
9
|
+
spec.summary = %q{Generate OpenAPI schema from RSpec request specs}
|
10
|
+
spec.description = %q{Generate OpenAPI from RSpec request specs}
|
11
11
|
spec.homepage = 'https://github.com/k0kubun/rspec-openapi'
|
12
12
|
spec.license = 'MIT'
|
13
13
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
14
14
|
|
15
15
|
spec.metadata['homepage_uri'] = spec.homepage
|
16
16
|
spec.metadata['source_code_uri'] = spec.homepage
|
17
|
-
|
17
|
+
spec.metadata['changelog_uri'] = File.join(spec.homepage, 'blob/master/CHANGELOG.md')
|
18
18
|
|
19
19
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
20
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
@@ -23,5 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
+
spec.add_dependency 'actionpack'
|
26
27
|
spec.add_dependency 'rspec'
|
27
28
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-openapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takashi Kokubun
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,13 +38,14 @@ dependencies:
|
|
24
38
|
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0'
|
27
|
-
description: Generate OpenAPI
|
41
|
+
description: Generate OpenAPI from RSpec request specs
|
28
42
|
email:
|
29
43
|
- takashikkbn@gmail.com
|
30
44
|
executables: []
|
31
45
|
extensions: []
|
32
46
|
extra_rdoc_files: []
|
33
47
|
files:
|
48
|
+
- ".github/workflows/test.yml"
|
34
49
|
- ".gitignore"
|
35
50
|
- ".rspec"
|
36
51
|
- ".travis.yml"
|
@@ -57,6 +72,7 @@ licenses:
|
|
57
72
|
metadata:
|
58
73
|
homepage_uri: https://github.com/k0kubun/rspec-openapi
|
59
74
|
source_code_uri: https://github.com/k0kubun/rspec-openapi
|
75
|
+
changelog_uri: https://github.com/k0kubun/rspec-openapi/blob/master/CHANGELOG.md
|
60
76
|
post_install_message:
|
61
77
|
rdoc_options: []
|
62
78
|
require_paths:
|
@@ -72,8 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
88
|
- !ruby/object:Gem::Version
|
73
89
|
version: '0'
|
74
90
|
requirements: []
|
75
|
-
rubygems_version: 3.
|
91
|
+
rubygems_version: 3.2.0.pre1
|
76
92
|
signing_key:
|
77
93
|
specification_version: 4
|
78
|
-
summary: Generate OpenAPI
|
94
|
+
summary: Generate OpenAPI schema from RSpec request specs
|
79
95
|
test_files: []
|