rspec-openapi 0.2.0 → 0.3.2

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: a131e530d729238260cf30416962eccbcbde9e1dc93aa0dc14065fe245621671
4
- data.tar.gz: c94f05ec6f9ffe4b8f72e0bc6bf0f5cc1a7395977c6a2a671b361dcc1ba9f354
3
+ metadata.gz: a6a204535e3afe650434a20936c2d1ec34bc5136071cb76e48f1481b13629de8
4
+ data.tar.gz: d102d224884511c60d2b15030083d382d5c8da0bbbfa12dc3c293248456fd7ed
5
5
  SHA512:
6
- metadata.gz: acd6e8a4757c7610d5d92a5ee167995fe029f435b1c1bacf8f1b8eda8099b5fe3eef5e71eb4db26863e6fde7102abdcf7620896abec4da143ce2bd3d4ee3e9ef
7
- data.tar.gz: 66f7a5be114b32fcda8cd61236acda18e399ecb4a38daa30ee4b050d87f19aa604a3c84a6c82682dda5069a3affa3369d13521600b4f4e06210e3f6b49f96fb0
6
+ metadata.gz: 241b1dd789ae12fa853dc90b12cb497945dfc1bf4da0974a93d097c9f685ce6d585ddf08874a8dccfdec22467a8e47428e3a85a68ce847e8d306821b402b36fa
7
+ data.tar.gz: 60f6569f6d91262742732d45eacf8ef3192e201ddf91254b49fe00bdeada72ef05db02f3fcc61a817ed9ff54dc44a12fc45ffc4a1ee95e229f93c9e14b0e5b08
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,28 @@
1
+ ## v0.3.2
2
+
3
+ * Stop generating format as path parameters in Rails
4
+ [#8](https://github.com/k0kubun/rspec-openapi/issues/8)
5
+
6
+ ## v0.3.1
7
+
8
+ * Add `RSpec::OpenAPI.description_builder` config to dynamically generate a description [experimental]
9
+ [#6](https://github.com/k0kubun/rspec-openapi/issues/6)
10
+
11
+ ## v0.3.0
12
+
13
+ * Initial support of rack-test and non-Rails apps [#5](https://github.com/k0kubun/rspec-openapi/issues/5)
14
+
15
+ ## v0.2.2
16
+
17
+ * Allow disabling `example` by `RSpec::OpenAPI.enable_example = false`
18
+
19
+ ## v0.2.1
20
+
21
+ * Generate `example` of request body and path / query params
22
+ [#4](https://github.com/k0kubun/rspec-openapi/issues/4)
23
+ * Remove a wrapper param created by Rails in request body
24
+ [#4](https://github.com/k0kubun/rspec-openapi/issues/4)
25
+
1
26
  ## v0.2.0
2
27
 
3
28
  * Generate `example` of response body [#3](https://github.com/k0kubun/rspec-openapi/issues/3)
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
@@ -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 an 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 rails?
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)'),
16
- path_params: request.path_parameters,
29
+ path: path,
30
+ path_params: raw_path_params(request),
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,39 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
28
41
 
29
42
  private
30
43
 
44
+ def rails?
45
+ defined?(Rails) && Rails.application
46
+ end
47
+
48
+ def rack_test?(context)
49
+ defined?(Rack::Test::Methods) && context.class < Rack::Test::Methods
50
+ end
51
+
31
52
  # @param [ActionDispatch::Request] request
32
- def find_route(request)
53
+ def find_rails_route(request)
33
54
  Rails.application.routes.router.recognize(request) do |route|
34
55
  return route
35
56
  end
36
57
  raise "No route matched for #{request.request_method} #{request.path_info}"
37
58
  end
59
+
60
+ # :controller and :action always exist. :format is added when routes is configured as such.
61
+ def raw_path_params(request)
62
+ if rails?
63
+ request.path_parameters.reject do |key, _value|
64
+ %i[controller action format].include?(key)
65
+ end
66
+ else
67
+ request.path_parameters
68
+ end
69
+ end
70
+
71
+ # workaround to get real request parameters
72
+ # because ActionController::ParamsWrapper overwrites request_parameters
73
+ def raw_request_params(request)
74
+ original = request.delete_header('action_dispatch.request.request_parameters')
75
+ request.request_parameters
76
+ ensure
77
+ request.set_header('action_dispatch.request.request_parameters', original)
78
+ end
38
79
  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,8 +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
- example: record.response_body,
19
- },
18
+ example: (record.response_body if example_enabled?),
19
+ }.compact,
20
20
  },
21
21
  },
22
22
  },
@@ -28,17 +28,21 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
28
28
 
29
29
  private
30
30
 
31
+ def example_enabled?
32
+ RSpec::OpenAPI.enable_example
33
+ end
34
+
31
35
  def build_parameters(record)
32
36
  parameters = []
33
37
 
34
38
  record.path_params.each do |key, value|
35
- next if %i[controller action].include?(key)
36
39
  parameters << {
37
40
  name: key.to_s,
38
41
  in: 'path',
39
42
  required: true,
40
43
  schema: build_property(try_cast(value)),
41
- }
44
+ example: (try_cast(value) if example_enabled?),
45
+ }.compact
42
46
  end
43
47
 
44
48
  record.query_params.each do |key, value|
@@ -46,7 +50,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
46
50
  name: key.to_s,
47
51
  in: 'query',
48
52
  schema: build_property(try_cast(value)),
49
- }
53
+ example: (try_cast(value) if example_enabled?),
54
+ }.compact
50
55
  end
51
56
 
52
57
  return nil if parameters.empty?
@@ -61,7 +66,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
61
66
  content: {
62
67
  normalize_content_type(record.request_content_type) => {
63
68
  schema: build_property(record.request_params),
64
- }
69
+ example: (record.request_params if example_enabled?),
70
+ }.compact
65
71
  }
66
72
  }
67
73
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module OpenAPI
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.2'
4
4
  end
5
5
  end
@@ -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.2.0
4
+ version: 0.3.2
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-22 00:00:00.000000000 Z
11
+ date: 2020-08-01 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
@@ -74,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
88
  - !ruby/object:Gem::Version
75
89
  version: '0'
76
90
  requirements: []
77
- rubygems_version: 3.1.2
91
+ rubygems_version: 3.2.0.pre1
78
92
  signing_key:
79
93
  specification_version: 4
80
94
  summary: Generate OpenAPI schema from RSpec request specs