rspec-openapi 0.2.1 → 0.3.3

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: cb2aae5a8366967c1ce6a904bb990372af7f5d3b09d4c81fad3866e590c168c3
4
- data.tar.gz: 0665f9348a14cb0eb77c37368f69d3952d6eb71e919d8864931cdfbbbc3055dc
3
+ metadata.gz: b483a73ef32d771b4da3e66f3c63d1cd0a1cfbb839752546d5465cda43ff8444
4
+ data.tar.gz: cf8ec362c29193e2434db5433170ec655008cd5f636a8f2341f9abf7e7f3d454
5
5
  SHA512:
6
- metadata.gz: 69ec6408d396d9e4c75bc4ba3ef1c71f82ea2a7bbd67def082262b275c20b9769db851fdb2c1bea971a4ede35c10b6f325fbc35a73be8591f12a986e94a01637
7
- data.tar.gz: b71662890f9710012a329db3f380f831e10a8d37671fd9db2a9e3b84d237c699c0c40892020e4f20c781b36ab68563205208bc51607f73f5b2f002c80b5c48de
6
+ metadata.gz: de935612cc1afdd9c8ccca7d69711beca33dea36ec97555293978544c9c1a978c04cdc196c42a39888408d7575e40d0d6dff76adf1d629b27512c569feb9b29a
7
+ data.tar.gz: 423d62a92e8a9b143fae9304af57d893e079dbc9cb956e17320ea45a3bf30d8084f88395fdeaa835cd8689c3b72ecba2b40812bd9c81a0f044ddf91754169f9c
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,26 @@
1
+ ## v0.3.3
2
+
3
+ * Avoid `JSON::ParserError` when a response body is no content
4
+ [#9](https://github.com/k0kubun/rspec-openapi/issues/9)
5
+
6
+ ## v0.3.2
7
+
8
+ * Stop generating format as path parameters in Rails
9
+ [#8](https://github.com/k0kubun/rspec-openapi/issues/8)
10
+
11
+ ## v0.3.1
12
+
13
+ * Add `RSpec::OpenAPI.description_builder` config to dynamically generate a description [experimental]
14
+ [#6](https://github.com/k0kubun/rspec-openapi/issues/6)
15
+
16
+ ## v0.3.0
17
+
18
+ * Initial support of rack-test and non-Rails apps [#5](https://github.com/k0kubun/rspec-openapi/issues/5)
19
+
20
+ ## v0.2.2
21
+
22
+ * Allow disabling `example` by `RSpec::OpenAPI.enable_example = false`
23
+
1
24
  ## v0.2.1
2
25
 
3
26
  * Generate `example` of request body and path / query params
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,41 +1,80 @@
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
26
+
27
+ response_body =
28
+ begin
29
+ response.parsed_body
30
+ rescue JSON::ParserError
31
+ nil
32
+ end
12
33
 
13
34
  RSpec::OpenAPI::Record.new(
14
35
  method: request.request_method,
15
- path: route.path.spec.to_s.delete_suffix('(.:format)'),
16
- path_params: request.path_parameters,
36
+ path: path,
37
+ path_params: raw_path_params(request),
17
38
  query_params: request.query_parameters,
18
39
  request_params: raw_request_params(request),
19
40
  request_content_type: request.content_type,
20
- controller: route.requirements[:controller],
21
- action: route.requirements[:action],
22
- description: example.description,
41
+ summary: summary,
42
+ description: RSpec::OpenAPI.description_builder.call(example),
23
43
  status: response.status,
24
- response_body: response.parsed_body,
44
+ response_body: response_body,
25
45
  response_content_type: response.content_type,
26
46
  ).freeze
27
47
  end
28
48
 
29
49
  private
30
50
 
51
+ def rails?
52
+ defined?(Rails) && Rails.application
53
+ end
54
+
55
+ def rack_test?(context)
56
+ defined?(Rack::Test::Methods) && context.class < Rack::Test::Methods
57
+ end
58
+
31
59
  # @param [ActionDispatch::Request] request
32
- def find_route(request)
60
+ def find_rails_route(request)
33
61
  Rails.application.routes.router.recognize(request) do |route|
34
62
  return route
35
63
  end
36
64
  raise "No route matched for #{request.request_method} #{request.path_info}"
37
65
  end
38
66
 
67
+ # :controller and :action always exist. :format is added when routes is configured as such.
68
+ def raw_path_params(request)
69
+ if rails?
70
+ request.path_parameters.reject do |key, _value|
71
+ %i[controller action format].include?(key)
72
+ end
73
+ else
74
+ request.path_parameters
75
+ end
76
+ end
77
+
39
78
  # workaround to get real request parameters
40
79
  # because ActionController::ParamsWrapper overwrites request_parameters
41
80
  def raw_request_params(request)
@@ -2,23 +2,28 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
2
2
  # @param [RSpec::OpenAPI::Record] record
3
3
  # @return [Hash]
4
4
  def build(record)
5
+ response = {
6
+ description: record.description,
7
+ }
8
+
9
+ if record.response_body
10
+ response[:content] = {
11
+ normalize_content_type(record.response_content_type) => {
12
+ schema: build_property(record.response_body),
13
+ example: (record.response_body if example_enabled?),
14
+ }.compact,
15
+ }
16
+ end
17
+
5
18
  {
6
19
  paths: {
7
20
  normalize_path(record.path) => {
8
21
  record.method.downcase => {
9
- summary: "#{record.controller} ##{record.action}",
22
+ summary: record.summary,
10
23
  parameters: build_parameters(record),
11
24
  requestBody: build_request_body(record),
12
25
  responses: {
13
- record.status.to_s => {
14
- description: record.description,
15
- content: {
16
- normalize_content_type(record.response_content_type) => {
17
- schema: build_property(record.response_body),
18
- example: record.response_body,
19
- },
20
- },
21
- },
26
+ record.status.to_s => response,
22
27
  },
23
28
  }.compact,
24
29
  },
@@ -28,18 +33,21 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
28
33
 
29
34
  private
30
35
 
36
+ def example_enabled?
37
+ RSpec::OpenAPI.enable_example
38
+ end
39
+
31
40
  def build_parameters(record)
32
41
  parameters = []
33
42
 
34
43
  record.path_params.each do |key, value|
35
- next if %i[controller action].include?(key)
36
44
  parameters << {
37
45
  name: key.to_s,
38
46
  in: 'path',
39
47
  required: true,
40
48
  schema: build_property(try_cast(value)),
41
- example: try_cast(value),
42
- }
49
+ example: (try_cast(value) if example_enabled?),
50
+ }.compact
43
51
  end
44
52
 
45
53
  record.query_params.each do |key, value|
@@ -47,8 +55,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
47
55
  name: key.to_s,
48
56
  in: 'query',
49
57
  schema: build_property(try_cast(value)),
50
- example: try_cast(value),
51
- }
58
+ example: (try_cast(value) if example_enabled?),
59
+ }.compact
52
60
  end
53
61
 
54
62
  return nil if parameters.empty?
@@ -63,8 +71,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
63
71
  content: {
64
72
  normalize_content_type(record.request_content_type) => {
65
73
  schema: build_property(record.request_params),
66
- example: record.request_params,
67
- }
74
+ example: (record.request_params if example_enabled?),
75
+ }.compact
68
76
  }
69
77
  }
70
78
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module OpenAPI
3
- VERSION = '0.2.1'
3
+ VERSION = '0.3.3'
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.1
4
+ version: 0.3.3
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-25 00:00:00.000000000 Z
11
+ date: 2020-09-11 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.2.0.pre1
91
+ rubygems_version: 3.1.2
78
92
  signing_key:
79
93
  specification_version: 4
80
94
  summary: Generate OpenAPI schema from RSpec request specs