rspec-openapi 0.2.2 → 0.3.4

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: 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