rspec_api_documentation 5.1.0 → 6.0.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 +4 -4
- data/lib/rspec_api_documentation.rb +26 -1
- data/lib/rspec_api_documentation/api_documentation.rb +2 -0
- data/lib/rspec_api_documentation/configuration.rb +11 -1
- data/lib/rspec_api_documentation/dsl/endpoint.rb +33 -1
- data/lib/rspec_api_documentation/dsl/endpoint/params.rb +19 -3
- data/lib/rspec_api_documentation/dsl/endpoint/set_param.rb +12 -1
- data/lib/rspec_api_documentation/dsl/resource.rb +23 -0
- data/lib/rspec_api_documentation/open_api/contact.rb +9 -0
- data/lib/rspec_api_documentation/open_api/example.rb +7 -0
- data/lib/rspec_api_documentation/open_api/header.rb +12 -0
- data/lib/rspec_api_documentation/open_api/headers.rb +7 -0
- data/lib/rspec_api_documentation/open_api/helper.rb +29 -0
- data/lib/rspec_api_documentation/open_api/info.rb +12 -0
- data/lib/rspec_api_documentation/open_api/license.rb +8 -0
- data/lib/rspec_api_documentation/open_api/node.rb +112 -0
- data/lib/rspec_api_documentation/open_api/operation.rb +18 -0
- data/lib/rspec_api_documentation/open_api/parameter.rb +33 -0
- data/lib/rspec_api_documentation/open_api/path.rb +13 -0
- data/lib/rspec_api_documentation/open_api/paths.rb +7 -0
- data/lib/rspec_api_documentation/open_api/response.rb +10 -0
- data/lib/rspec_api_documentation/open_api/responses.rb +9 -0
- data/lib/rspec_api_documentation/open_api/root.rb +21 -0
- data/lib/rspec_api_documentation/open_api/schema.rb +15 -0
- data/lib/rspec_api_documentation/open_api/security_definitions.rb +7 -0
- data/lib/rspec_api_documentation/open_api/security_schema.rb +14 -0
- data/lib/rspec_api_documentation/open_api/tag.rb +9 -0
- data/lib/rspec_api_documentation/views/api_blueprint_example.rb +15 -3
- data/lib/rspec_api_documentation/views/api_blueprint_index.rb +17 -2
- data/lib/rspec_api_documentation/views/markdown_example.rb +1 -1
- data/lib/rspec_api_documentation/views/markup_example.rb +8 -3
- data/lib/rspec_api_documentation/views/slate_index.rb +4 -0
- data/lib/rspec_api_documentation/writers/combined_json_writer.rb +1 -1
- data/lib/rspec_api_documentation/writers/json_iodocs_writer.rb +2 -2
- data/lib/rspec_api_documentation/writers/json_writer.rb +6 -6
- data/lib/rspec_api_documentation/writers/markdown_writer.rb +1 -1
- data/lib/rspec_api_documentation/writers/open_api_writer.rb +244 -0
- data/lib/rspec_api_documentation/writers/slate_writer.rb +2 -8
- data/templates/rspec_api_documentation/api_blueprint_index.mustache +6 -5
- data/templates/rspec_api_documentation/slate_index.mustache +8 -0
- metadata +57 -3
@@ -0,0 +1,18 @@
|
|
1
|
+
module RspecApiDocumentation
|
2
|
+
module OpenApi
|
3
|
+
class Operation < Node
|
4
|
+
add_setting :tags, :default => []
|
5
|
+
add_setting :summary
|
6
|
+
add_setting :description, :default => ''
|
7
|
+
add_setting :externalDocs
|
8
|
+
add_setting :operationId
|
9
|
+
add_setting :consumes
|
10
|
+
add_setting :produces
|
11
|
+
add_setting :parameters, :default => [], :schema => [Parameter]
|
12
|
+
add_setting :responses, :required => true, :schema => Responses
|
13
|
+
add_setting :schemes
|
14
|
+
add_setting :deprecated, :default => false
|
15
|
+
add_setting :security
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RspecApiDocumentation
|
2
|
+
module OpenApi
|
3
|
+
class Parameter < Node
|
4
|
+
# Required to write example values to description of parameter when option `with_example: true` is provided
|
5
|
+
attr_accessor :value
|
6
|
+
attr_accessor :with_example
|
7
|
+
|
8
|
+
add_setting :name, :required => true
|
9
|
+
add_setting :in, :required => true
|
10
|
+
add_setting :description, :default => ''
|
11
|
+
add_setting :required, :default => lambda { |parameter| parameter.in.to_s == 'path' ? true : false }
|
12
|
+
add_setting :schema
|
13
|
+
add_setting :type
|
14
|
+
add_setting :items
|
15
|
+
add_setting :default
|
16
|
+
add_setting :minimum
|
17
|
+
add_setting :maximum
|
18
|
+
add_setting :enum
|
19
|
+
|
20
|
+
def description_with_example
|
21
|
+
str = description_without_example.dup || ''
|
22
|
+
if with_example && value
|
23
|
+
str << "\n" unless str.empty?
|
24
|
+
str << "Eg, `#{value}`"
|
25
|
+
end
|
26
|
+
str
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :description_without_example, :description
|
30
|
+
alias_method :description, :description_with_example
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module RspecApiDocumentation
|
2
|
+
module OpenApi
|
3
|
+
class Path < Node
|
4
|
+
add_setting :get, :schema => Operation
|
5
|
+
add_setting :put, :schema => Operation
|
6
|
+
add_setting :post, :schema => Operation
|
7
|
+
add_setting :delete, :schema => Operation
|
8
|
+
add_setting :options, :schema => Operation
|
9
|
+
add_setting :head, :schema => Operation
|
10
|
+
add_setting :patch, :schema => Operation
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module RspecApiDocumentation
|
2
|
+
module OpenApi
|
3
|
+
class Response < Node
|
4
|
+
add_setting :description, :required => true, :default => 'Successful operation'
|
5
|
+
add_setting :schema, :schema => Schema
|
6
|
+
add_setting :headers, :schema => Headers
|
7
|
+
add_setting :examples, :schema => Example
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RspecApiDocumentation
|
2
|
+
module OpenApi
|
3
|
+
class Root < Node
|
4
|
+
add_setting :swagger, :default => '2.0', :required => true
|
5
|
+
add_setting :info, :default => Info.new, :required => true, :schema => Info
|
6
|
+
add_setting :host, :default => 'localhost:3000'
|
7
|
+
add_setting :basePath
|
8
|
+
add_setting :schemes, :default => %w(http https)
|
9
|
+
add_setting :consumes, :default => %w(application/json application/xml)
|
10
|
+
add_setting :produces, :default => %w(application/json application/xml)
|
11
|
+
add_setting :paths, :default => Paths.new, :required => true, :schema => Paths
|
12
|
+
add_setting :definitions
|
13
|
+
add_setting :parameters
|
14
|
+
add_setting :responses
|
15
|
+
add_setting :securityDefinitions, :schema => SecurityDefinitions
|
16
|
+
add_setting :security
|
17
|
+
add_setting :tags, :default => [], :schema => [Tag]
|
18
|
+
add_setting :externalDocs
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RspecApiDocumentation
|
2
|
+
module OpenApi
|
3
|
+
class Schema < Node
|
4
|
+
add_setting :format
|
5
|
+
add_setting :title
|
6
|
+
add_setting :description, :default => ''
|
7
|
+
add_setting :required
|
8
|
+
add_setting :enum
|
9
|
+
add_setting :type
|
10
|
+
add_setting :items
|
11
|
+
add_setting :properties
|
12
|
+
add_setting :example
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RspecApiDocumentation
|
2
|
+
module OpenApi
|
3
|
+
class SecuritySchema < Node
|
4
|
+
add_setting :type, :required => true
|
5
|
+
add_setting :description, :default => ''
|
6
|
+
add_setting :name
|
7
|
+
add_setting :in
|
8
|
+
add_setting :flow
|
9
|
+
add_setting :authorizationUrl
|
10
|
+
add_setting :tokenUrl
|
11
|
+
add_setting :scopes
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module RspecApiDocumentation
|
2
2
|
module Views
|
3
3
|
class ApiBlueprintExample < MarkupExample
|
4
|
-
TOTAL_SPACES_INDENTATION =
|
4
|
+
TOTAL_SPACES_INDENTATION = 12.freeze
|
5
5
|
|
6
6
|
def initialize(example, configuration)
|
7
7
|
super
|
@@ -20,14 +20,14 @@ module RspecApiDocumentation
|
|
20
20
|
|
21
21
|
def requests
|
22
22
|
super.map do |request|
|
23
|
-
request[:request_headers_text] = remove_utf8_for_json(request[:request_headers_text])
|
23
|
+
request[:request_headers_text] = remove_utf8_for_json(remove_content_type(request[:request_headers_text]))
|
24
24
|
request[:request_headers_text] = indent(request[:request_headers_text])
|
25
25
|
request[:request_content_type] = content_type(request[:request_headers])
|
26
26
|
request[:request_content_type] = remove_utf8_for_json(request[:request_content_type])
|
27
27
|
request[:request_body] = body_to_json(request, :request)
|
28
28
|
request[:request_body] = indent(request[:request_body])
|
29
29
|
|
30
|
-
request[:response_headers_text] = remove_utf8_for_json(request[:response_headers_text])
|
30
|
+
request[:response_headers_text] = remove_utf8_for_json(remove_content_type(request[:response_headers_text]))
|
31
31
|
request[:response_headers_text] = indent(request[:response_headers_text])
|
32
32
|
request[:response_content_type] = content_type(request[:response_headers])
|
33
33
|
request[:response_content_type] = remove_utf8_for_json(request[:response_content_type])
|
@@ -46,6 +46,18 @@ module RspecApiDocumentation
|
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
|
+
# `Content-Type` header is removed because the information would be duplicated
|
50
|
+
# since it's already present in `request[:request_content_type]`.
|
51
|
+
def remove_content_type(headers)
|
52
|
+
return unless headers
|
53
|
+
headers
|
54
|
+
.split("\n")
|
55
|
+
.reject { |header|
|
56
|
+
header.start_with?('Content-Type:')
|
57
|
+
}
|
58
|
+
.join("\n")
|
59
|
+
end
|
60
|
+
|
49
61
|
def has_request?(metadata)
|
50
62
|
metadata.any? do |key, value|
|
51
63
|
[:request_body, :request_headers, :request_content_type].include?(key) && value
|
@@ -23,7 +23,7 @@ module RspecApiDocumentation
|
|
23
23
|
{
|
24
24
|
"has_attributes?".to_sym => attrs.size > 0,
|
25
25
|
"has_parameters?".to_sym => params.size > 0,
|
26
|
-
route:
|
26
|
+
route: format_route(examples[0]),
|
27
27
|
route_name: examples[0][:route_name],
|
28
28
|
attributes: attrs,
|
29
29
|
parameters: params,
|
@@ -45,6 +45,17 @@ module RspecApiDocumentation
|
|
45
45
|
|
46
46
|
private
|
47
47
|
|
48
|
+
# APIB follows the RFC 6570 to format URI templates.
|
49
|
+
# According to it, simple string expansion (used to perform variable
|
50
|
+
# expansion) should be represented by `{var}` and not by `/:var`
|
51
|
+
# For example `/posts/:id` should become `/posts/{id}`
|
52
|
+
# cf. https://github.com/apiaryio/api-blueprint/blob/format-1A/API%20Blueprint%20Specification.md#431-resource-section
|
53
|
+
# cf. https://tools.ietf.org/html/rfc6570#section-3.2.6
|
54
|
+
def format_route(example)
|
55
|
+
route_uri = example[:route_uri].gsub(/:(.*?)([.\/?{]|$)/, '{\1}\2')
|
56
|
+
"#{route_uri}#{example[:route_optionals]}"
|
57
|
+
end
|
58
|
+
|
48
59
|
# APIB has both `parameters` and `attributes`. This generates a hash
|
49
60
|
# with all of its properties, like name, description, required.
|
50
61
|
# {
|
@@ -63,7 +74,11 @@ module RspecApiDocumentation
|
|
63
74
|
.uniq { |property| property[:name] }
|
64
75
|
.map do |property|
|
65
76
|
properties = []
|
66
|
-
|
77
|
+
if property[:required] == true
|
78
|
+
properties << 'required'
|
79
|
+
else
|
80
|
+
properties << 'optional'
|
81
|
+
end
|
67
82
|
properties << property[:type] if property[:type]
|
68
83
|
if properties.count > 0
|
69
84
|
property[:properties_description] = properties.join(", ")
|
@@ -3,6 +3,8 @@ require 'mustache'
|
|
3
3
|
module RspecApiDocumentation
|
4
4
|
module Views
|
5
5
|
class MarkupExample < Mustache
|
6
|
+
SPECIAL_CHARS = /[<>:"\/\\|?*]/.freeze
|
7
|
+
|
6
8
|
def initialize(example, configuration)
|
7
9
|
@example = example
|
8
10
|
@host = configuration.curl_host
|
@@ -19,12 +21,11 @@ module RspecApiDocumentation
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def dirname
|
22
|
-
resource_name.to_s.downcase
|
24
|
+
sanitize(resource_name.to_s.downcase)
|
23
25
|
end
|
24
26
|
|
25
27
|
def filename
|
26
|
-
|
27
|
-
basename = description.downcase.gsub(/\s+/, '_').gsub(special_chars, '')
|
28
|
+
basename = sanitize(description.downcase)
|
28
29
|
basename = Digest::MD5.new.update(description).to_s if basename.blank?
|
29
30
|
"#{basename}.#{extension}"
|
30
31
|
end
|
@@ -87,6 +88,10 @@ module RspecApiDocumentation
|
|
87
88
|
def content_type(headers)
|
88
89
|
headers && headers.fetch("Content-Type", nil)
|
89
90
|
end
|
91
|
+
|
92
|
+
def sanitize(name)
|
93
|
+
name.gsub(/\s+/, '_').gsub(SPECIAL_CHARS, '')
|
94
|
+
end
|
90
95
|
end
|
91
96
|
end
|
92
97
|
end
|
@@ -7,7 +7,7 @@ module RspecApiDocumentation
|
|
7
7
|
File.open(configuration.docs_dir.join("combined.json"), "w+") do |f|
|
8
8
|
examples = []
|
9
9
|
index.examples.each do |rspec_example|
|
10
|
-
examples << Formatter.to_json(
|
10
|
+
examples << Formatter.to_json(JSONExample.new(rspec_example, configuration))
|
11
11
|
end
|
12
12
|
|
13
13
|
f.write "["
|
@@ -32,7 +32,7 @@ module RspecApiDocumentation
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def examples
|
35
|
-
@index.examples.map { |example|
|
35
|
+
@index.examples.map { |example| JsonIodocsExample.new(example, @configuration) }
|
36
36
|
end
|
37
37
|
|
38
38
|
def as_json(opts = nil)
|
@@ -48,7 +48,7 @@ module RspecApiDocumentation
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
class
|
51
|
+
class JsonIodocsExample
|
52
52
|
def initialize(example, configuration)
|
53
53
|
@example = example
|
54
54
|
end
|
@@ -2,19 +2,19 @@ require 'rspec_api_documentation/writers/formatter'
|
|
2
2
|
|
3
3
|
module RspecApiDocumentation
|
4
4
|
module Writers
|
5
|
-
class
|
5
|
+
class JSONWriter < Writer
|
6
6
|
delegate :docs_dir, :to => :configuration
|
7
7
|
|
8
8
|
def write
|
9
9
|
File.open(docs_dir.join("index.json"), "w+") do |f|
|
10
|
-
f.write Formatter.to_json(
|
10
|
+
f.write Formatter.to_json(JSONIndex.new(index, configuration))
|
11
11
|
end
|
12
12
|
write_examples
|
13
13
|
end
|
14
14
|
|
15
15
|
def write_examples
|
16
16
|
index.examples.each do |example|
|
17
|
-
json_example =
|
17
|
+
json_example = JSONExample.new(example, configuration)
|
18
18
|
FileUtils.mkdir_p(docs_dir.join(json_example.dirname))
|
19
19
|
File.open(docs_dir.join(json_example.dirname, json_example.filename), "w+") do |f|
|
20
20
|
f.write Formatter.to_json(json_example)
|
@@ -23,7 +23,7 @@ module RspecApiDocumentation
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
class
|
26
|
+
class JSONIndex
|
27
27
|
def initialize(index, configuration)
|
28
28
|
@index = index
|
29
29
|
@configuration = configuration
|
@@ -34,7 +34,7 @@ module RspecApiDocumentation
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def examples
|
37
|
-
@index.examples.map { |example|
|
37
|
+
@index.examples.map { |example| JSONExample.new(example, @configuration) }
|
38
38
|
end
|
39
39
|
|
40
40
|
def as_json(opts = nil)
|
@@ -61,7 +61,7 @@ module RspecApiDocumentation
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
class
|
64
|
+
class JSONExample
|
65
65
|
def initialize(example, configuration)
|
66
66
|
@example = example
|
67
67
|
@host = configuration.curl_host
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'rspec_api_documentation/writers/formatter'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module RspecApiDocumentation
|
5
|
+
module Writers
|
6
|
+
class OpenApiWriter < Writer
|
7
|
+
FILENAME = 'open_api'
|
8
|
+
|
9
|
+
delegate :docs_dir, :configurations_dir, to: :configuration
|
10
|
+
|
11
|
+
def write
|
12
|
+
File.open(docs_dir.join("#{FILENAME}.json"), 'w+') do |f|
|
13
|
+
f.write Formatter.to_json(OpenApiIndex.new(index, configuration, load_config))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def load_config
|
20
|
+
return JSON.parse(File.read("#{configurations_dir}/open_api.json")) if File.exist?("#{configurations_dir}/open_api.json")
|
21
|
+
YAML.load_file("#{configurations_dir}/open_api.yml") if File.exist?("#{configurations_dir}/open_api.yml")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class OpenApiIndex
|
26
|
+
attr_reader :index, :configuration, :init_config
|
27
|
+
|
28
|
+
def initialize(index, configuration, init_config)
|
29
|
+
@index = index
|
30
|
+
@configuration = configuration
|
31
|
+
@init_config = init_config
|
32
|
+
end
|
33
|
+
|
34
|
+
def as_json
|
35
|
+
@specs = OpenApi::Root.new(init_config)
|
36
|
+
add_tags!
|
37
|
+
add_paths!
|
38
|
+
add_security_definitions!
|
39
|
+
specs.as_json
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :specs
|
45
|
+
|
46
|
+
def examples
|
47
|
+
index.examples.map { |example| OpenApiExample.new(example) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_security_definitions!
|
51
|
+
security_definitions = OpenApi::SecurityDefinitions.new
|
52
|
+
|
53
|
+
arr = examples.map do |example|
|
54
|
+
example.respond_to?(:authentications) ? example.authentications : nil
|
55
|
+
end.compact
|
56
|
+
|
57
|
+
arr.each do |securities|
|
58
|
+
securities.each do |security, opts|
|
59
|
+
schema = OpenApi::SecuritySchema.new(
|
60
|
+
name: opts[:name],
|
61
|
+
description: opts[:description],
|
62
|
+
type: opts[:type],
|
63
|
+
in: opts[:in]
|
64
|
+
)
|
65
|
+
security_definitions.add_setting security, :value => schema
|
66
|
+
end
|
67
|
+
end
|
68
|
+
specs.securityDefinitions = security_definitions unless arr.empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_tags!
|
72
|
+
tags = {}
|
73
|
+
examples.each do |example|
|
74
|
+
tags[example.resource_name] ||= example.resource_explanation
|
75
|
+
end
|
76
|
+
specs.safe_assign_setting(:tags, [])
|
77
|
+
tags.each do |name, desc|
|
78
|
+
specs.tags << OpenApi::Tag.new(name: name, description: desc) unless specs.tags.any? { |tag| tag.name == name }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_paths!
|
83
|
+
specs.safe_assign_setting(:paths, OpenApi::Paths.new)
|
84
|
+
examples.each do |example|
|
85
|
+
specs.paths.add_setting example.route, :value => OpenApi::Path.new
|
86
|
+
|
87
|
+
operation = specs.paths.setting(example.route).setting(example.http_method) || OpenApi::Operation.new
|
88
|
+
|
89
|
+
operation.safe_assign_setting(:tags, [example.resource_name])
|
90
|
+
operation.safe_assign_setting(:summary, example.respond_to?(:route_summary) ? example.route_summary : '')
|
91
|
+
operation.safe_assign_setting(:description, example.respond_to?(:route_description) ? example.route_description : '')
|
92
|
+
operation.safe_assign_setting(:responses, OpenApi::Responses.new)
|
93
|
+
operation.safe_assign_setting(:parameters, extract_parameters(example))
|
94
|
+
operation.safe_assign_setting(:consumes, example.requests.map { |request| request[:request_content_type] }.compact.map { |q| q[/[^;]+/] })
|
95
|
+
operation.safe_assign_setting(:produces, example.requests.map { |request| request[:response_content_type] }.compact.map { |q| q[/[^;]+/] })
|
96
|
+
operation.safe_assign_setting(:security, example.respond_to?(:authentications) ? example.authentications.map { |(k, _)| {k => []} } : [])
|
97
|
+
|
98
|
+
process_responses(operation.responses, example)
|
99
|
+
|
100
|
+
specs.paths.setting(example.route).assign_setting(example.http_method, operation)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def process_responses(responses, example)
|
105
|
+
schema = extract_schema(example.respond_to?(:response_fields) ? example.response_fields : [])
|
106
|
+
example.requests.each do |request|
|
107
|
+
response = OpenApi::Response.new(
|
108
|
+
description: example.description,
|
109
|
+
schema: schema
|
110
|
+
)
|
111
|
+
|
112
|
+
if request[:response_headers]
|
113
|
+
response.safe_assign_setting(:headers, OpenApi::Headers.new)
|
114
|
+
request[:response_headers].each do |header, value|
|
115
|
+
response.headers.add_setting header, :value => OpenApi::Header.new('x-example-value' => value)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if /\A(?<response_content_type>[^;]+)/ =~ request[:response_content_type]
|
120
|
+
response.safe_assign_setting(:examples, OpenApi::Example.new)
|
121
|
+
response_body = JSON.parse(request[:response_body]) rescue nil
|
122
|
+
response.examples.add_setting response_content_type, :value => response_body
|
123
|
+
end
|
124
|
+
responses.add_setting "#{request[:response_status]}", :value => response
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def extract_schema(fields)
|
129
|
+
schema = {type: 'object', properties: {}}
|
130
|
+
|
131
|
+
fields.each do |field|
|
132
|
+
current = schema
|
133
|
+
if field[:scope]
|
134
|
+
[*field[:scope]].each do |scope|
|
135
|
+
current[:properties][scope] ||= {type: 'object', properties: {}}
|
136
|
+
current = current[:properties][scope]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
current[:properties][field[:name]] = {type: field[:type] || OpenApi::Helper.extract_type(field[:value])}
|
140
|
+
current[:properties][field[:name]][:example] = field[:value] if field[:value] && field[:with_example]
|
141
|
+
current[:properties][field[:name]][:default] = field[:default] if field[:default]
|
142
|
+
current[:properties][field[:name]][:description] = field[:description] if field[:description]
|
143
|
+
|
144
|
+
opts = {enum: field[:enum], minimum: field[:minimum], maximum: field[:maximum]}
|
145
|
+
|
146
|
+
if current[:properties][field[:name]][:type] == :array
|
147
|
+
current[:properties][field[:name]][:items] = field[:items] || OpenApi::Helper.extract_items(field[:value][0], opts)
|
148
|
+
else
|
149
|
+
opts.each { |k, v| current[:properties][field[:name]][k] = v if v }
|
150
|
+
end
|
151
|
+
|
152
|
+
current[:required] ||= [] << field[:name] if field[:required]
|
153
|
+
end
|
154
|
+
|
155
|
+
OpenApi::Schema.new(schema)
|
156
|
+
end
|
157
|
+
|
158
|
+
def extract_parameters(example)
|
159
|
+
extract_known_parameters(example.extended_parameters.select { |p| !p[:in].nil? }) +
|
160
|
+
extract_unknown_parameters(example, example.extended_parameters.select { |p| p[:in].nil? })
|
161
|
+
end
|
162
|
+
|
163
|
+
def extract_parameter(opts)
|
164
|
+
OpenApi::Parameter.new(
|
165
|
+
name: opts[:name],
|
166
|
+
in: opts[:in],
|
167
|
+
description: opts[:description],
|
168
|
+
required: opts[:required],
|
169
|
+
type: opts[:type] || OpenApi::Helper.extract_type(opts[:value]),
|
170
|
+
value: opts[:value],
|
171
|
+
with_example: opts[:with_example],
|
172
|
+
default: opts[:default],
|
173
|
+
).tap do |elem|
|
174
|
+
if elem.type == :array
|
175
|
+
elem.items = opts[:items] || OpenApi::Helper.extract_items(opts[:value][0], { minimum: opts[:minimum], maximum: opts[:maximum], enum: opts[:enum] })
|
176
|
+
else
|
177
|
+
elem.minimum = opts[:minimum]
|
178
|
+
elem.maximum = opts[:maximum]
|
179
|
+
elem.enum = opts[:enum]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def extract_unknown_parameters(example, parameters)
|
185
|
+
if example.http_method == :get
|
186
|
+
parameters.map { |parameter| extract_parameter(parameter.merge(in: :query)) }
|
187
|
+
elsif parameters.any? { |parameter| !parameter[:scope].nil? }
|
188
|
+
[OpenApi::Parameter.new(
|
189
|
+
name: :body,
|
190
|
+
in: :body,
|
191
|
+
description: '',
|
192
|
+
schema: extract_schema(parameters)
|
193
|
+
)]
|
194
|
+
else
|
195
|
+
parameters.map { |parameter| extract_parameter(parameter.merge(in: :formData)) }
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def extract_known_parameters(parameters)
|
200
|
+
result = parameters.select { |parameter| %w(query path header formData).include?(parameter[:in].to_s) }
|
201
|
+
.map { |parameter| extract_parameter(parameter) }
|
202
|
+
|
203
|
+
body = parameters.select { |parameter| %w(body).include?(parameter[:in].to_s) }
|
204
|
+
|
205
|
+
result.unshift(
|
206
|
+
OpenApi::Parameter.new(
|
207
|
+
name: :body,
|
208
|
+
in: :body,
|
209
|
+
description: '',
|
210
|
+
schema: extract_schema(body)
|
211
|
+
)
|
212
|
+
) unless body.empty?
|
213
|
+
|
214
|
+
result
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
class OpenApiExample
|
219
|
+
def initialize(example)
|
220
|
+
@example = example
|
221
|
+
end
|
222
|
+
|
223
|
+
def method_missing(method, *args, &block)
|
224
|
+
@example.send(method, *args, &block)
|
225
|
+
end
|
226
|
+
|
227
|
+
def respond_to?(method, include_private = false)
|
228
|
+
super || @example.respond_to?(method, include_private)
|
229
|
+
end
|
230
|
+
|
231
|
+
def http_method
|
232
|
+
metadata[:method]
|
233
|
+
end
|
234
|
+
|
235
|
+
def requests
|
236
|
+
super.select { |request| request[:request_method].to_s.downcase == http_method.to_s.downcase }
|
237
|
+
end
|
238
|
+
|
239
|
+
def route
|
240
|
+
super.gsub(/:(?<parameter>[^\/]+)/, '{\k<parameter>}')
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|