rswag-specs-2.1 2.9.1.ruby21

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.
@@ -0,0 +1,304 @@
1
+ # frozen_string_literal: true
2
+ require "active_support"
3
+ require 'active_support/core_ext/hash/slice'
4
+ require 'active_support/core_ext/hash/conversions'
5
+ require 'json'
6
+
7
+ module Rswag
8
+ module Specs
9
+ class RequestFactory
10
+ def initialize(config = ::Rswag::Specs.config)
11
+ @config = config
12
+ end
13
+
14
+ def build_request(metadata, example)
15
+ swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
16
+ parameters = expand_parameters(metadata, swagger_doc, example)
17
+
18
+ {}.tap do |request|
19
+ add_verb(request, metadata)
20
+ add_path(request, metadata, swagger_doc, parameters, example)
21
+ add_headers(request, metadata, swagger_doc, parameters, example)
22
+ add_payload(request, parameters, example)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def expand_parameters(metadata, swagger_doc, example)
29
+ operation_params = metadata[:operation][:parameters] || []
30
+ path_item_params = metadata[:path_item][:parameters] || []
31
+ security_params = derive_security_params(metadata, swagger_doc)
32
+
33
+ # NOTE: Use of + instead of concat to avoid mutation of the metadata object
34
+ (operation_params + path_item_params + security_params)
35
+ .map { |p| p['$ref'] ? resolve_parameter(p['$ref'], swagger_doc) : p }
36
+ .uniq { |p| p[:name] }
37
+ .reject { |p| p[:required] == false && !example.respond_to?(extract_getter(p)) }
38
+ end
39
+
40
+ def derive_security_params(metadata, swagger_doc)
41
+ requirements = metadata[:operation][:security] || swagger_doc[:security] || []
42
+ scheme_names = requirements.flat_map(&:keys)
43
+ schemes = security_version(scheme_names, swagger_doc)
44
+
45
+ schemes.map do |scheme|
46
+ param = (scheme[:type] == :apiKey) ? scheme.slice(:name, :in) : { name: 'Authorization', in: :header }
47
+ param.merge(type: :string, required: requirements.one?)
48
+ end
49
+ end
50
+
51
+ def security_version(scheme_names, swagger_doc)
52
+ if doc_version(swagger_doc).start_with?('2')
53
+ (swagger_doc[:securityDefinitions] || {}).slice(*scheme_names).values
54
+ else # Openapi3
55
+ if swagger_doc.key?(:securityDefinitions)
56
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: securityDefinitions is replaced in OpenAPI3! Rename to components/securitySchemes (in swagger_helper.rb)')
57
+ swagger_doc[:components] ||= { securitySchemes: swagger_doc[:securityDefinitions] }
58
+ swagger_doc.delete(:securityDefinitions)
59
+ end
60
+ components = swagger_doc[:components] || {}
61
+ (components[:securitySchemes] || {}).slice(*scheme_names).values
62
+ end
63
+ end
64
+
65
+ def resolve_parameter(ref, swagger_doc)
66
+ key = key_version(ref, swagger_doc)
67
+ definitions = definition_version(swagger_doc)
68
+ raise "Referenced parameter '#{ref}' must be defined" unless definitions && definitions[key]
69
+
70
+ definitions[key]
71
+ end
72
+
73
+ def key_version(ref, swagger_doc)
74
+ if doc_version(swagger_doc).start_with?('2')
75
+ ref.sub('#/parameters/', '').to_sym
76
+ else # Openapi3
77
+ if ref.start_with?('#/parameters/')
78
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: #/parameters/ refs are replaced in OpenAPI3! Rename to #/components/parameters/')
79
+ ref.sub('#/parameters/', '').to_sym
80
+ else
81
+ ref.sub('#/components/parameters/', '').to_sym
82
+ end
83
+ end
84
+ end
85
+
86
+ def definition_version(swagger_doc)
87
+ if doc_version(swagger_doc).start_with?('2')
88
+ swagger_doc[:parameters]
89
+ else # Openapi3
90
+ if swagger_doc.key?(:parameters)
91
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: parameters is replaced in OpenAPI3! Rename to components/parameters (in swagger_helper.rb)')
92
+ swagger_doc[:parameters]
93
+ else
94
+ components = swagger_doc[:components] || {}
95
+ components[:parameters]
96
+ end
97
+ end
98
+ end
99
+
100
+ def add_verb(request, metadata)
101
+ request[:verb] = metadata[:operation][:verb]
102
+ end
103
+
104
+ def base_path_from_servers(swagger_doc, use_server = :default)
105
+ return '' if swagger_doc[:servers].nil? || swagger_doc[:servers].empty?
106
+ server = swagger_doc[:servers].first
107
+ variables = {}
108
+ server.fetch(:variables, {}).each_pair { |k,v| variables[k] = v[use_server] }
109
+ base_path = server[:url].gsub(/\{(.*?)\}/) { variables[$1.to_sym] }
110
+ URI(base_path).path
111
+ end
112
+
113
+ def add_path(request, metadata, swagger_doc, parameters, example)
114
+ open_api_3_doc = doc_version(swagger_doc).start_with?('3')
115
+ uses_base_path = swagger_doc[:basePath].present?
116
+
117
+ if open_api_3_doc && uses_base_path
118
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: basePath is replaced in OpenAPI3! Update your swagger_helper.rb')
119
+ end
120
+
121
+ if uses_base_path
122
+ template = (swagger_doc[:basePath] || '') + metadata[:path_item][:template]
123
+ else # OpenAPI 3
124
+ template = base_path_from_servers(swagger_doc) + metadata[:path_item][:template]
125
+ end
126
+
127
+ request[:path] = template.tap do |path_template|
128
+ parameters.select { |p| p[:in] == :path }.each do |p|
129
+ unless example.respond_to?(extract_getter(p))
130
+ raise ArgumentError.new("`#{p[:name].to_s}` parameter key present, but not defined within example group"\
131
+ "(i. e `it` or `let` block)")
132
+ end
133
+ path_template.gsub!("{#{p[:name]}}", example.send(extract_getter(p)).to_s)
134
+ end
135
+
136
+ parameters.select { |p| p[:in] == :query }.each_with_index do |p, i|
137
+ path_template.concat(i.zero? ? '?' : '&')
138
+ path_template.concat(build_query_string_part(p, example.send(extract_getter(p)), swagger_doc))
139
+ end
140
+ end
141
+ end
142
+
143
+ def build_query_string_part(param, value, swagger_doc)
144
+ name = param[:name]
145
+
146
+ # OAS 3: https://swagger.io/docs/specification/serialization/
147
+ if swagger_doc && doc_version(swagger_doc).start_with?('3') && param[:schema]
148
+ # style = param[:style]&.to_sym || :form
149
+ style = param[:style] ? param[:style].to_sym : :form
150
+ explode = param[:explode].nil? ? true : param[:explode]
151
+
152
+ # case param[:schema][:type]&.to_sym
153
+ case param[:schema][:type] ? param[:schema][:type].to_sym : nil
154
+ when :object
155
+ case style
156
+ when :deepObject
157
+ return { name => value }.to_query
158
+ when :form
159
+ if explode
160
+ return value.to_query
161
+ else
162
+ return "#{CGI.escape(name.to_s)}=" + value.to_a.flatten.map{|v| CGI.escape(v.to_s) }.join(',')
163
+ end
164
+ end
165
+ when :array
166
+ case explode
167
+ when true
168
+ return value.to_a.flatten.map{|v| "#{CGI.escape(name.to_s)}=#{CGI.escape(v.to_s)}"}.join('&')
169
+ else
170
+ separator = case style
171
+ when :form then ','
172
+ when :spaceDelimited then '%20'
173
+ when :pipeDelimited then '|'
174
+ end
175
+ return "#{CGI.escape(name.to_s)}=" + value.to_a.flatten.map{|v| CGI.escape(v.to_s) }.join(separator)
176
+ end
177
+ else
178
+ return "#{name}=#{value}"
179
+ end
180
+ end
181
+
182
+ # type = param[:type] || param.dig(:schema, :type)
183
+ type = param[:type] || (param[:schema] && param[:schema][:type])
184
+ # return "#{name}=#{value}" unless type&.to_sym == :array
185
+ return "#{name}=#{value}" unless type && type.to_sym == :array
186
+
187
+ case param[:collectionFormat]
188
+ when :ssv
189
+ "#{name}=#{value.join(' ')}"
190
+ when :tsv
191
+ "#{name}=#{value.join('\t')}"
192
+ when :pipes
193
+ "#{name}=#{value.join('|')}"
194
+ when :multi
195
+ value.map { |v| "#{name}=#{v}" }.join('&')
196
+ else
197
+ "#{name}=#{value.join(',')}" # csv is default
198
+ end
199
+ end
200
+
201
+ def add_headers(request, metadata, swagger_doc, parameters, example)
202
+ tuples = parameters
203
+ .select { |p| p[:in] == :header }
204
+ .map { |p| [p[:name], example.send(extract_getter(p)).to_s] }
205
+
206
+ # Accept header
207
+ produces = metadata[:operation][:produces] || swagger_doc[:produces]
208
+ if produces
209
+ accept = example.respond_to?(:Accept) ? example.send(:Accept) : produces.first
210
+ tuples << ['Accept', accept]
211
+ end
212
+
213
+ # Content-Type header
214
+ consumes = metadata[:operation][:consumes] || swagger_doc[:consumes]
215
+ if consumes
216
+ content_type = example.respond_to?(:'Content-Type') ? example.send(:'Content-Type') : consumes.first
217
+ tuples << ['Content-Type', content_type]
218
+ end
219
+
220
+ # Host header
221
+ host = metadata[:operation][:host] || swagger_doc[:host]
222
+ if host.present?
223
+ host = example.respond_to?(:'Host') ? example.send(:'Host') : host
224
+ tuples << ['Host', host]
225
+ end
226
+
227
+ # Rails test infrastructure requires rack-formatted headers
228
+ rack_formatted_tuples = tuples.map do |pair|
229
+ [
230
+ case pair[0]
231
+ when 'Accept' then 'HTTP_ACCEPT'
232
+ when 'Content-Type' then 'CONTENT_TYPE'
233
+ when 'Authorization' then 'HTTP_AUTHORIZATION'
234
+ when 'Host' then 'HTTP_HOST'
235
+ else pair[0]
236
+ end,
237
+ pair[1]
238
+ ]
239
+ end
240
+
241
+ request[:headers] = Hash[rack_formatted_tuples]
242
+ end
243
+
244
+ def add_payload(request, parameters, example)
245
+ content_type = request[:headers]['CONTENT_TYPE']
246
+ return if content_type.nil?
247
+
248
+ if ['application/x-www-form-urlencoded', 'multipart/form-data'].include?(content_type)
249
+ request[:payload] = build_form_payload(parameters, example)
250
+ else
251
+ request[:payload] = build_json_payload(parameters, example)
252
+ end
253
+ end
254
+
255
+ def build_form_payload(parameters, example)
256
+ # See http://seejohncode.com/2012/04/29/quick-tip-testing-multipart-uploads-with-rspec/
257
+ # Rather that serializing with the appropriate encoding (e.g. multipart/form-data),
258
+ # Rails test infrastructure allows us to send the values directly as a hash
259
+ # PROS: simple to implement, CONS: serialization/deserialization is bypassed in test
260
+ tuples = parameters
261
+ .select { |p| p[:in] == :formData }
262
+ .map { |p| [p[:name], example.send(extract_getter(p))] }
263
+ Hash[tuples]
264
+ end
265
+
266
+ def build_json_payload(parameters, example)
267
+ body_param = parameters.select { |p| p[:in] == :body }.first
268
+
269
+ return nil unless body_param
270
+
271
+ raise(MissingParameterError, body_param[:name]) unless example.respond_to?(body_param[:name])
272
+
273
+ example.send(body_param[:name]).to_json
274
+ end
275
+
276
+ def doc_version(doc)
277
+ doc[:openapi] || doc[:swagger] || '3'
278
+ end
279
+
280
+ def extract_getter(parameter)
281
+ parameter[:getter] || parameter[:name]
282
+ end
283
+ end
284
+
285
+ class MissingParameterError < StandardError
286
+ attr_reader :body_param
287
+
288
+ def initialize(body_param)
289
+ @body_param = body_param
290
+ end
291
+
292
+ def message
293
+ <<-MSG
294
+ Missing parameter '#{body_param}'
295
+
296
+ Please check your spec. It looks like you defined a body parameter,
297
+ but did not declare usage via let. Try adding:
298
+
299
+ let(:#{body_param}) {}
300
+ MSG
301
+ end
302
+ end
303
+ end
304
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/hash/slice'
4
+ require 'json-schema'
5
+ require 'json'
6
+ require 'rswag/specs/extended_schema'
7
+
8
+ module Rswag
9
+ module Specs
10
+ class ResponseValidator
11
+ def initialize(config = ::Rswag::Specs.config)
12
+ @config = config
13
+ end
14
+
15
+ def validate!(metadata, response)
16
+ swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
17
+
18
+ validate_code!(metadata, response)
19
+ validate_headers!(metadata, response.headers)
20
+ validate_body!(metadata, swagger_doc, response.body)
21
+ end
22
+
23
+ private
24
+
25
+ def validate_code!(metadata, response)
26
+ expected = metadata[:response][:code].to_s
27
+ if response.code != expected
28
+ raise UnexpectedResponse,
29
+ "Expected response code '#{response.code}' to match '#{expected}'\n" \
30
+ "Response body: #{response.body}"
31
+ end
32
+ end
33
+
34
+ def validate_headers!(metadata, headers)
35
+ header_schemas = (metadata[:response][:headers] || {})
36
+ expected = header_schemas.keys
37
+ expected.each do |name|
38
+ # nullable_attribute = header_schemas.dig(name.to_s, :schema, :nullable)
39
+ nullable_attribute = header_schemas[name.to_s] && header_schemas[name.to_s][:schema] && header_schemas[name.to_s][:schema][:nullable]
40
+ # required_attribute = header_schemas.dig(name.to_s, :required)
41
+ required_attribute = header_schemas[name.to_s] && header_schemas[name.to_s][:required]
42
+
43
+ is_nullable = nullable_attribute.nil? ? false : nullable_attribute
44
+ is_required = required_attribute.nil? ? true : required_attribute
45
+
46
+ if headers.exclude?(name.to_s) && is_required
47
+ raise UnexpectedResponse, "Expected response header #{name} to be present"
48
+ end
49
+
50
+ if headers.include?(name.to_s) && headers[name.to_s].nil? && !is_nullable
51
+ raise UnexpectedResponse, "Expected response header #{name} to not be null"
52
+ end
53
+ end
54
+ end
55
+
56
+ def validate_body!(metadata, swagger_doc, body)
57
+ response_schema = metadata[:response][:schema]
58
+ return if response_schema.nil?
59
+
60
+ version = @config.get_swagger_doc_version(metadata[:swagger_doc])
61
+ schemas = definitions_or_component_schemas(swagger_doc, version)
62
+
63
+ validation_schema = response_schema
64
+ .merge('$schema' => 'http://tempuri.org/rswag/specs/extended_schema')
65
+ .merge(schemas)
66
+
67
+ validation_options = validation_options_from(metadata)
68
+
69
+ errors = JSON::Validator.fully_validate(validation_schema, body, validation_options)
70
+ return unless errors.any?
71
+
72
+ raise UnexpectedResponse,
73
+ "Expected response body to match schema: #{errors.join("\n")}\n" \
74
+ "Response body: #{JSON.pretty_generate(JSON.parse(body))}"
75
+ end
76
+
77
+ def validation_options_from(metadata)
78
+ is_strict = !!metadata.fetch(
79
+ :swagger_strict_schema_validation,
80
+ @config.swagger_strict_schema_validation
81
+ )
82
+
83
+ { strict: is_strict }
84
+ end
85
+
86
+ def definitions_or_component_schemas(swagger_doc, version)
87
+ if version.start_with?('2')
88
+ swagger_doc.slice(:definitions)
89
+ else # Openapi3
90
+ if swagger_doc.key?(:definitions)
91
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: definitions is replaced in OpenAPI3! Rename to components/schemas (in swagger_helper.rb)')
92
+ swagger_doc.slice(:definitions)
93
+ else
94
+ components = swagger_doc[:components] || {}
95
+ { components: components }
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ class UnexpectedResponse < StandardError; end
102
+ end
103
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/hash/deep_merge'
4
+ require 'rspec/core/formatters/base_text_formatter'
5
+ require 'swagger_helper'
6
+
7
+ module Rswag
8
+ module Specs
9
+ class SwaggerFormatter < ::RSpec::Core::Formatters::BaseTextFormatter
10
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: Support for Ruby 2.6 will be dropped in v3.0') if RUBY_VERSION.start_with? '2.6'
11
+
12
+ if RSPEC_VERSION > 2
13
+ ::RSpec::Core::Formatters.register self, :example_group_finished, :stop
14
+ else
15
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: Support for RSpec 2.X will be dropped in v3.0')
16
+ end
17
+
18
+ def initialize(output, config = Rswag::Specs.config)
19
+ @output = output
20
+ @config = config
21
+
22
+ @output.puts 'Generating Swagger docs ...'
23
+ end
24
+
25
+ def example_group_finished(notification)
26
+ metadata = if RSPEC_VERSION > 2
27
+ notification.group.metadata
28
+ else
29
+ notification.metadata
30
+ end
31
+
32
+ # !metadata[:document] won't work, since nil means we should generate
33
+ # docs.
34
+ return if metadata[:document] == false
35
+ return unless metadata.key?(:response)
36
+
37
+ swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
38
+
39
+ unless doc_version(swagger_doc).start_with?('2')
40
+ # This is called multiple times per file!
41
+ # metadata[:operation] is also re-used between examples within file
42
+ # therefore be careful NOT to modify its content here.
43
+ upgrade_request_type!(metadata)
44
+ upgrade_servers!(swagger_doc)
45
+ upgrade_oauth!(swagger_doc)
46
+ upgrade_response_produces!(swagger_doc, metadata)
47
+ end
48
+
49
+ swagger_doc.deep_merge!(metadata_to_swagger(metadata))
50
+ end
51
+
52
+ def stop(_notification = nil)
53
+ @config.swagger_docs.each do |url_path, doc|
54
+ unless doc_version(doc).start_with?('2')
55
+ # doc[:paths]&.each_pair do |_k, v|
56
+ (doc[:paths] || {}).each_pair do |_k, v|
57
+ v.each_pair do |_verb, value|
58
+ is_hash = value.is_a?(Hash)
59
+ if is_hash && value[:parameters]
60
+ # schema_param = value[:parameters]&.find { |p| (p[:in] == :body || p[:in] == :formData) && p[:schema] }
61
+ schema_param = (value[:parameters] || []).find { |p| (p[:in] == :body || p[:in] == :formData) && p[:schema] }
62
+ mime_list = value[:consumes] || doc[:consumes]
63
+
64
+ if value && schema_param && mime_list
65
+ # value[:requestBody] = { content: {} } unless value.dig(:requestBody, :content)
66
+ value[:requestBody] = { content: {} } unless value[:requestBody] && value[:requestBody][:content]
67
+ value[:requestBody][:required] = true if schema_param[:required]
68
+ value[:requestBody][:description] = schema_param[:description] if schema_param[:description]
69
+ # examples = value.dig(:request_examples)
70
+ examples = value[:request_examples]
71
+ mime_list.each do |mime|
72
+ value[:requestBody][:content][mime] = { schema: schema_param[:schema] }
73
+ if examples
74
+ value[:requestBody][:content][mime][:examples] ||= {}
75
+ examples.map do |example|
76
+ value[:requestBody][:content][mime][:examples][example[:name]] = {
77
+ summary: example[:summary] || value[:summary],
78
+ value: example[:value]
79
+ }
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ value[:parameters].reject! { |p| p[:in] == :body || p[:in] == :formData }
86
+ end
87
+ remove_invalid_operation_keys!(value)
88
+ end
89
+ end
90
+ end
91
+
92
+ file_path = File.join(@config.swagger_root, url_path)
93
+ dirname = File.dirname(file_path)
94
+ FileUtils.mkdir_p dirname unless File.exist?(dirname)
95
+
96
+ File.open(file_path, 'w') do |file|
97
+ file.write(pretty_generate(doc))
98
+ end
99
+
100
+ @output.puts "Swagger doc generated at #{file_path}"
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def pretty_generate(doc)
107
+ if @config.swagger_format == :yaml
108
+ clean_doc = yaml_prepare(doc)
109
+ YAML.dump(clean_doc)
110
+ else # config errors are thrown in 'def swagger_format', no throw needed here
111
+ JSON.pretty_generate(doc)
112
+ end
113
+ end
114
+
115
+ def yaml_prepare(doc)
116
+ json_doc = JSON.pretty_generate(doc)
117
+ JSON.parse(json_doc)
118
+ end
119
+
120
+ def metadata_to_swagger(metadata)
121
+ response_code = metadata[:response][:code]
122
+ response = metadata[:response].reject { |k, _v| k == :code }
123
+
124
+ verb = metadata[:operation][:verb]
125
+ operation = metadata[:operation]
126
+ .reject { |k, _v| k == :verb }
127
+ .merge(responses: { response_code => response })
128
+
129
+ path_template = metadata[:path_item][:template]
130
+ path_item = metadata[:path_item]
131
+ .reject { |k, _v| k == :template }
132
+ .merge(verb => operation)
133
+
134
+ { paths: { path_template => path_item } }
135
+ end
136
+
137
+ def doc_version(doc)
138
+ doc[:openapi] || doc[:swagger] || '3'
139
+ end
140
+
141
+ def upgrade_response_produces!(swagger_doc, metadata)
142
+ # Accept header
143
+ mime_list = Array(metadata[:operation][:produces] || swagger_doc[:produces])
144
+ target_node = metadata[:response]
145
+ upgrade_content!(mime_list, target_node)
146
+ metadata[:response].delete(:schema)
147
+ end
148
+
149
+ def upgrade_content!(mime_list, target_node)
150
+ schema = target_node[:schema]
151
+ return if mime_list.empty? || schema.nil?
152
+
153
+ target_node[:content] ||= {}
154
+ mime_list.each do |mime_type|
155
+ # TODO: upgrade to have content-type specific schema
156
+ (target_node[:content][mime_type] ||= {}).merge!(schema: schema)
157
+ end
158
+ end
159
+
160
+ def upgrade_request_type!(metadata)
161
+ # No deprecation here as it seems valid to allow type as a shorthand
162
+ operation_nodes = metadata[:operation][:parameters] || []
163
+ path_nodes = metadata[:path_item][:parameters] || []
164
+ header_node = metadata[:response][:headers] || {}
165
+
166
+ (operation_nodes + path_nodes + [header_node]).each do |node|
167
+ if node && node[:type] && node[:schema].nil?
168
+ node[:schema] = { type: node[:type] }
169
+ node.delete(:type)
170
+ end
171
+ end
172
+ end
173
+
174
+ def upgrade_servers!(swagger_doc)
175
+ return unless swagger_doc[:servers].nil? && swagger_doc.key?(:schemes)
176
+
177
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: schemes, host, and basePath are replaced in OpenAPI3! Rename to array of servers[{url}] (in swagger_helper.rb)')
178
+
179
+ swagger_doc[:servers] = { urls: [] }
180
+ swagger_doc[:schemes].each do |scheme|
181
+ swagger_doc[:servers][:urls] << scheme + '://' + swagger_doc[:host] + swagger_doc[:basePath]
182
+ end
183
+
184
+ swagger_doc.delete(:schemes)
185
+ swagger_doc.delete(:host)
186
+ swagger_doc.delete(:basePath)
187
+ end
188
+
189
+ def upgrade_oauth!(swagger_doc)
190
+ # find flow in securitySchemes (securityDefinitions will have been re-written)
191
+ # schemes = swagger_doc.dig(:components, :securitySchemes)
192
+ schemes = swagger_doc[:components] && swagger_doc[:components][:securitySchemes]
193
+ # return unless schemes&.any? { |_k, v| v.key?(:flow) }
194
+ return unless schemes && schemes.any? { |_k, v| v.key?(:flow) }
195
+
196
+ schemes.each do |name, v|
197
+ next unless v.key?(:flow)
198
+
199
+ ActiveSupport::Deprecation.warn("Rswag::Specs: WARNING: securityDefinitions flow is replaced in OpenAPI3! Rename to components/securitySchemes/#{name}/flows[] (in swagger_helper.rb)")
200
+ flow = swagger_doc[:components][:securitySchemes][name].delete(:flow).to_s
201
+ if flow == 'accessCode'
202
+ ActiveSupport::Deprecation.warn("Rswag::Specs: WARNING: securityDefinitions accessCode is replaced in OpenAPI3! Rename to clientCredentials (in swagger_helper.rb)")
203
+ flow = 'authorizationCode'
204
+ end
205
+ if flow == 'application'
206
+ ActiveSupport::Deprecation.warn("Rswag::Specs: WARNING: securityDefinitions application is replaced in OpenAPI3! Rename to authorizationCode (in swagger_helper.rb)")
207
+ flow = 'clientCredentials'
208
+ end
209
+ flow_elements = swagger_doc[:components][:securitySchemes][name].except(:type).each_with_object({}) do |(k, _v), a|
210
+ a[k] = swagger_doc[:components][:securitySchemes][name].delete(k)
211
+ end
212
+ swagger_doc[:components][:securitySchemes][name].merge!(flows: { flow => flow_elements })
213
+ end
214
+ end
215
+
216
+ def remove_invalid_operation_keys!(value)
217
+ return unless value.is_a?(Hash)
218
+
219
+ value.delete(:consumes) if value[:consumes]
220
+ value.delete(:produces) if value[:produces]
221
+ value.delete(:request_examples) if value[:request_examples]
222
+ value[:parameters].each { |p| p.delete(:getter) } if value[:parameters]
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core'
4
+ require 'rswag/specs/example_group_helpers'
5
+ require 'rswag/specs/example_helpers'
6
+ require 'rswag/specs/configuration'
7
+ require 'rswag/specs/railtie' if defined?(Rails::Railtie)
8
+
9
+ module Rswag
10
+ module Specs
11
+ # Extend RSpec with a swagger-based DSL
12
+ ::RSpec.configure do |c|
13
+ c.add_setting :swagger_root
14
+ c.add_setting :swagger_docs
15
+ c.add_setting :swagger_dry_run
16
+ c.add_setting :swagger_format
17
+ c.add_setting :swagger_strict_schema_validation
18
+ c.extend ExampleGroupHelpers, type: :request
19
+ c.include ExampleHelpers, type: :request
20
+ end
21
+
22
+ def self.config
23
+ @config ||= Configuration.new(RSpec.configuration)
24
+ end
25
+
26
+ # Support Rails 3+ and RSpec 2+ (sigh!)
27
+ RAILS_VERSION = Rails::VERSION::MAJOR
28
+ RSPEC_VERSION = RSpec::Core::Version::STRING.split('.').first.to_i
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ namespace :rswag do
6
+ namespace :specs do
7
+ desc 'Generate Swagger JSON files from integration specs'
8
+ RSpec::Core::RakeTask.new('swaggerize') do |t|
9
+ t.pattern = ENV.fetch(
10
+ 'PATTERN',
11
+ 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
12
+ )
13
+
14
+ additional_rspec_opts = ENV.fetch(
15
+ 'ADDITIONAL_RSPEC_OPTS',
16
+ ''
17
+ )
18
+
19
+ t.rspec_opts = [additional_rspec_opts]
20
+
21
+ if Rswag::Specs::RSPEC_VERSION > 2 && Rswag::Specs.config.swagger_dry_run
22
+ t.rspec_opts += ['--format Rswag::Specs::SwaggerFormatter', '--dry-run', '--order defined']
23
+ else
24
+ ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: Support for RSpec 2.X will be dropped in v3.0')
25
+ t.rspec_opts += ['--format Rswag::Specs::SwaggerFormatter', '--order defined']
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ task rswag: ['rswag:specs:swaggerize']