rspec-openapi 0.1.5 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: adaf5569dc17aa32bf1f570f77023097aed29ce2b76ee9c9c62ab5b48208dc31
4
- data.tar.gz: ddf2e7c33c5a02a86f610dc68d6e3a46f1be0a44b2df838ab1681c9e09cca5dc
3
+ metadata.gz: 98be98f98f98819f1d1b018ad1a155184ebbe1c226b96414895acc0f60106b18
4
+ data.tar.gz: 6985138afe53b9223647f67d8802acdab580d737c18caec6b312d625d649d7c7
5
5
  SHA512:
6
- metadata.gz: 07011c0a01fce891827ee62898a814ce653f17b68a13dca3dd11c4b166e3ef0c864675a43a86c8f94175ea50936d0cfb3a3a386c2c1eeaf68c13f3c180731be2
7
- data.tar.gz: cb24d803cc198dd7df3578147a54c4160dbd160bb642e5ecfad149c5c119a3d6cd35a2fba02618b7c58a2511b5907129ced6d408fbe2e341cd616d0224f01a4d
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
@@ -1,3 +1,4 @@
1
+ --exclude-pattern spec/requests/**/*_spec.rb
1
2
  --format documentation
3
+ --require rspec/openapi
2
4
  --color
3
- --require spec_helper
@@ -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
@@ -4,5 +4,9 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'rails', '6.0.3.2'
7
+ gem 'roda'
7
8
  gem 'rspec-rails'
8
- gem 'pry'
9
+
10
+ group :development do
11
+ gem 'pry'
12
+ end
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # rspec-openapi
1
+ # rspec-openapi ![test](https://github.com/k0kubun/rspec-openapi/workflows/test/badge.svg)
2
2
 
3
- Generate OpenAPI specs from RSpec request specs.
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/railsapp/doc/openapi.yaml) like:
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
- ![Redoc example](./spec/railsapp/doc/screenshot.png)
96
+ ![Redoc example](./spec/rails/doc/screenshot.png)
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
- PoC / Experimental
147
+ Beta
142
148
 
143
- This worked for some of my Rails apps, but this may raise a basic error for your app.
149
+ Basic features are there, and some people are already using this.
144
150
 
145
- ### Current limitations
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
@@ -1,6 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- RSpec::Core::RakeTask.new(:spec)
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.pattern = 'spec/rspec/openapi/**/*_spec.rb'
6
+ end
5
7
 
6
- task :default => :spec
8
+ task default: :spec
@@ -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
@@ -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 && request && response
12
- records << RSpec::OpenAPI::RecordBuilder.build(self, example: example)
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
 
@@ -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
- :controller, # @param [String] - "v1/statuses"
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
- # TODO: Support Non-Rails frameworks
9
- request = context.request
10
- response = context.response
11
- route = find_route(request)
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: route.path.spec.to_s.delete_suffix('(.:format)'),
29
+ path: path,
16
30
  path_params: request.path_parameters,
17
31
  query_params: request.query_parameters,
18
- request_params: request.request_parameters,
32
+ request_params: raw_request_params(request),
19
33
  request_content_type: request.content_type,
20
- controller: route.requirements[:controller],
21
- action: route.requirements[:action],
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 find_route(request)
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: "#{record.controller} ##{record.action}",
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
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module OpenAPI
3
- VERSION = '0.1.5'
3
+ VERSION = '0.3.1'
4
4
  end
5
5
  end
@@ -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 specs from RSpec request specs}
10
- spec.description = %q{Generate OpenAPI specs from RSpec request specs}
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
- # spec.metadata['changelog_uri'] = 'TODO'
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.5
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-06-20 00:00:00.000000000 Z
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 specs from RSpec request specs
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.1.2
91
+ rubygems_version: 3.2.0.pre1
76
92
  signing_key:
77
93
  specification_version: 4
78
- summary: Generate OpenAPI specs from RSpec request specs
94
+ summary: Generate OpenAPI schema from RSpec request specs
79
95
  test_files: []