rswag-specs 2.2.0 → 2.3.0

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: fbf70bfdf1bb5ead3794e4d64a1afb85e00864701ec0cf42416a063b522fe489
4
- data.tar.gz: ff39363f981f9a868e5afc47a00d87fbc629cda498118b024131318696e5e734
3
+ metadata.gz: d6672807e8e018237741224a8adc8c7103a001f2e03b57663771979864820593
4
+ data.tar.gz: 57b604d7d2211136046d61d93ede936f6bec0f162bc6424f97db81e2e71033cb
5
5
  SHA512:
6
- metadata.gz: e21b1bc504582ad44c5df4f24465a3751136ccd2e9114a95bc1aca95cc69b7a6110017c18faf3f2d854bdd596bf0612a56cb40803543c0a078143bc204083f37
7
- data.tar.gz: bf21e075a70eb3e3a8712b8ee081aad9358cfe6472d258bacca82565b7d32426023e1c779a8b3e00b5c3496629357749d26daa6713bdfdd3a461cb72b601c773
6
+ metadata.gz: 38dc2d2ef165069f34ad96289841a56bafee6b388777036349b36f782c18dad4241d278d1ee8911761a563d3c25ee2c70f434cbd502a7131b3d7e88abb62b786
7
+ data.tar.gz: 7cbb9dbd0073d29176af4a8bccd9534e5468f5c56db7e4ae04e37401bc377135b3f11821ffa5f2f955fe28c73d011460d6fc8adeb497c9618dc660c8a0892e03
data/Rakefile CHANGED
@@ -20,8 +20,4 @@ RDoc::Task.new(:rdoc) do |rdoc|
20
20
  rdoc.rdoc_files.include('lib/**/*.rb')
21
21
  end
22
22
 
23
-
24
-
25
-
26
23
  Bundler::GemHelper.install_tasks
27
-
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rswag/route_parser'
2
4
  require 'rails/generators'
3
5
 
4
6
  module Rspec
5
7
  class SwaggerGenerator < ::Rails::Generators::NamedBase
6
- source_root File.expand_path('../templates', __FILE__)
8
+ source_root File.expand_path('templates', __dir__)
7
9
 
8
10
  def setup
9
11
  @routes = Rswag::RouteParser.new(controller_path).routes
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails/generators'
2
4
 
3
5
  module Rswag
4
6
  module Specs
5
-
6
7
  class InstallGenerator < Rails::Generators::Base
7
- source_root File.expand_path('../templates', __FILE__)
8
+ source_root File.expand_path('templates', __dir__)
8
9
 
9
10
  def add_swagger_helper
10
11
  template('swagger_helper.rb', 'spec/swagger_helper.rb')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails_helper'
2
4
 
3
5
  RSpec.configure do |config|
@@ -19,7 +21,17 @@ RSpec.configure do |config|
19
21
  title: 'API V1',
20
22
  version: 'v1'
21
23
  },
22
- paths: {}
24
+ paths: {},
25
+ servers: [
26
+ {
27
+ url: 'https://{defaultHost}',
28
+ variables: {
29
+ defaultHost: {
30
+ default: 'www.example.com'
31
+ }
32
+ }
33
+ }
34
+ ]
23
35
  }
24
36
  }
25
37
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rswag
2
4
  class RouteParser
3
5
  attr_reader :controller
@@ -9,7 +11,7 @@ module Rswag
9
11
  def routes
10
12
  ::Rails.application.routes.routes.select do |route|
11
13
  route.defaults[:controller] == controller
12
- end.reduce({}) do |tree, route|
14
+ end.each_with_object({}) do |tree, route|
13
15
  path = path_from(route)
14
16
  verb = verb_from(route)
15
17
  tree[path] ||= { params: params_from(route), actions: {} }
@@ -28,7 +30,7 @@ module Rswag
28
30
 
29
31
  def verb_from(route)
30
32
  verb = route.verb
31
- if verb.kind_of? String
33
+ if verb.is_a? String
32
34
  verb.downcase
33
35
  else
34
36
  verb.source.gsub(/[$^]/, '').downcase
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec/core'
2
4
  require 'rswag/specs/example_group_helpers'
3
5
  require 'rswag/specs/example_helpers'
@@ -6,7 +8,6 @@ require 'rswag/specs/railtie' if defined?(Rails::Railtie)
6
8
 
7
9
  module Rswag
8
10
  module Specs
9
-
10
11
  # Extend RSpec with a swagger-based DSL
11
12
  ::RSpec.configure do |c|
12
13
  c.add_setting :swagger_root
@@ -1,8 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rswag
2
4
  module Specs
3
-
4
5
  class Configuration
5
-
6
6
  def initialize(rspec_config)
7
7
  @rspec_config = rspec_config
8
8
  end
@@ -12,6 +12,7 @@ module Rswag
12
12
  if @rspec_config.swagger_root.nil?
13
13
  raise ConfigurationError, 'No swagger_root provided. See swagger_helper.rb'
14
14
  end
15
+
15
16
  @rspec_config.swagger_root
16
17
  end
17
18
  end
@@ -21,6 +22,7 @@ module Rswag
21
22
  if @rspec_config.swagger_docs.nil? || @rspec_config.swagger_docs.empty?
22
23
  raise ConfigurationError, 'No swagger_docs defined. See swagger_helper.rb'
23
24
  end
25
+
24
26
  @rspec_config.swagger_docs
25
27
  end
26
28
  end
@@ -35,6 +37,7 @@ module Rswag
35
37
  @swagger_format ||= begin
36
38
  @rspec_config.swagger_format = :json if @rspec_config.swagger_format.nil? || @rspec_config.swagger_format.empty?
37
39
  raise ConfigurationError, "Unknown swagger_format '#{@rspec_config.swagger_format}'" unless [:json, :yaml].include?(@rspec_config.swagger_format)
40
+
38
41
  @rspec_config.swagger_format
39
42
  end
40
43
  end
@@ -42,8 +45,14 @@ module Rswag
42
45
  def get_swagger_doc(name)
43
46
  return swagger_docs.values.first if name.nil?
44
47
  raise ConfigurationError, "Unknown swagger_doc '#{name}'" unless swagger_docs[name]
48
+
45
49
  swagger_docs[name]
46
50
  end
51
+
52
+ def get_swagger_doc_version(name)
53
+ doc = get_swagger_doc(name)
54
+ doc[:openapi] || doc[:swagger]
55
+ end
47
56
  end
48
57
 
49
58
  class ConfigurationError < StandardError; end
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rswag
2
4
  module Specs
3
5
  module ExampleGroupHelpers
4
-
5
- def path(template, metadata={}, &block)
6
+ def path(template, metadata = {}, &block)
6
7
  metadata[:path_item] = { template: template }
7
8
  describe(template, metadata, &block)
8
9
  end
9
10
 
10
- [ :get, :post, :patch, :put, :delete, :head, :options, :trace ].each do |verb|
11
+ [:get, :post, :patch, :put, :delete, :head, :options, :trace].each do |verb|
11
12
  define_method(verb) do |summary, &block|
12
13
  api_metadata = { operation: { verb: verb, summary: summary } }
13
14
  describe(verb, api_metadata, &block)
14
15
  end
15
16
  end
16
17
 
17
- [ :operationId, :deprecated, :security ].each do |attr_name|
18
+ [:operationId, :deprecated, :security].each do |attr_name|
18
19
  define_method(attr_name) do |value|
19
20
  metadata[:operation][attr_name] = value
20
21
  end
@@ -23,13 +24,14 @@ module Rswag
23
24
  # NOTE: 'description' requires special treatment because ExampleGroup already
24
25
  # defines a method with that name. Provide an override that supports the existing
25
26
  # functionality while also setting the appropriate metadata if applicable
26
- def description(value=nil)
27
+ def description(value = nil)
27
28
  return super() if value.nil?
29
+
28
30
  metadata[:operation][:description] = value
29
31
  end
30
32
 
31
33
  # These are array properties - note the splat operator
32
- [ :tags, :consumes, :produces, :schemes ].each do |attr_name|
34
+ [:tags, :consumes, :produces, :schemes].each do |attr_name|
33
35
  define_method(attr_name) do |*value|
34
36
  metadata[:operation][attr_name] = value
35
37
  end
@@ -40,7 +42,7 @@ module Rswag
40
42
  attributes[:required] = true
41
43
  end
42
44
 
43
- if metadata.has_key?(:operation)
45
+ if metadata.key?(:operation)
44
46
  metadata[:operation][:parameters] ||= []
45
47
  metadata[:operation][:parameters] << attributes
46
48
  else
@@ -49,7 +51,7 @@ module Rswag
49
51
  end
50
52
  end
51
53
 
52
- def response(code, description, metadata={}, &block)
54
+ def response(code, description, metadata = {}, &block)
53
55
  metadata[:response] = { code: code, description: description }
54
56
  context(description, metadata, &block)
55
57
  end
@@ -60,6 +62,7 @@ module Rswag
60
62
 
61
63
  def header(name, attributes)
62
64
  metadata[:response][:headers] ||= {}
65
+
63
66
  metadata[:response][:headers][name] = attributes
64
67
  end
65
68
 
@@ -68,6 +71,7 @@ module Rswag
68
71
  # rspec-core ExampleGroup
69
72
  def examples(example = nil)
70
73
  return super() if example.nil?
74
+
71
75
  metadata[:response][:examples] = example
72
76
  end
73
77
 
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rswag/specs/request_factory'
2
4
  require 'rswag/specs/response_validator'
3
5
 
4
6
  module Rswag
5
7
  module Specs
6
8
  module ExampleHelpers
7
-
8
9
  def submit_request(metadata)
9
10
  request = RequestFactory.new.build_request(metadata, self)
10
11
 
@@ -19,10 +20,8 @@ module Rswag
19
20
  send(
20
21
  request[:verb],
21
22
  request[:path],
22
- {
23
- params: request[:payload],
24
- headers: request[:headers]
25
- }
23
+ params: request[:payload],
24
+ headers: request[:headers]
26
25
  )
27
26
  end
28
27
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json-schema'
2
4
 
3
5
  module Rswag
4
6
  module Specs
5
7
  class ExtendedSchema < JSON::Schema::Draft4
6
-
7
8
  def initialize
8
9
  super
9
10
  @attributes['type'] = ExtendedTypeAttribute
@@ -13,9 +14,9 @@ module Rswag
13
14
  end
14
15
 
15
16
  class ExtendedTypeAttribute < JSON::Schema::TypeV4Attribute
17
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
18
+ return if data.nil? && (current_schema.schema['nullable'] == true || current_schema.schema['x-nullable'] == true)
16
19
 
17
- def self.validate(current_schema, data, fragments, processor, validator, options={})
18
- return if data.nil? && current_schema.schema['x-nullable'] == true
19
20
  super
20
21
  end
21
22
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rswag
2
4
  module Specs
3
5
  class Railtie < ::Rails::Railtie
4
-
5
6
  rake_tasks do
6
- load File.expand_path('../../../tasks/rswag-specs_tasks.rake', __FILE__)
7
+ load File.expand_path('../../tasks/rswag-specs_tasks.rake', __dir__)
7
8
  end
8
9
 
9
10
  generators do
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/hash/slice'
2
4
  require 'active_support/core_ext/hash/conversions'
3
5
  require 'json'
6
+ require 'byebug'
4
7
 
5
8
  module Rswag
6
9
  module Specs
7
10
  class RequestFactory
8
-
9
11
  def initialize(config = ::Rswag::Specs.config)
10
12
  @config = config
11
13
  end
@@ -38,8 +40,8 @@ module Rswag
38
40
 
39
41
  def derive_security_params(metadata, swagger_doc)
40
42
  requirements = metadata[:operation][:security] || swagger_doc[:security] || []
41
- scheme_names = requirements.flat_map { |r| r.keys }
42
- schemes = (swagger_doc[:securityDefinitions] || {}).slice(*scheme_names).values
43
+ scheme_names = requirements.flat_map(&:keys)
44
+ schemes = security_version(scheme_names, swagger_doc)
43
45
 
44
46
  schemes.map do |scheme|
45
47
  param = (scheme[:type] == :apiKey) ? scheme.slice(:name, :in) : { name: 'Authorization', in: :header }
@@ -47,13 +49,55 @@ module Rswag
47
49
  end
48
50
  end
49
51
 
52
+ def security_version(scheme_names, swagger_doc)
53
+ if doc_version(swagger_doc).start_with?('2')
54
+ (swagger_doc[:securityDefinitions] || {}).slice(*scheme_names).values
55
+ else # Openapi3
56
+ if swagger_doc.key?(:securityDefinitions)
57
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: securityDefinitions is replaced in OpenAPI3! Rename to components/securitySchemes (in swagger_helper.rb)')
58
+ swagger_doc[:components] ||= { securitySchemes: swagger_doc[:securityDefinitions] }
59
+ swagger_doc.delete(:securityDefinitions)
60
+ end
61
+ components = swagger_doc[:components] || {}
62
+ (components[:securitySchemes] || {}).slice(*scheme_names).values
63
+ end
64
+ end
65
+
50
66
  def resolve_parameter(ref, swagger_doc)
51
- key = ref.sub('#/parameters/', '').to_sym
52
- definitions = swagger_doc[:parameters]
67
+ key = key_version(ref, swagger_doc)
68
+ definitions = definition_version(swagger_doc)
53
69
  raise "Referenced parameter '#{ref}' must be defined" unless definitions && definitions[key]
70
+
54
71
  definitions[key]
55
72
  end
56
73
 
74
+ def key_version(ref, swagger_doc)
75
+ if doc_version(swagger_doc).start_with?('2')
76
+ ref.sub('#/parameters/', '').to_sym
77
+ else # Openapi3
78
+ if ref.start_with?('#/parameters/')
79
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: #/parameters/ refs are replaced in OpenAPI3! Rename to #/components/parameters/')
80
+ ref.sub('#/parameters/', '').to_sym
81
+ else
82
+ ref.sub('#/components/parameters/', '').to_sym
83
+ end
84
+ end
85
+ end
86
+
87
+ def definition_version(swagger_doc)
88
+ if doc_version(swagger_doc).start_with?('2')
89
+ swagger_doc[:parameters]
90
+ else # Openapi3
91
+ if swagger_doc.key?(:parameters)
92
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: parameters is replaced in OpenAPI3! Rename to components/parameters (in swagger_helper.rb)')
93
+ swagger_doc[:parameters]
94
+ else
95
+ components = swagger_doc[:components] || {}
96
+ components[:parameters]
97
+ end
98
+ end
99
+ end
100
+
57
101
  def add_verb(request, metadata)
58
102
  request[:verb] = metadata[:operation][:verb]
59
103
  end
@@ -61,21 +105,21 @@ module Rswag
61
105
  def add_path(request, metadata, swagger_doc, parameters, example)
62
106
  template = (swagger_doc[:basePath] || '') + metadata[:path_item][:template]
63
107
 
64
- request[:path] = template.tap do |template|
108
+ request[:path] = template.tap do |path_template|
65
109
  parameters.select { |p| p[:in] == :path }.each do |p|
66
- template.gsub!("{#{p[:name]}}", example.send(p[:name]).to_s)
110
+ path_template.gsub!("{#{p[:name]}}", example.send(p[:name]).to_s)
67
111
  end
68
112
 
69
113
  parameters.select { |p| p[:in] == :query }.each_with_index do |p, i|
70
- template.concat(i == 0 ? '?' : '&')
71
- template.concat(build_query_string_part(p, example.send(p[:name])))
114
+ path_template.concat(i.zero? ? '?' : '&')
115
+ path_template.concat(build_query_string_part(p, example.send(p[:name])))
72
116
  end
73
117
  end
74
118
  end
75
119
 
76
120
  def build_query_string_part(param, value)
77
121
  name = param[:name]
78
- return "#{name}=#{value.to_s}" unless param[:type].to_sym == :array
122
+ return "#{name}=#{value}" unless param[:type].to_sym == :array
79
123
 
80
124
  case param[:collectionFormat]
81
125
  when :ssv
@@ -94,43 +138,43 @@ module Rswag
94
138
  def add_headers(request, metadata, swagger_doc, parameters, example)
95
139
  tuples = parameters
96
140
  .select { |p| p[:in] == :header }
97
- .map { |p| [ p[:name], example.send(p[:name]).to_s ] }
141
+ .map { |p| [p[:name], example.send(p[:name]).to_s] }
98
142
 
99
143
  # Accept header
100
144
  produces = metadata[:operation][:produces] || swagger_doc[:produces]
101
145
  if produces
102
- accept = example.respond_to?(:'Accept') ? example.send(:'Accept') : produces.first
103
- tuples << [ 'Accept', accept ]
146
+ accept = example.respond_to?(:Accept) ? example.send(:Accept) : produces.first
147
+ tuples << ['Accept', accept]
104
148
  end
105
149
 
106
150
  # Content-Type header
107
151
  consumes = metadata[:operation][:consumes] || swagger_doc[:consumes]
108
152
  if consumes
109
153
  content_type = example.respond_to?(:'Content-Type') ? example.send(:'Content-Type') : consumes.first
110
- tuples << [ 'Content-Type', content_type ]
154
+ tuples << ['Content-Type', content_type]
111
155
  end
112
156
 
113
157
  # Rails test infrastructure requires rackified headers
114
158
  rackified_tuples = tuples.map do |pair|
115
159
  [
116
160
  case pair[0]
117
- when 'Accept' then 'HTTP_ACCEPT'
118
- when 'Content-Type' then 'CONTENT_TYPE'
119
- when 'Authorization' then 'HTTP_AUTHORIZATION'
120
- else pair[0]
161
+ when 'Accept' then 'HTTP_ACCEPT'
162
+ when 'Content-Type' then 'CONTENT_TYPE'
163
+ when 'Authorization' then 'HTTP_AUTHORIZATION'
164
+ else pair[0]
121
165
  end,
122
166
  pair[1]
123
167
  ]
124
168
  end
125
169
 
126
- request[:headers] = Hash[ rackified_tuples ]
170
+ request[:headers] = Hash[rackified_tuples]
127
171
  end
128
172
 
129
173
  def add_payload(request, parameters, example)
130
174
  content_type = request[:headers]['CONTENT_TYPE']
131
175
  return if content_type.nil?
132
176
 
133
- if [ 'application/x-www-form-urlencoded', 'multipart/form-data' ].include?(content_type)
177
+ if ['application/x-www-form-urlencoded', 'multipart/form-data'].include?(content_type)
134
178
  request[:payload] = build_form_payload(parameters, example)
135
179
  else
136
180
  request[:payload] = build_json_payload(parameters, example)
@@ -144,14 +188,18 @@ module Rswag
144
188
  # PROS: simple to implement, CONS: serialization/deserialization is bypassed in test
145
189
  tuples = parameters
146
190
  .select { |p| p[:in] == :formData }
147
- .map { |p| [ p[:name], example.send(p[:name]) ] }
148
- Hash[ tuples ]
191
+ .map { |p| [p[:name], example.send(p[:name])] }
192
+ Hash[tuples]
149
193
  end
150
194
 
151
195
  def build_json_payload(parameters, example)
152
196
  body_param = parameters.select { |p| p[:in] == :body }.first
153
197
  body_param ? example.send(body_param[:name]).to_json : nil
154
198
  end
199
+
200
+ def doc_version(doc)
201
+ doc[:openapi] || doc[:swagger] || '3'
202
+ end
155
203
  end
156
204
  end
157
205
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/hash/slice'
2
4
  require 'json-schema'
3
5
  require 'json'
@@ -6,7 +8,6 @@ require 'rswag/specs/extended_schema'
6
8
  module Rswag
7
9
  module Specs
8
10
  class ResponseValidator
9
-
10
11
  def initialize(config = ::Rswag::Specs.config)
11
12
  @config = config
12
13
  end
@@ -25,8 +26,8 @@ module Rswag
25
26
  expected = metadata[:response][:code].to_s
26
27
  if response.code != expected
27
28
  raise UnexpectedResponse,
28
- "Expected response code '#{response.code}' to match '#{expected}'\n" \
29
- "Response body: #{response.body}"
29
+ "Expected response code '#{response.code}' to match '#{expected}'\n" \
30
+ "Response body: #{response.body}"
30
31
  end
31
32
  end
32
33
 
@@ -41,12 +42,30 @@ module Rswag
41
42
  response_schema = metadata[:response][:schema]
42
43
  return if response_schema.nil?
43
44
 
45
+ version = @config.get_swagger_doc_version(metadata[:swagger_doc])
46
+ schemas = definitions_or_component_schemas(swagger_doc, version)
47
+
44
48
  validation_schema = response_schema
45
49
  .merge('$schema' => 'http://tempuri.org/rswag/specs/extended_schema')
46
- .merge(swagger_doc.slice(:definitions))
50
+ .merge(schemas)
51
+
47
52
  errors = JSON::Validator.fully_validate(validation_schema, body)
48
53
  raise UnexpectedResponse, "Expected response body to match schema: #{errors[0]}" if errors.any?
49
54
  end
55
+
56
+ def definitions_or_component_schemas(swagger_doc, version)
57
+ if version.start_with?('2')
58
+ swagger_doc.slice(:definitions)
59
+ else # Openapi3
60
+ if swagger_doc.key?(:definitions)
61
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: definitions is replaced in OpenAPI3! Rename to components/schemas (in swagger_helper.rb)')
62
+ swagger_doc.slice(:definitions)
63
+ else
64
+ components = swagger_doc[:components] || {}
65
+ { components: { schemas: components[:schemas] } }
66
+ end
67
+ end
68
+ end
50
69
  end
51
70
 
52
71
  class UnexpectedResponse < StandardError; end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/hash/deep_merge'
4
+ require 'rspec/core/formatters/base_text_formatter'
2
5
  require 'swagger_helper'
3
6
 
4
7
  module Rswag
5
8
  module Specs
6
- class SwaggerFormatter
9
+ class SwaggerFormatter < ::RSpec::Core::Formatters::BaseTextFormatter
7
10
 
8
11
  # NOTE: rspec 2.x support
9
12
  if RSPEC_VERSION > 2
@@ -19,22 +22,58 @@ module Rswag
19
22
 
20
23
  def example_group_finished(notification)
21
24
  # NOTE: rspec 2.x support
22
- if RSPEC_VERSION > 2
23
- metadata = notification.group.metadata
25
+ metadata = if RSPEC_VERSION > 2
26
+ notification.group.metadata
24
27
  else
25
- metadata = notification.metadata
28
+ notification.metadata
26
29
  end
27
30
 
28
- return unless metadata.has_key?(:response)
31
+ # !metadata[:document] won't work, since nil means we should generate
32
+ # docs.
33
+ return if metadata[:document] == false
34
+ return unless metadata.key?(:response)
35
+
29
36
  swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
37
+
38
+ unless doc_version(swagger_doc).start_with?('2')
39
+ # This is called multiple times per file!
40
+ # metadata[:operation] is also re-used between examples within file
41
+ # therefore be careful NOT to modify its content here.
42
+ upgrade_request_type!(metadata)
43
+ upgrade_servers!(swagger_doc)
44
+ upgrade_oauth!(swagger_doc)
45
+ upgrade_response_produces!(swagger_doc, metadata)
46
+ end
47
+
30
48
  swagger_doc.deep_merge!(metadata_to_swagger(metadata))
31
49
  end
32
50
 
33
- def stop(notification=nil)
51
+ def stop(_notification = nil)
34
52
  @config.swagger_docs.each do |url_path, doc|
53
+ unless doc_version(doc).start_with?('2')
54
+ doc[:paths]&.each_pair do |_k, v|
55
+ v.each_pair do |_verb, value|
56
+ is_hash = value.is_a?(Hash)
57
+ if is_hash && value.dig(:parameters)
58
+ schema_param = value.dig(:parameters)&.find { |p| (p[:in] == :body || p[:in] == :formData) && p[:schema] }
59
+ mime_list = value.dig(:consumes)
60
+ if value && schema_param && mime_list
61
+ value[:requestBody] = { content: {} } unless value.dig(:requestBody, :content)
62
+ mime_list.each do |mime|
63
+ value[:requestBody][:content][mime] = { schema: schema_param[:schema] }
64
+ end
65
+ end
66
+
67
+ value[:parameters].reject! { |p| p[:in] == :body || p[:in] == :formData }
68
+ end
69
+ remove_invalid_operation_keys!(value)
70
+ end
71
+ end
72
+ end
73
+
35
74
  file_path = File.join(@config.swagger_root, url_path)
36
75
  dirname = File.dirname(file_path)
37
- FileUtils.mkdir_p dirname unless File.exists?(dirname)
76
+ FileUtils.mkdir_p dirname unless File.exist?(dirname)
38
77
 
39
78
  File.open(file_path, 'w') do |file|
40
79
  file.write(pretty_generate(doc))
@@ -57,25 +96,108 @@ module Rswag
57
96
 
58
97
  def yaml_prepare(doc)
59
98
  json_doc = JSON.pretty_generate(doc)
60
- clean_doc = JSON.parse(json_doc)
99
+ JSON.parse(json_doc)
61
100
  end
62
101
 
63
102
  def metadata_to_swagger(metadata)
64
103
  response_code = metadata[:response][:code]
65
- response = metadata[:response].reject { |k,v| k == :code }
104
+ response = metadata[:response].reject { |k, _v| k == :code }
66
105
 
67
106
  verb = metadata[:operation][:verb]
68
107
  operation = metadata[:operation]
69
- .reject { |k,v| k == :verb }
108
+ .reject { |k, _v| k == :verb }
70
109
  .merge(responses: { response_code => response })
71
110
 
72
111
  path_template = metadata[:path_item][:template]
73
112
  path_item = metadata[:path_item]
74
- .reject { |k,v| k == :template }
113
+ .reject { |k, _v| k == :template }
75
114
  .merge(verb => operation)
76
115
 
77
116
  { paths: { path_template => path_item } }
78
117
  end
118
+
119
+ def doc_version(doc)
120
+ doc[:openapi] || doc[:swagger] || '3'
121
+ end
122
+
123
+ def upgrade_response_produces!(swagger_doc, metadata)
124
+ # Accept header
125
+ mime_list = Array(metadata[:operation][:produces] || swagger_doc[:produces])
126
+ target_node = metadata[:response]
127
+ upgrade_content!(mime_list, target_node)
128
+ metadata[:response].delete(:schema)
129
+ end
130
+
131
+ def upgrade_content!(mime_list, target_node)
132
+ target_node.merge!(content: {})
133
+ schema = target_node[:schema]
134
+ return if mime_list.empty? || schema.nil?
135
+
136
+ mime_list.each do |mime_type|
137
+ # TODO upgrade to have content-type specific schema
138
+ target_node[:content][mime_type] = { schema: schema }
139
+ end
140
+ end
141
+
142
+ def upgrade_request_type!(metadata)
143
+ # No deprecation here as it seems valid to allow type as a shorthand
144
+ operation_nodes = metadata[:operation][:parameters] || []
145
+ path_nodes = metadata[:path_item][:parameters] || []
146
+ header_node = metadata[:response][:headers] || {}
147
+
148
+ (operation_nodes + path_nodes + [header_node]).each do |node|
149
+ if node && node[:type] && node[:schema].nil?
150
+ node[:schema] = { type: node[:type] }
151
+ node.delete(:type)
152
+ end
153
+ end
154
+ end
155
+
156
+ def upgrade_servers!(swagger_doc)
157
+ return unless swagger_doc[:servers].nil? && swagger_doc.key?(:schemes)
158
+
159
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: schemes, host, and basePath are replaced in OpenAPI3! Rename to array of servers[{url}] (in swagger_helper.rb)')
160
+
161
+ swagger_doc[:servers] = { urls: [] }
162
+ swagger_doc[:schemes].each do |scheme|
163
+ swagger_doc[:servers][:urls] << scheme + '://' + swagger_doc[:host] + swagger_doc[:basePath]
164
+ end
165
+
166
+ swagger_doc.delete(:schemes)
167
+ swagger_doc.delete(:host)
168
+ swagger_doc.delete(:basePath)
169
+ end
170
+
171
+ def upgrade_oauth!(swagger_doc)
172
+ # find flow in securitySchemes (securityDefinitions will have been re-written)
173
+ schemes = swagger_doc.dig(:components, :securitySchemes)
174
+ return unless schemes&.any? { |_k, v| v.key?(:flow) }
175
+
176
+ schemes.each do |name, v|
177
+ next unless v.key?(:flow)
178
+
179
+ ActiveSupport::Deprecation.warn("Rswag::Specs: WARNING: securityDefinitions flow is replaced in OpenAPI3! Rename to components/securitySchemes/#{name}/flows[] (in swagger_helper.rb)")
180
+ flow = swagger_doc[:components][:securitySchemes][name].delete(:flow).to_s
181
+ if flow == 'accessCode'
182
+ ActiveSupport::Deprecation.warn("Rswag::Specs: WARNING: securityDefinitions accessCode is replaced in OpenAPI3! Rename to clientCredentials (in swagger_helper.rb)")
183
+ flow = 'authorizationCode'
184
+ end
185
+ if flow == 'application'
186
+ ActiveSupport::Deprecation.warn("Rswag::Specs: WARNING: securityDefinitions application is replaced in OpenAPI3! Rename to authorizationCode (in swagger_helper.rb)")
187
+ flow = 'clientCredentials'
188
+ end
189
+ flow_elements = swagger_doc[:components][:securitySchemes][name].except(:type).each_with_object({}) do |(k, _v), a|
190
+ a[k] = swagger_doc[:components][:securitySchemes][name].delete(k)
191
+ end
192
+ swagger_doc[:components][:securitySchemes][name].merge!(flows: { flow => flow_elements })
193
+ end
194
+ end
195
+
196
+ def remove_invalid_operation_keys!(value)
197
+ is_hash = value.is_a?(Hash)
198
+ value.delete(:consumes) if is_hash && value.dig(:consumes)
199
+ value.delete(:produces) if is_hash && value.dig(:produces)
200
+ end
79
201
  end
80
202
  end
81
203
  end
@@ -1,21 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec/core/rake_task'
2
4
 
3
5
  namespace :rswag do
4
6
  namespace :specs do
5
-
6
7
  desc 'Generate Swagger JSON files from integration specs'
7
8
  RSpec::Core::RakeTask.new('swaggerize') do |t|
8
- t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
9
+ t.pattern = ENV.fetch(
10
+ 'PATTERN',
11
+ 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
12
+ )
9
13
 
10
14
  # NOTE: rspec 2.x support
11
15
  if Rswag::Specs::RSPEC_VERSION > 2 && Rswag::Specs.config.swagger_dry_run
12
- t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--dry-run', '--order defined' ]
16
+ t.rspec_opts = ['--format Rswag::Specs::SwaggerFormatter', '--dry-run', '--order defined']
13
17
  else
14
- t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--order defined' ]
18
+ t.rspec_opts = ['--format Rswag::Specs::SwaggerFormatter', '--order defined']
15
19
  end
16
20
  end
17
21
  end
18
22
  end
19
23
 
20
- task :rswag => ['rswag:specs:swaggerize']
21
-
24
+ task rswag: ['rswag:specs:swaggerize']
metadata CHANGED
@@ -1,14 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rswag-specs
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richie Morris
8
+ - Greg Myers
9
+ - Jay Danielian
8
10
  autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
- date: 2019-11-02 00:00:00.000000000 Z
13
+ date: 2020-04-05 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: activesupport
@@ -19,7 +21,7 @@ dependencies:
19
21
  version: '3.1'
20
22
  - - "<"
21
23
  - !ruby/object:Gem::Version
22
- version: '6.1'
24
+ version: '7.0'
23
25
  type: :runtime
24
26
  prerelease: false
25
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +31,7 @@ dependencies:
29
31
  version: '3.1'
30
32
  - - "<"
31
33
  - !ruby/object:Gem::Version
32
- version: '6.1'
34
+ version: '7.0'
33
35
  - !ruby/object:Gem::Dependency
34
36
  name: railties
35
37
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +41,7 @@ dependencies:
39
41
  version: '3.1'
40
42
  - - "<"
41
43
  - !ruby/object:Gem::Version
42
- version: '6.1'
44
+ version: '7.0'
43
45
  type: :runtime
44
46
  prerelease: false
45
47
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,7 +51,7 @@ dependencies:
49
51
  version: '3.1'
50
52
  - - "<"
51
53
  - !ruby/object:Gem::Version
52
- version: '6.1'
54
+ version: '7.0'
53
55
  - !ruby/object:Gem::Dependency
54
56
  name: json-schema
55
57
  requirement: !ruby/object:Gem::Requirement
@@ -91,7 +93,7 @@ files:
91
93
  - lib/rswag/specs/response_validator.rb
92
94
  - lib/rswag/specs/swagger_formatter.rb
93
95
  - lib/tasks/rswag-specs_tasks.rake
94
- homepage: https://github.com/domaindrivendev/rswag
96
+ homepage: https://github.com/rswag/rswag
95
97
  licenses:
96
98
  - MIT
97
99
  metadata: {}
@@ -110,8 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
112
  - !ruby/object:Gem::Version
111
113
  version: '0'
112
114
  requirements: []
113
- rubyforge_project:
114
- rubygems_version: 2.7.8
115
+ rubygems_version: 3.0.6
115
116
  signing_key:
116
117
  specification_version: 4
117
118
  summary: A Swagger-based DSL for rspec-rails & accompanying rake task for generating