rspec_api_documentation 5.1.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|