rspec-openapi 0.16.1 → 0.18.0

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: c69f9d896be2ad115cb158175dbcf2c1cba9b6d08a7214d23a107a67ec5fc529
4
- data.tar.gz: a5af92611c4b47790f165ce6d861bfdedd1214f5dcd9c5641e618f272fe5a7ca
3
+ metadata.gz: c6e2096ff66c70fb288c3a18a0a9e14c47908ce55963f1cd22a26fa676f86858
4
+ data.tar.gz: 1e2baa5f03c45f80b26647ef2c623bf9af4ae281fd4305c682112786d509ef44
5
5
  SHA512:
6
- metadata.gz: 8e345e850fc539eb4e9fb018cdc48e7acadb47c774b47b438fcd1a95ebc680708999ae6d5440ee45f793bab47d27effd4e054fa3a08e25bf4e6956085effe9ea
7
- data.tar.gz: b1f47ba0a95186141f38465ac4e6a89636185606c48b8a0a67b7f163934c18181ea59e4c7fe94e54291aa821f662ee726437f350f69f872399063b024f7cbfde
6
+ metadata.gz: 13be21458d7e45bb8c2cba02dc89e125efccc1c56a2f2cb44e18bd607e859634bf8174e7ad650c920254ef0c649453f0f6ac38e0ed20e33b689a80e4f457a41e
7
+ data.tar.gz: e77910efe4dfb113fab8f022a7333aa81f1aae36004715ffdeef93706e77921f639ee743f143829943259d6ba562441ebfeb8fa8ade8d653416b2c755c8ee7f8
@@ -0,0 +1,24 @@
1
+ changelog:
2
+ exclude:
3
+ labels:
4
+ - ignore-for-release
5
+ authors:
6
+ - octocat
7
+ categories:
8
+ - title: 🛠 Breaking Changes
9
+ labels:
10
+ - semver-major
11
+ - breaking-change
12
+ - title: 🎉 Exciting New Features
13
+ labels:
14
+ - semver-minor
15
+ - enhancement
16
+ - title: 🐞 Bugfixes
17
+ labels:
18
+ - bug
19
+ - title: 📄 Documentation
20
+ labels:
21
+ - documentation
22
+ - title: 📦 Other Changes
23
+ labels:
24
+ - "*"
@@ -19,13 +19,12 @@ jobs:
19
19
  - ruby: ruby:2.7
20
20
  - ruby: ruby:3.0
21
21
  - ruby: ruby:3.1
22
- rails: 6.0.5
23
22
  - ruby: ruby:3.1
24
- rails: 6.1.6
23
+ rails: 6.1.7
25
24
  - ruby: ruby:3.1
26
- rails: 7.0.3
25
+ rails: 7.0.8
27
26
  - ruby: ruby:3.3
28
- rails: 7.1.2
27
+ rails: 7.1.3.2
29
28
  coverage: coverage
30
29
  env:
31
30
  RAILS_VERSION: ${{ matrix.rails == '' && '6.1.6' || matrix.rails }}
data/.rubocop_todo.yml CHANGED
@@ -1,35 +1,35 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-04-01 14:13:50 UTC using RuboCop version 1.62.1.
3
+ # on 2024-04-12 14:06:44 UTC using RuboCop version 1.62.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 11
9
+ # Offense count: 13
10
10
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
11
11
  Metrics/AbcSize:
12
- Max: 49
12
+ Max: 47
13
13
 
14
- # Offense count: 2
14
+ # Offense count: 1
15
15
  # Configuration parameters: CountComments, CountAsOne.
16
16
  Metrics/ClassLength:
17
17
  Max: 195
18
18
 
19
- # Offense count: 8
19
+ # Offense count: 9
20
20
  # Configuration parameters: AllowedMethods, AllowedPatterns.
21
21
  Metrics/CyclomaticComplexity:
22
- Max: 13
22
+ Max: 12
23
23
 
24
- # Offense count: 19
24
+ # Offense count: 22
25
25
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
26
26
  Metrics/MethodLength:
27
27
  Max: 36
28
28
 
29
- # Offense count: 4
29
+ # Offense count: 5
30
30
  # Configuration parameters: AllowedMethods, AllowedPatterns.
31
31
  Metrics/PerceivedComplexity:
32
- Max: 13
32
+ Max: 12
33
33
 
34
34
  # Offense count: 1
35
35
  # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
@@ -39,7 +39,7 @@ Naming/VariableNumber:
39
39
  Exclude:
40
40
  - 'spec/integration_tests/roda_test.rb'
41
41
 
42
- # Offense count: 6
42
+ # Offense count: 7
43
43
  # Configuration parameters: AllowedConstants.
44
44
  Style/Documentation:
45
45
  Exclude:
@@ -49,3 +49,4 @@ Style/Documentation:
49
49
  - 'lib/rspec/openapi/minitest_hooks.rb'
50
50
  - 'lib/rspec/openapi/result_recorder.rb'
51
51
  - 'lib/rspec/openapi/schema_file.rb'
52
+ - 'lib/rspec/openapi/shared_hooks.rb'
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in rspec-openapi.gemspec
6
6
  gemspec
7
7
 
8
- gem 'rails', ENV['RAILS_VERSION'] || '6.0.3.7'
8
+ gem 'rails', ENV['RAILS_VERSION'] || '6.0.6.1'
9
9
 
10
10
  if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0')
11
11
  gem 'hanami', ENV['HANAMI_VERSION'] || '2.1.0'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # rspec-openapi [![Gem Version](https://badge.fury.io/rb/rspec-openapi.svg)](https://badge.fury.io/rb/rspec-openapi) [![test](https://github.com/exoego/rspec-openapi/actions/workflows/test.yml/badge.svg)](https://github.com/exoego/rspec-openapi/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/exoego/rspec-openapi/branch/master/graph/badge.svg?token=egYm6AlxkD)](https://codecov.io/gh/exoego/rspec-openapi)
1
+ # rspec-openapi [![Gem Version](https://badge.fury.io/rb/rspec-openapi.svg)](https://rubygems.org/gems/rspec-openapi) [![test](https://github.com/exoego/rspec-openapi/actions/workflows/test.yml/badge.svg)](https://github.com/exoego/rspec-openapi/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/exoego/rspec-openapi/branch/master/graph/badge.svg?token=egYm6AlxkD)](https://codecov.io/gh/exoego/rspec-openapi) [![Ruby-toolbox](https://img.shields.io/badge/ruby-toolbox-a61414?cacheSeconds=31536000)](https://www.ruby-toolbox.com/projects/rspec-openapi)
2
2
 
3
3
  Generate OpenAPI schema from RSpec request specs.
4
4
 
@@ -97,7 +97,7 @@ paths:
97
97
 
98
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).
99
99
 
100
- ![Redoc example](./spec/rails/doc/screenshot.png)
100
+ ![Redoc example](./spec/apps/rails/doc/screenshot.png)
101
101
 
102
102
 
103
103
  ### Configuration
@@ -190,6 +190,13 @@ RSpec::OpenAPI.ignored_path_params = %i[controller action format]
190
190
  # In that case, you can specify the paths to ignore.
191
191
  # String or Regexp is acceptable.
192
192
  RSpec::OpenAPI.ignored_paths = ["/admin/full/path/", Regexp.new("^/_internal/")]
193
+
194
+ # Your custom post-processing hook (like unrandomizing IDs)
195
+ RSpec::OpenAPI.post_process_hook = -> (path, records, spec) do
196
+ RSpec::OpenAPI::HashHelper.matched_paths(spec, 'paths.*.*.responses.*.content.*.*.*.id').each do |paths|
197
+ spec.dig(*paths[0..-2]).merge!(id: '123')
198
+ end
199
+ end
193
200
  ```
194
201
 
195
202
  ### Can I use rspec-openapi with `$ref` to minimize duplication of schema?
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/inflector'
4
+ require 'hanami'
5
+
6
+ # https://github.com/hanami/router/blob/97f75b8529574bd4ff23165460e82a6587bc323c/lib/hanami/router/inspector.rb#L13
7
+ class Inspector
8
+ attr_accessor :routes, :inflector
9
+
10
+ def initialize(routes: [])
11
+ @routes = routes
12
+ @inflector = Dry::Inflector.new
13
+ end
14
+
15
+ def add_route(route)
16
+ routes.push(route)
17
+ end
18
+
19
+ def call(verb, path)
20
+ route = routes.find { |r| r.http_method == verb && r.path == path }
21
+
22
+ if route.to.is_a?(Proc)
23
+ {
24
+ tags: [],
25
+ summary: "#{verb} #{path}",
26
+ }
27
+ else
28
+ data = route.to.split('.')
29
+
30
+ {
31
+ tags: [inflector.classify(data[0])],
32
+ summary: data[1],
33
+ }
34
+ end
35
+ end
36
+ end
37
+
38
+ InspectorAnalyzer = Inspector.new
39
+
40
+ # Add default parameter to load inspector before test cases run
41
+ module InspectorAnalyzerPrepender
42
+ def router(inspector: InspectorAnalyzer)
43
+ super
44
+ end
45
+ end
46
+
47
+ Hanami::Slice::ClassMethods.prepend(InspectorAnalyzerPrepender)
48
+
49
+ # Extractor for hanami
50
+ class << RSpec::OpenAPI::Extractors::Hanami = Object.new
51
+ # @param [ActionDispatch::Request] request
52
+ # @param [RSpec::Core::Example] example
53
+ # @return Array
54
+ def request_attributes(request, example)
55
+ route = Hanami.app.router.recognize(request.path, method: request.method)
56
+
57
+ return RSpec::OpenAPI::Extractors::Rack.request_attributes(request, example) unless route.routable?
58
+
59
+ metadata = example.metadata[:openapi] || {}
60
+ summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
61
+ tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
62
+ operation_id = metadata[:operation_id]
63
+ required_request_params = metadata[:required_request_params] || []
64
+ security = metadata[:security]
65
+ description = metadata[:description] || RSpec::OpenAPI.description_builder.call(example)
66
+ deprecated = metadata[:deprecated]
67
+ path = request.path
68
+
69
+ raw_path_params = route.params.filter { |_key, value| number_or_nil(value) }
70
+
71
+ result = InspectorAnalyzer.call(request.method, add_id(path, route))
72
+
73
+ summary ||= result[:summary]
74
+ tags ||= result[:tags]
75
+ path = add_openapi_id(path, route)
76
+
77
+ raw_path_params = raw_path_params.slice(*(raw_path_params.keys - RSpec::OpenAPI.ignored_path_params))
78
+
79
+ [path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated]
80
+ end
81
+
82
+ # @param [RSpec::ExampleGroups::*] context
83
+ def request_response(context)
84
+ request = ActionDispatch::Request.new(context.last_request.env)
85
+ request.body.rewind if request.body.respond_to?(:rewind)
86
+ response = ActionDispatch::TestResponse.new(*context.last_response.to_a)
87
+
88
+ [request, response]
89
+ end
90
+
91
+ def add_id(path, route)
92
+ return path if route.params.empty?
93
+
94
+ route.params.each_pair do |key, value|
95
+ next unless number_or_nil(value)
96
+
97
+ path = path.sub("/#{value}", "/:#{key}")
98
+ end
99
+
100
+ path
101
+ end
102
+
103
+ def add_openapi_id(path, route)
104
+ return path if route.params.empty?
105
+
106
+ route.params.each_pair do |key, value|
107
+ next unless number_or_nil(value)
108
+
109
+ path = path.sub("/#{value}", "/{#{key}}")
110
+ end
111
+
112
+ path
113
+ end
114
+
115
+ def number_or_nil(string)
116
+ Integer(string || '')
117
+ rescue ArgumentError
118
+ nil
119
+ end
120
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extractor for rack
4
+ class << RSpec::OpenAPI::Extractors::Rack = Object.new
5
+ # @param [ActionDispatch::Request] request
6
+ # @param [RSpec::Core::Example] example
7
+ # @return Array
8
+ def request_attributes(request, example)
9
+ metadata = example.metadata[:openapi] || {}
10
+ summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
11
+ tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
12
+ operation_id = metadata[:operation_id]
13
+ required_request_params = metadata[:required_request_params] || []
14
+ security = metadata[:security]
15
+ description = metadata[:description] || RSpec::OpenAPI.description_builder.call(example)
16
+ deprecated = metadata[:deprecated]
17
+ raw_path_params = request.path_parameters
18
+ path = request.path
19
+ summary ||= "#{request.method} #{path}"
20
+ [path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated]
21
+ end
22
+
23
+ # @param [RSpec::ExampleGroups::*] context
24
+ def request_response(context)
25
+ request = ActionDispatch::Request.new(context.last_request.env)
26
+ request.body.rewind if request.body.respond_to?(:rewind)
27
+ response = ActionDispatch::TestResponse.new(*context.last_response.to_a)
28
+
29
+ [request, response]
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extractor for rails
4
+ class << RSpec::OpenAPI::Extractors::Rails = Object.new
5
+ # @param [ActionDispatch::Request] request
6
+ # @param [RSpec::Core::Example] example
7
+ # @return Array
8
+ def request_attributes(request, example)
9
+ # Reverse the destructive modification by Rails https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_dispatch/journey/router.rb#L33-L41
10
+ fixed_request = request.dup
11
+ fixed_request.path_info = File.join(request.script_name, request.path_info) if request.script_name.present?
12
+
13
+ route, path = find_rails_route(fixed_request)
14
+
15
+ raise "No route matched for #{fixed_request.request_method} #{fixed_request.path_info}" if route.nil?
16
+
17
+ return RSpec::OpenAPI::Extractors::Rack.request_attributes(request, example) unless path
18
+
19
+ metadata = example.metadata[:openapi] || {}
20
+ summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
21
+ tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
22
+ operation_id = metadata[:operation_id]
23
+ required_request_params = metadata[:required_request_params] || []
24
+ security = metadata[:security]
25
+ description = metadata[:description] || RSpec::OpenAPI.description_builder.call(example)
26
+ deprecated = metadata[:deprecated]
27
+ raw_path_params = request.path_parameters
28
+
29
+ summary ||= route.requirements[:action]
30
+ tags ||= [route.requirements[:controller]&.classify].compact
31
+ # :controller and :action always exist. :format is added when routes is configured as such.
32
+ # TODO: Use .except(:controller, :action, :format) when we drop support for Ruby 2.x
33
+ raw_path_params = raw_path_params.slice(*(raw_path_params.keys - RSpec::OpenAPI.ignored_path_params))
34
+
35
+ summary ||= "#{request.method} #{path}"
36
+
37
+ [path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated]
38
+ end
39
+
40
+ # @param [RSpec::ExampleGroups::*] context
41
+ def request_response(context)
42
+ [context.request, context.response]
43
+ end
44
+
45
+ # @param [ActionDispatch::Request] request
46
+ def find_rails_route(request, app: Rails.application, path_prefix: '')
47
+ app.routes.router.recognize(request) do |route, parameters|
48
+ path = route.path.spec.to_s.delete_suffix('(.:format)')
49
+
50
+ if route.app.matches?(request)
51
+ if route.app.engine?
52
+ route, path = find_rails_route(request, app: route.app.app, path_prefix: path)
53
+ next if route.nil?
54
+ elsif path_prefix + path == add_id(request.path, parameters)
55
+ return [route, path_prefix + path]
56
+ else
57
+ return [route, nil]
58
+ end
59
+ return [route, path_prefix + path]
60
+ end
61
+ end
62
+
63
+ nil
64
+ end
65
+
66
+ def add_id(path, parameters)
67
+ parameters.each_pair do |key, value|
68
+ next unless number_or_nil(value)
69
+
70
+ path = path.sub("/#{value}", "/:#{key}")
71
+ end
72
+
73
+ path
74
+ end
75
+
76
+ def number_or_nil(string)
77
+ Integer(string || '')
78
+ rescue ArgumentError
79
+ nil
80
+ end
81
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Create namespace
4
+ module RSpec::OpenAPI::Extractors
5
+ end
@@ -13,7 +13,7 @@ module RSpec::OpenAPI::Minitest
13
13
  human_name = name.sub(/^test_/, '').gsub('_', ' ')
14
14
  example = Example.new(self, human_name, {}, file_path)
15
15
  path = RSpec::OpenAPI.path.then { |p| p.is_a?(Proc) ? p.call(example) : p }
16
- record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
16
+ record = RSpec::OpenAPI::RecordBuilder.build(self, example: example, extractor: SharedHooks.find_extractor)
17
17
  RSpec::OpenAPI.path_records[path] << record if record
18
18
  end
19
19
  result
@@ -7,12 +7,12 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
7
7
  # @param [RSpec::ExampleGroups::*] context
8
8
  # @param [RSpec::Core::Example] example
9
9
  # @return [RSpec::OpenAPI::Record,nil]
10
- def build(context, example:)
11
- request, response = extract_request_response(context)
10
+ def build(context, example:, extractor:)
11
+ request, response = extractor.request_response(context)
12
12
  return if request.nil?
13
13
 
14
14
  path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated =
15
- extract_request_attributes(request, example)
15
+ extractor.request_attributes(request, example)
16
16
 
17
17
  return if RSpec::OpenAPI.ignored_paths.any? { |ignored_path| path.match?(ignored_path) }
18
18
 
@@ -69,71 +69,6 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
69
69
  [request_headers, response_headers]
70
70
  end
71
71
 
72
- def extract_request_attributes(request, example)
73
- metadata = example.metadata[:openapi] || {}
74
- summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
75
- tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
76
- operation_id = metadata[:operation_id]
77
- required_request_params = metadata[:required_request_params] || []
78
- security = metadata[:security]
79
- description = metadata[:description] || RSpec::OpenAPI.description_builder.call(example)
80
- deprecated = metadata[:deprecated]
81
- raw_path_params = request.path_parameters
82
- path = request.path
83
- if rails?
84
- # Reverse the destructive modification by Rails https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_dispatch/journey/router.rb#L33-L41
85
- fixed_request = request.dup
86
- fixed_request.path_info = File.join(request.script_name, request.path_info) if request.script_name.present?
87
-
88
- route, path = find_rails_route(fixed_request)
89
- raise "No route matched for #{fixed_request.request_method} #{fixed_request.path_info}" if route.nil?
90
-
91
- path = path.delete_suffix('(.:format)')
92
- summary ||= route.requirements[:action]
93
- tags ||= [route.requirements[:controller]&.classify].compact
94
- # :controller and :action always exist. :format is added when routes is configured as such.
95
- # TODO: Use .except(:controller, :action, :format) when we drop support for Ruby 2.x
96
- raw_path_params = raw_path_params.slice(*(raw_path_params.keys - RSpec::OpenAPI.ignored_path_params))
97
- end
98
- summary ||= "#{request.method} #{path}"
99
- [path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated]
100
- end
101
-
102
- def extract_request_response(context)
103
- if rack_test?(context)
104
- request = ActionDispatch::Request.new(context.last_request.env)
105
- request.body.rewind if request.body.respond_to?(:rewind)
106
- response = ActionDispatch::TestResponse.new(*context.last_response.to_a)
107
- else
108
- request = context.request
109
- response = context.response
110
- end
111
- [request, response]
112
- end
113
-
114
- def rails?
115
- defined?(Rails) && Rails.respond_to?(:application) && Rails.application
116
- end
117
-
118
- def rack_test?(context)
119
- defined?(Rack::Test::Methods) && context.class < Rack::Test::Methods
120
- end
121
-
122
- # @param [ActionDispatch::Request] request
123
- def find_rails_route(request, app: Rails.application, path_prefix: '')
124
- app.routes.router.recognize(request) do |route|
125
- path = route.path.spec.to_s
126
- if route.app.matches?(request)
127
- if route.app.engine?
128
- route, path = find_rails_route(request, app: route.app.app, path_prefix: path)
129
- next if route.nil?
130
- end
131
- return [route, path_prefix + path]
132
- end
133
- end
134
- nil
135
- end
136
-
137
72
  # workaround to get real request parameters
138
73
  # because ActionController::ParamsWrapper overwrites request_parameters
139
74
  def raw_request_params(request)
@@ -29,11 +29,8 @@ class RSpec::OpenAPI::ResultRecorder
29
29
  rescue StandardError, NotImplementedError => e # e.g. SchemaBuilder raises a NotImplementedError
30
30
  @error_records[e] = record # Avoid failing the build
31
31
  end
32
- RSpec::OpenAPI::SchemaCleaner.cleanup_conflicting_security_parameters!(spec)
33
- RSpec::OpenAPI::SchemaCleaner.cleanup!(spec, new_from_zero)
34
- RSpec::OpenAPI::ComponentsUpdater.update!(spec, new_from_zero)
35
- RSpec::OpenAPI::SchemaCleaner.cleanup_empty_required_array!(spec)
36
- RSpec::OpenAPI::SchemaSorter.deep_sort!(spec)
32
+ cleanup_schema!(new_from_zero, spec)
33
+ execute_post_process_hook(path, records, spec)
37
34
  end
38
35
  end
39
36
  end
@@ -49,4 +46,18 @@ class RSpec::OpenAPI::ResultRecorder
49
46
  #{@error_records.map { |e, record| "#{e.inspect}: #{record.inspect}" }.join("\n")}
50
47
  ERR_MSG
51
48
  end
49
+
50
+ private
51
+
52
+ def execute_post_process_hook(path, records, spec)
53
+ RSpec::OpenAPI.post_process_hook.call(path, records, spec) if RSpec::OpenAPI.post_process_hook.is_a?(Proc)
54
+ end
55
+
56
+ def cleanup_schema!(new_from_zero, spec)
57
+ RSpec::OpenAPI::SchemaCleaner.cleanup_conflicting_security_parameters!(spec)
58
+ RSpec::OpenAPI::SchemaCleaner.cleanup!(spec, new_from_zero)
59
+ RSpec::OpenAPI::ComponentsUpdater.update!(spec, new_from_zero)
60
+ RSpec::OpenAPI::SchemaCleaner.cleanup_empty_required_array!(spec)
61
+ RSpec::OpenAPI::SchemaSorter.deep_sort!(spec)
62
+ end
52
63
  end
@@ -5,7 +5,7 @@ require 'rspec/core'
5
5
  RSpec.configuration.after(:each) do |example|
6
6
  if RSpec::OpenAPI.example_types.include?(example.metadata[:type]) && example.metadata[:openapi] != false
7
7
  path = RSpec::OpenAPI.path.then { |p| p.is_a?(Proc) ? p.call(example) : p }
8
- record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
8
+ record = RSpec::OpenAPI::RecordBuilder.build(self, example: example, extractor: SharedHooks.find_extractor)
9
9
  RSpec::OpenAPI.path_records[path] << record if record
10
10
  end
11
11
  end
@@ -64,10 +64,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
64
64
  end
65
65
 
66
66
  def build_parameters(record)
67
- parameters = []
68
-
69
- record.path_params.each do |key, value|
70
- parameters << {
67
+ path_params = record.path_params.map do |key, value|
68
+ {
71
69
  name: build_parameter_name(key, value),
72
70
  in: 'path',
73
71
  required: true,
@@ -76,8 +74,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
76
74
  }.compact
77
75
  end
78
76
 
79
- record.query_params.each do |key, value|
80
- parameters << {
77
+ query_params = record.query_params.map do |key, value|
78
+ {
81
79
  name: build_parameter_name(key, value),
82
80
  in: 'query',
83
81
  required: record.required_request_params.include?(key),
@@ -86,8 +84,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
86
84
  }.compact
87
85
  end
88
86
 
89
- record.request_headers.each do |key, value|
90
- parameters << {
87
+ header_params = record.request_headers.map do |key, value|
88
+ {
91
89
  name: build_parameter_name(key, value),
92
90
  in: 'header',
93
91
  required: true,
@@ -96,6 +94,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
96
94
  }.compact
97
95
  end
98
96
 
97
+ parameters = path_params + query_params + header_params
98
+
99
99
  return nil if parameters.empty?
100
100
 
101
101
  parameters
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharedHooks
4
+ def self.find_extractor
5
+ if defined?(Rails) && Rails.respond_to?(:application) && Rails.application
6
+ RSpec::OpenAPI::Extractors::Rails
7
+ elsif defined?(Hanami) && Hanami.respond_to?(:app) && Hanami.app?
8
+ RSpec::OpenAPI::Extractors::Hanami
9
+ # elsif defined?(Roda)
10
+ # some Roda extractor
11
+ else
12
+ RSpec::OpenAPI::Extractors::Rack
13
+ end
14
+ end
15
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.16.1'
5
+ VERSION = '0.18.0'
6
6
  end
7
7
  end
data/lib/rspec/openapi.rb CHANGED
@@ -11,6 +11,29 @@ require 'rspec/openapi/schema_merger'
11
11
  require 'rspec/openapi/schema_cleaner'
12
12
  require 'rspec/openapi/schema_sorter'
13
13
  require 'rspec/openapi/key_transformer'
14
+ require 'rspec/openapi/shared_hooks'
15
+ require 'rspec/openapi/extractors'
16
+ require 'rspec/openapi/extractors/rack'
17
+
18
+ if ENV['OPENAPI']
19
+ DEBUG_ENABLED = ['', '1', 'true'].include?(ENV['DEBUG']&.downcase)
20
+
21
+ begin
22
+ require 'hanami'
23
+ rescue LoadError
24
+ warn 'Hanami not detected' if DEBUG_ENABLED
25
+ else
26
+ require 'rspec/openapi/extractors/hanami'
27
+ end
28
+
29
+ begin
30
+ require 'rails'
31
+ rescue LoadError
32
+ warn 'Rails not detected' if DEBUG_ENABLED
33
+ else
34
+ require 'rspec/openapi/extractors/rails'
35
+ end
36
+ end
14
37
 
15
38
  require 'rspec/openapi/minitest_hooks' if Object.const_defined?('Minitest')
16
39
  require 'rspec/openapi/rspec_hooks' if ENV['OPENAPI'] && Object.const_defined?('RSpec')
@@ -33,6 +56,7 @@ module RSpec::OpenAPI
33
56
  @path_records = Hash.new { |h, k| h[k] = [] }
34
57
  @ignored_path_params = %i[controller action format]
35
58
  @ignored_paths = []
59
+ @post_process_hook = nil
36
60
 
37
61
  # This is the configuraion override file name we look for within each path.
38
62
  @config_filename = 'rspec_openapi.rb'
@@ -54,7 +78,8 @@ module RSpec::OpenAPI
54
78
  :response_headers,
55
79
  :path_records,
56
80
  :ignored_paths,
57
- :ignored_path_params
81
+ :ignored_path_params,
82
+ :post_process_hook
58
83
 
59
84
  attr_reader :config_filename
60
85
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-openapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-04-01 00:00:00.000000000 Z
12
+ date: 2024-04-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -62,6 +62,7 @@ extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
64
  - ".github/dependabot.yml"
65
+ - ".github/release.yaml"
65
66
  - ".github/workflows/codeql-analysis.yml"
66
67
  - ".github/workflows/rubocop.yml"
67
68
  - ".github/workflows/test.yml"
@@ -80,6 +81,10 @@ files:
80
81
  - lib/rspec/openapi.rb
81
82
  - lib/rspec/openapi/components_updater.rb
82
83
  - lib/rspec/openapi/default_schema.rb
84
+ - lib/rspec/openapi/extractors.rb
85
+ - lib/rspec/openapi/extractors/hanami.rb
86
+ - lib/rspec/openapi/extractors/rack.rb
87
+ - lib/rspec/openapi/extractors/rails.rb
83
88
  - lib/rspec/openapi/hash_helper.rb
84
89
  - lib/rspec/openapi/key_transformer.rb
85
90
  - lib/rspec/openapi/minitest_hooks.rb
@@ -92,6 +97,7 @@ files:
92
97
  - lib/rspec/openapi/schema_file.rb
93
98
  - lib/rspec/openapi/schema_merger.rb
94
99
  - lib/rspec/openapi/schema_sorter.rb
100
+ - lib/rspec/openapi/shared_hooks.rb
95
101
  - lib/rspec/openapi/version.rb
96
102
  - rspec-openapi.gemspec
97
103
  - scripts/rspec
@@ -103,7 +109,7 @@ licenses:
103
109
  metadata:
104
110
  homepage_uri: https://github.com/exoego/rspec-openapi
105
111
  source_code_uri: https://github.com/exoego/rspec-openapi
106
- changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.16.1
112
+ changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.18.0
107
113
  rubygems_mfa_required: 'true'
108
114
  post_install_message:
109
115
  rdoc_options: []