rspec-openapi 0.2.2 → 0.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11f6fd4a506ac6f481fa67c33e2bacecba3ee4e568339338acf4237f4f599bac
4
- data.tar.gz: 45ea7bbf466a413d3eb5d32ae19a38591c542431eee4dcb21905b8070b2280ac
3
+ metadata.gz: 72b0193cdb3e3f8ee7b2cf52f1d2211a0b57a2074424323ab595af78693b5563
4
+ data.tar.gz: 021e6b386ffb4ed889bd08e61b8d26030cae32667fd8225f1e6ad24e1345aaf4
5
5
  SHA512:
6
- metadata.gz: 3b98372303bb006dba766c0b48218c4be8a45a84f470d6cdfc40b3e7f9d780edb484ca58373239a6aeeba83c7f4c8c3daf8c241cdcb903cc1158c80c92269203
7
- data.tar.gz: 8a198194bdc3e4bf0d09f6fe66b1893da1eaa4b0333c1168b9bd309ba26634ab16d53342c140f8d7b4c0f31e02cfc1ba9629b5dc12905dfc5e6eb35990f23c30
6
+ metadata.gz: 65e8998e2bcfd5ad2d34bd42477e86474baa3daadc8de07210ae9601c05cdb571306b9e8768d3d50de803fd078e167592febea3f820df294e3807e09f0c0c2fd
7
+ data.tar.gz: 1b9fee7f0277f71d4364ee7d39679749c941afcf3fd79df0180abf7510379b9d06e7f9ce24f0cb686f258204c205251f0d50b7211706f82e62f27f1ebc2ff0ce
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
+ ## v0.3.4
2
+
3
+ * Generate tags by controller names
4
+ [#10](https://github.com/k0kubun/rspec-openapi/issues/10)
5
+
6
+ ## v0.3.3
7
+
8
+ * Avoid `JSON::ParserError` when a response body is no content
9
+ [#9](https://github.com/k0kubun/rspec-openapi/issues/9)
10
+
11
+ ## v0.3.2
12
+
13
+ * Stop generating format as path parameters in Rails
14
+ [#8](https://github.com/k0kubun/rspec-openapi/issues/8)
15
+
16
+ ## v0.3.1
17
+
18
+ * Add `RSpec::OpenAPI.description_builder` config to dynamically generate a description [experimental]
19
+ [#6](https://github.com/k0kubun/rspec-openapi/issues/6)
20
+
21
+ ## v0.3.0
22
+
23
+ * Initial support of rack-test and non-Rails apps [#5](https://github.com/k0kubun/rspec-openapi/issues/5)
24
+
1
25
  ## v0.2.2
2
26
 
3
27
  * Allow disabling `example` by `RSpec::OpenAPI.enable_example = false`
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
@@ -65,15 +65,19 @@ paths:
65
65
  "/tables":
66
66
  get:
67
67
  summary: 'tables #index'
68
+ tags:
69
+ - tables
68
70
  parameters:
69
71
  - name: page
70
72
  in: query
71
73
  schema:
72
74
  type: integer
75
+ example: 1
73
76
  - name: per
74
77
  in: query
75
78
  schema:
76
79
  type: integer
80
+ example: 10
77
81
  responses:
78
82
  '200':
79
83
  description: returns a list of tables
@@ -93,7 +97,7 @@ paths:
93
97
 
94
98
  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
99
 
96
- ![Redoc example](./spec/railsapp/doc/screenshot.png)
100
+ ![Redoc example](./spec/rails/doc/screenshot.png)
97
101
 
98
102
 
99
103
  ### Configuration
@@ -114,6 +118,9 @@ RSpec::OpenAPI.comment = <<~EOS
114
118
  When you write a spec in spec/requests, running the spec with `OPENAPI=1 rspec` will
115
119
  update this file automatically. You can also manually edit this file.
116
120
  EOS
121
+
122
+ # Generate a custom description, given an RSpec example
123
+ RSpec::OpenAPI.description_builder = -> (example) { example.description }
117
124
  ```
118
125
 
119
126
  ### How can I add information which can't be generated from RSpec?
@@ -145,11 +152,9 @@ Beta
145
152
 
146
153
  Basic features are there, and some people are already using this.
147
154
 
148
- ### Current limitations
155
+ ### Current limitation
149
156
 
150
157
  * Generating a JSON file is not supported yet
151
- * This only works for RSpec request specs
152
- * Only Rails is supported for looking up a request route
153
158
 
154
159
  ### Other missing features with notes
155
160
 
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
@@ -5,8 +5,9 @@ module RSpec::OpenAPI
5
5
  @path = 'doc/openapi.yaml'
6
6
  @comment = nil
7
7
  @enable_example = true
8
+ @description_builder = -> (example) { example.description }
8
9
 
9
10
  class << self
10
- attr_accessor :path, :comment, :enable_example
11
+ attr_accessor :path, :comment, :enable_example, :description_builder
11
12
  end
12
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,8 @@ 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"
9
+ :tags, # @param [Array] - ["Status"]
10
10
  :description, # @param [String] - "returns a status"
11
11
  :status, # @param [Integer] - 200
12
12
  :response_body, # @param [Object] - {"status" => "ok"}
@@ -1,41 +1,82 @@
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
+ tags = [route.requirements[:controller]]
23
+ else
24
+ path = request.path
25
+ summary = "#{request.method} #{request.path}"
26
+ end
27
+
28
+ response_body =
29
+ begin
30
+ response.parsed_body
31
+ rescue JSON::ParserError
32
+ nil
33
+ end
12
34
 
13
35
  RSpec::OpenAPI::Record.new(
14
36
  method: request.request_method,
15
- path: route.path.spec.to_s.delete_suffix('(.:format)'),
16
- path_params: request.path_parameters,
37
+ path: path,
38
+ path_params: raw_path_params(request),
17
39
  query_params: request.query_parameters,
18
40
  request_params: raw_request_params(request),
19
41
  request_content_type: request.content_type,
20
- controller: route.requirements[:controller],
21
- action: route.requirements[:action],
22
- description: example.description,
42
+ summary: summary,
43
+ tags: tags,
44
+ description: RSpec::OpenAPI.description_builder.call(example),
23
45
  status: response.status,
24
- response_body: response.parsed_body,
46
+ response_body: response_body,
25
47
  response_content_type: response.content_type,
26
48
  ).freeze
27
49
  end
28
50
 
29
51
  private
30
52
 
53
+ def rails?
54
+ defined?(Rails) && Rails.application
55
+ end
56
+
57
+ def rack_test?(context)
58
+ defined?(Rack::Test::Methods) && context.class < Rack::Test::Methods
59
+ end
60
+
31
61
  # @param [ActionDispatch::Request] request
32
- def find_route(request)
62
+ def find_rails_route(request)
33
63
  Rails.application.routes.router.recognize(request) do |route|
34
64
  return route
35
65
  end
36
66
  raise "No route matched for #{request.request_method} #{request.path_info}"
37
67
  end
38
68
 
69
+ # :controller and :action always exist. :format is added when routes is configured as such.
70
+ def raw_path_params(request)
71
+ if rails?
72
+ request.path_parameters.reject do |key, _value|
73
+ %i[controller action format].include?(key)
74
+ end
75
+ else
76
+ request.path_parameters
77
+ end
78
+ end
79
+
39
80
  # workaround to get real request parameters
40
81
  # because ActionController::ParamsWrapper overwrites request_parameters
41
82
  def raw_request_params(request)
@@ -2,23 +2,29 @@ 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,
23
+ tags: record.tags,
10
24
  parameters: build_parameters(record),
11
25
  requestBody: build_request_body(record),
12
26
  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 if example_enabled?),
19
- }.compact,
20
- },
21
- },
27
+ record.status.to_s => response,
22
28
  },
23
29
  }.compact,
24
30
  },
@@ -36,7 +42,6 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
36
42
  parameters = []
37
43
 
38
44
  record.path_params.each do |key, value|
39
- next if %i[controller action].include?(key)
40
45
  parameters << {
41
46
  name: key.to_s,
42
47
  in: 'path',
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module OpenAPI
3
- VERSION = '0.2.2'
3
+ VERSION = '0.3.4'
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.2
4
+ version: 0.3.4
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-07-02 00:00:00.000000000 Z
11
+ date: 2020-10-22 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