nexmo-oas-renderer 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env.example +1 -0
- data/.github/workflows/push-docker-publish.yml +19 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.rubocop.yml +135 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +58 -0
- data/CONTRIBUTING.md +46 -0
- data/Dockerfile +6 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +248 -0
- data/LICENSE.txt +21 -0
- data/README.md +103 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/nexmo-oas-renderer +5 -0
- data/lib/nexmo/oas/engine.rb +11 -0
- data/lib/nexmo/oas/renderer.rb +13 -0
- data/lib/nexmo/oas/renderer/app.rb +201 -0
- data/lib/nexmo/oas/renderer/config.ru +9 -0
- data/lib/nexmo/oas/renderer/config/code_languages.yml +138 -0
- data/lib/nexmo/oas/renderer/config/dynamic_content.yml +1 -0
- data/lib/nexmo/oas/renderer/decorators/response_parser_decorator.rb +55 -0
- data/lib/nexmo/oas/renderer/helpers/navigation.rb +66 -0
- data/lib/nexmo/oas/renderer/helpers/render.rb +25 -0
- data/lib/nexmo/oas/renderer/helpers/summary.rb +33 -0
- data/lib/nexmo/oas/renderer/helpers/url.rb +19 -0
- data/lib/nexmo/oas/renderer/presenters/api_specification.rb +54 -0
- data/lib/nexmo/oas/renderer/presenters/endpoint.rb +21 -0
- data/lib/nexmo/oas/renderer/presenters/groups.rb +39 -0
- data/lib/nexmo/oas/renderer/presenters/navigation.rb +26 -0
- data/lib/nexmo/oas/renderer/presenters/open_api_specification.rb +62 -0
- data/lib/nexmo/oas/renderer/presenters/request_body_raw.rb +141 -0
- data/lib/nexmo/oas/renderer/presenters/response_format.rb +29 -0
- data/lib/nexmo/oas/renderer/presenters/response_tab/link.rb +36 -0
- data/lib/nexmo/oas/renderer/presenters/response_tab/panel.rb +45 -0
- data/lib/nexmo/oas/renderer/presenters/response_tabs.rb +58 -0
- data/lib/nexmo/oas/renderer/presenters/versions.rb +51 -0
- data/lib/nexmo/oas/renderer/public/500.html +66 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/android.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/curl.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/dotnet.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/ios.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/java.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/javascript.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/node.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/php.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/python.svg +11 -0
- data/lib/nexmo/oas/renderer/public/assets/images/brands/ruby.svg +1 -0
- data/lib/nexmo/oas/renderer/public/assets/images/lost.svg +37 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/components/format.js +47 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/nexmo-oas-renderer.js +65 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/popper.min.js +5 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/prism.js +22 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/tooltip.min.js +5 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.accordion.js +307 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.core.js +230 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.modal.js +300 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.tabs.js +143 -0
- data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.tooltip.js +76 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css +287 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css.map +7 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/api.scss +341 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/mediaqueries.scss +48 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/style.scss +6 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/dark.scss +89 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/light.scss +68 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/variables.scss +91 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta-prism.min.css +1 -0
- data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta.min.css +1 -0
- data/lib/nexmo/oas/renderer/public/assets/symbol/volta-icons.svg +1 -0
- data/lib/nexmo/oas/renderer/public/favicon.ico +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.eot +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.ttf +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.woff +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.woff2 +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.eot +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.ttf +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.woff +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.woff2 +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.eot +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.ttf +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.woff +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.woff2 +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.eot +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.ttf +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.woff +0 -0
- data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.woff2 +0 -0
- data/lib/nexmo/oas/renderer/services/oas_parser.rb +23 -0
- data/lib/nexmo/oas/renderer/services/open_api_definition_resolver.rb +35 -0
- data/lib/nexmo/oas/renderer/version.rb +9 -0
- data/lib/nexmo/oas/renderer/views/api/index.erb +19 -0
- data/lib/nexmo/oas/renderer/views/api/show.erb +1 -0
- data/lib/nexmo/oas/renderer/views/concepts/list/plain.html.erb +5 -0
- data/lib/nexmo/oas/renderer/views/layouts/_head.erb +6 -0
- data/lib/nexmo/oas/renderer/views/layouts/_javascripts.erb +14 -0
- data/lib/nexmo/oas/renderer/views/layouts/api.erb +23 -0
- data/lib/nexmo/oas/renderer/views/layouts/open_api.erb +12 -0
- data/lib/nexmo/oas/renderer/views/open_api/_auth.erb +74 -0
- data/lib/nexmo/oas/renderer/views/open_api/_available_endpoints.erb +25 -0
- data/lib/nexmo/oas/renderer/views/open_api/_callback.erb +5 -0
- data/lib/nexmo/oas/renderer/views/open_api/_callback_endpoint.erb +38 -0
- data/lib/nexmo/oas/renderer/views/open_api/_callbacks.erb +5 -0
- data/lib/nexmo/oas/renderer/views/open_api/_code_examples.erb +16 -0
- data/lib/nexmo/oas/renderer/views/open_api/_endpoint.erb +54 -0
- data/lib/nexmo/oas/renderer/views/open_api/_header.erb +71 -0
- data/lib/nexmo/oas/renderer/views/open_api/_model.erb +43 -0
- data/lib/nexmo/oas/renderer/views/open_api/_navigation.erb +68 -0
- data/lib/nexmo/oas/renderer/views/open_api/_parameter_groups.erb +56 -0
- data/lib/nexmo/oas/renderer/views/open_api/_parameters.erb +115 -0
- data/lib/nexmo/oas/renderer/views/open_api/_request_json.erb +4 -0
- data/lib/nexmo/oas/renderer/views/open_api/_request_one_of.erb +70 -0
- data/lib/nexmo/oas/renderer/views/open_api/_request_single.erb +53 -0
- data/lib/nexmo/oas/renderer/views/open_api/_requests.erb +22 -0
- data/lib/nexmo/oas/renderer/views/open_api/_response_description_parameters.erb +130 -0
- data/lib/nexmo/oas/renderer/views/open_api/_response_descriptions.erb +45 -0
- data/lib/nexmo/oas/renderer/views/open_api/_response_fields.erb +3 -0
- data/lib/nexmo/oas/renderer/views/open_api/_response_tabs.erb +18 -0
- data/lib/nexmo/oas/renderer/views/open_api/_responses.erb +51 -0
- data/lib/nexmo/oas/renderer/views/open_api/_tabbed_parameters.erb +64 -0
- data/lib/nexmo/oas/renderer/views/open_api/_tabbed_single_parameter.erb +56 -0
- data/lib/nexmo/oas/renderer/views/open_api/_webhooks.erb +30 -0
- data/lib/nexmo/oas/renderer/views/open_api/show.erb +26 -0
- data/lib/nexmo/oas/renderer/views/static/404.erb +6 -0
- data/nexmo-oas-renderer.gemspec +51 -0
- metadata +397 -0
@@ -0,0 +1 @@
|
|
1
|
+
dynamic_content_example: the future
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rouge'
|
4
|
+
require 'neatjson'
|
5
|
+
require_relative '../services/oas_parser'
|
6
|
+
|
7
|
+
module Nexmo
|
8
|
+
module OAS
|
9
|
+
module Renderer
|
10
|
+
class ResponseParserDecorator < ::OasParser::ResponseParser
|
11
|
+
def formatted_json
|
12
|
+
JSON.neat_generate(parse, {
|
13
|
+
wrap: true,
|
14
|
+
after_colon: 1,
|
15
|
+
})
|
16
|
+
end
|
17
|
+
|
18
|
+
def formatted_xml(xml_options = {})
|
19
|
+
xml_options[:root] = xml_options['name'] if xml_options
|
20
|
+
xml_string = xml(xml_options)
|
21
|
+
xml_string.gsub!(%r{^(\s+?)(<(?:\w|=|"|_|\s)+?>)(.+?)(</.+?>)}).each do |s|
|
22
|
+
indentation = Regexp.last_match(1)
|
23
|
+
indentation_plus_one = "#{Regexp.last_match(1)} "
|
24
|
+
opening_tag = Regexp.last_match(2)
|
25
|
+
content = Regexp.last_match(3)
|
26
|
+
closing_tag = Regexp.last_match(4)
|
27
|
+
|
28
|
+
next(s) if (indentation.size + opening_tag.size + content.size) < 60
|
29
|
+
|
30
|
+
next "#{indentation}#{opening_tag}\n#{indentation_plus_one}#{content}\n#{indentation}#{closing_tag}"
|
31
|
+
end
|
32
|
+
|
33
|
+
xml_string.gsub('<', '<')
|
34
|
+
end
|
35
|
+
|
36
|
+
def html(format = 'application/json', xml_options: nil, theme_light: nil)
|
37
|
+
case format
|
38
|
+
when 'application/json'
|
39
|
+
language = 'json'
|
40
|
+
response = formatted_json
|
41
|
+
when 'text/xml', 'application/xml'
|
42
|
+
language = 'xml'
|
43
|
+
response = formatted_xml(xml_options)
|
44
|
+
end
|
45
|
+
|
46
|
+
output = <<~HEREDOC
|
47
|
+
<pre class="pre-wrap language-#{language} #{theme_light ? 'Vlt-prism--dark' : ''} Vlt-prism--copy-disabled"><code>#{response}</code></pre>
|
48
|
+
HEREDOC
|
49
|
+
|
50
|
+
output
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nexmo
|
4
|
+
module OAS
|
5
|
+
module Renderer
|
6
|
+
module Helpers
|
7
|
+
module Navigation
|
8
|
+
HEADING_TAG_DEPTHS = {
|
9
|
+
'h0' => 0,
|
10
|
+
'h1' => 1,
|
11
|
+
'h2' => 2,
|
12
|
+
'h3' => 3,
|
13
|
+
'h4' => 4,
|
14
|
+
'h5' => 5,
|
15
|
+
'h6' => 6,
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def navigation_from_content(content:, title: nil)
|
19
|
+
content = "<h0 class='injected'>#{title}</h0>\n" + content if title
|
20
|
+
document = build_document(content)
|
21
|
+
|
22
|
+
nodes = ['<ul class="Vlt-sidemenu Vlt-sidemenu--rounded Vlt-sidemenu--flat navigation js-navigation">']
|
23
|
+
last_node = nil
|
24
|
+
|
25
|
+
document.css('.reveal').remove
|
26
|
+
|
27
|
+
document.css('h0,h2,h3,h4,h5,h6').each do |heading|
|
28
|
+
# If it's a header within tabbed content (including Code Snippets) we don't want to treat
|
29
|
+
# the header as a navigation item in the sidebar
|
30
|
+
next unless heading.ancestors('.Vlt-tabs').empty?
|
31
|
+
|
32
|
+
# Same with callouts
|
33
|
+
next unless heading.ancestors('.Vlt-callout').empty?
|
34
|
+
|
35
|
+
if last_node.nil? || heading.name == last_node.name
|
36
|
+
# Do nothing (cleaner than adding wrapping further conditions)
|
37
|
+
elsif heading.name >= last_node.name # e.g. h2 -> h3
|
38
|
+
nodes << '<ul>'
|
39
|
+
else # e.g. h4 -> h2
|
40
|
+
(HEADING_TAG_DEPTHS[last_node.name] - HEADING_TAG_DEPTHS[heading.name]).times do
|
41
|
+
nodes << '</li></ul>'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
nodes << <<~HEREDOC
|
46
|
+
<li>
|
47
|
+
<a class="Vlt-sidemenu__link Vlt-grey-darker" href="##{heading.attributes['id']}" data-scrollspy-id="#{heading['data-id']}">
|
48
|
+
#{heading.text}
|
49
|
+
</a>
|
50
|
+
HEREDOC
|
51
|
+
last_node = heading
|
52
|
+
end
|
53
|
+
nodes << '</li></ul>'
|
54
|
+
nodes.join("\n").html_safe
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def build_document(content)
|
60
|
+
Nokogiri::HTML::DocumentFragment.parse(content)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nexmo
|
4
|
+
module OAS
|
5
|
+
module Renderer
|
6
|
+
module Helpers
|
7
|
+
module Render
|
8
|
+
def find_template(views, name, engine, &block)
|
9
|
+
Array(views).each do |v|
|
10
|
+
super(v, name, engine, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(*args)
|
15
|
+
if args.length > 2
|
16
|
+
super
|
17
|
+
else
|
18
|
+
ApplicationController.renderer.render(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nexmo
|
4
|
+
module OAS
|
5
|
+
module Renderer
|
6
|
+
module Helpers
|
7
|
+
module Summary
|
8
|
+
def normalize_summary_title(summary, operation_id)
|
9
|
+
# return summary early if provided
|
10
|
+
return summary unless summary.nil?
|
11
|
+
|
12
|
+
# If the operation ID is camelCase,
|
13
|
+
if operation_id.match?(/^[a-zA-Z]\w+(?:[A-Z]\w+){1,}/x)
|
14
|
+
# Use the rails `.underscore` method to convert someString to some_string
|
15
|
+
operation_id = operation_id.underscore
|
16
|
+
end
|
17
|
+
|
18
|
+
# Replace snake_case and kebab-case with spaces and titelize the string
|
19
|
+
operation_id = operation_id.gsub(/(_|-)/, ' ').titleize
|
20
|
+
|
21
|
+
# Some terms need to be capitalised all the time
|
22
|
+
uppercase_array = %w[SMS DTMF]
|
23
|
+
operation_id.split(' ').map do |c|
|
24
|
+
next c.upcase if uppercase_array.include?(c.upcase)
|
25
|
+
|
26
|
+
c
|
27
|
+
end.join(' ')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nexmo
|
4
|
+
module OAS
|
5
|
+
module Renderer
|
6
|
+
module Helpers
|
7
|
+
module URL
|
8
|
+
def parameter_values(enum)
|
9
|
+
enum.map { |value| "<code>#{value}</code>" }.to_sentence(last_word_connector: ' or ', two_words_connector: ' or ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def canonical_path
|
13
|
+
request.path.chomp("/#{@code_language}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nexmo
|
4
|
+
module OAS
|
5
|
+
module Renderer
|
6
|
+
module Presenters
|
7
|
+
class ApiSpecification
|
8
|
+
def initialize(document_name:, code_language: nil)
|
9
|
+
@document_name = document_name
|
10
|
+
@code_language = code_language
|
11
|
+
end
|
12
|
+
|
13
|
+
def side_navigation
|
14
|
+
if defined?(NexmoDeveloper::Application)
|
15
|
+
"#{Rails.configuration.docs_base_path}/api/#{@document_name}"
|
16
|
+
else
|
17
|
+
"api/#{@document_name}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def document_path
|
22
|
+
if defined?(NexmoDeveloper::Application)
|
23
|
+
"#{Rails.configuration.docs_base_path}/_api/#{@document_name}.md"
|
24
|
+
else
|
25
|
+
"_api/#{@document_name}.md"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def document
|
30
|
+
@document ||= File.read(document_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def frontmatter
|
34
|
+
@frontmatter ||= YAML.safe_load(document)
|
35
|
+
end
|
36
|
+
|
37
|
+
def side_navigation_title
|
38
|
+
@side_navigation_title ||= frontmatter.fetch('api')
|
39
|
+
end
|
40
|
+
|
41
|
+
def document_title
|
42
|
+
@document_title ||= "#{side_navigation_title} > #{frontmatter.fetch('title')}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def content
|
46
|
+
options = {}
|
47
|
+
options.merge(code_language: @code_language) if @code_language
|
48
|
+
@content ||= Nexmo::Markdown::Renderer.new(options).call(document)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './response_format'
|
4
|
+
|
5
|
+
module Nexmo
|
6
|
+
module OAS
|
7
|
+
module Renderer
|
8
|
+
module Presenters
|
9
|
+
class Endpoint
|
10
|
+
def initialize(endpoint)
|
11
|
+
@endpoint = endpoint
|
12
|
+
end
|
13
|
+
|
14
|
+
def formats
|
15
|
+
@formats ||= ResponseFormat.new(@endpoint.responses).extract
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nexmo
|
4
|
+
module OAS
|
5
|
+
module Renderer
|
6
|
+
module Presenters
|
7
|
+
class Groups
|
8
|
+
def initialize(definition)
|
9
|
+
@definition = definition
|
10
|
+
end
|
11
|
+
|
12
|
+
def groups
|
13
|
+
tags = @definition.raw['tags']
|
14
|
+
# For now we only use the first tag in the list as an equivalent for the old x-group functionality
|
15
|
+
@groups = @definition.endpoints.group_by do |endpoint|
|
16
|
+
next nil unless tags
|
17
|
+
|
18
|
+
endpoint.raw['tags']&.first
|
19
|
+
end
|
20
|
+
|
21
|
+
# We want to use the order in which the tags are defined in the definition, so iterate over the tags
|
22
|
+
# and store the index against the tag name. We'll use this later for sorting
|
23
|
+
ordering = {}
|
24
|
+
tags&.each_with_index do |tag, index|
|
25
|
+
ordering[tag['name'].capitalize] = index
|
26
|
+
end
|
27
|
+
|
28
|
+
# Sort by the order in which they're defined in the definition
|
29
|
+
@groups = @groups.sort_by do |name, _|
|
30
|
+
next -1 if name.nil?
|
31
|
+
|
32
|
+
ordering[name.capitalize] || 999
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helpers/navigation'
|
4
|
+
|
5
|
+
module Nexmo
|
6
|
+
module OAS
|
7
|
+
module Renderer
|
8
|
+
module Presenters
|
9
|
+
class Navigation
|
10
|
+
include Helpers::Navigation
|
11
|
+
|
12
|
+
attr_reader :title, :content
|
13
|
+
|
14
|
+
def initialize(content:, title: nil)
|
15
|
+
@content = content
|
16
|
+
@title = title
|
17
|
+
end
|
18
|
+
|
19
|
+
def navigation_from_content
|
20
|
+
super(content: @content, title: @title)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require_relative './endpoint'
|
5
|
+
require_relative './response_format'
|
6
|
+
require_relative './groups'
|
7
|
+
require_relative './versions'
|
8
|
+
require_relative '../services/open_api_definition_resolver'
|
9
|
+
|
10
|
+
module Nexmo
|
11
|
+
module OAS
|
12
|
+
module Renderer
|
13
|
+
module Presenters
|
14
|
+
class OpenApiSpecification
|
15
|
+
extend Forwardable
|
16
|
+
|
17
|
+
attr_reader :definition_name
|
18
|
+
|
19
|
+
def_delegators :@versions, :current_version, :available_versions
|
20
|
+
def_delegators :@groups, :groups
|
21
|
+
|
22
|
+
def initialize(definition_name:, expand_responses:)
|
23
|
+
@definition_name = definition_name
|
24
|
+
@expand_responses = expand_responses
|
25
|
+
@versions = Versions.new(definition_name)
|
26
|
+
@groups = Groups.new(definition)
|
27
|
+
end
|
28
|
+
|
29
|
+
def errors?
|
30
|
+
File.exist?("#{API.oas_path}/../../errors/#{@definition_name}.md")
|
31
|
+
end
|
32
|
+
|
33
|
+
def definition_errors
|
34
|
+
return unless errors?
|
35
|
+
|
36
|
+
@definition_errors ||= Nexmo::Markdown::Renderer.new.call(
|
37
|
+
File.read("#{API.oas_path}/../../errors/#{@definition_name}.md")
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def definition
|
42
|
+
@definition ||= OpenApiDefinitionResolver.find(@definition_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def auto_expand_responses
|
46
|
+
@expand_responses
|
47
|
+
end
|
48
|
+
|
49
|
+
def formats
|
50
|
+
@formats ||= ResponseFormat.to_dropdown(endpoints.flat_map(&:formats).uniq)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def endpoints
|
56
|
+
@endpoints ||= definition.endpoints.map { |e| Endpoint.new(e) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nexmo
|
4
|
+
module OAS
|
5
|
+
module Renderer
|
6
|
+
module Presenters
|
7
|
+
class RequestBodyRaw
|
8
|
+
def initialize(parameters, options = {}, endpoint = nil)
|
9
|
+
@parameters = parameters
|
10
|
+
@options = options
|
11
|
+
@endpoint = endpoint
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_format(format)
|
15
|
+
return to_urlencoded if format == 'application/x-www-form-urlencoded'
|
16
|
+
|
17
|
+
to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_urlencoded
|
21
|
+
example = ''
|
22
|
+
body = URI.encode_www_form(generate_request)
|
23
|
+
if @endpoint
|
24
|
+
servers = @endpoint.path.servers || @endpoint.definition.servers
|
25
|
+
path = @endpoint.path.path.gsub(/\{(.+?)\}/, ':\1')
|
26
|
+
uri = URI("#{servers[0]['url']}#{path}")
|
27
|
+
example += "#{@endpoint.method.upcase} #{uri.path} HTTP/1.1\n"
|
28
|
+
example += "Host: #{uri.host} \n"
|
29
|
+
example += "Content-Type: application/x-www-form-urlencoded\n"
|
30
|
+
example += "Content-Length: #{body.length}\n"
|
31
|
+
example += "\n"
|
32
|
+
end
|
33
|
+
example += body
|
34
|
+
example
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_json(*_args)
|
38
|
+
JSON.pretty_generate(generate_request)
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_request(parameters = nil, options = nil)
|
42
|
+
parameters ||= @parameters
|
43
|
+
options ||= @options
|
44
|
+
output = {}
|
45
|
+
|
46
|
+
parameters.each do |parameter|
|
47
|
+
next if options['required_only'] && optional?(parameter, options['required'])
|
48
|
+
|
49
|
+
parameter_name = name(parameter)
|
50
|
+
param = parameter
|
51
|
+
|
52
|
+
# This is all required to handle an edge case where parameter.name is an OasParser::Property
|
53
|
+
# Which happens when you use a oneOf inside items in a property.
|
54
|
+
# I believe this is a bug, but it's a BC break in the parser
|
55
|
+
|
56
|
+
if parameter_name.instance_of?(OasParser::Property)
|
57
|
+
parameter_name = parameter.owner.name
|
58
|
+
param = OasParser::Parameter.new(parameter_name, parameter.schema)
|
59
|
+
end
|
60
|
+
|
61
|
+
if param.raw['items'] && param.raw['example']
|
62
|
+
output[parameter_name] = param.raw['example']
|
63
|
+
elsif param.raw['items'] && param.raw['items']['oneOf']
|
64
|
+
param = param.raw['items']['oneOf'][0]
|
65
|
+
output[parameter_name] = [generate_request(properties(param).map(&:name))]
|
66
|
+
elsif collection?(param) && properties?(param)
|
67
|
+
nested_output = generate_request(properties(param))
|
68
|
+
next unless nested_output.keys.length.positive?
|
69
|
+
|
70
|
+
nested_output = [nested_output] if param.array?
|
71
|
+
output[parameter_name] = nested_output
|
72
|
+
else
|
73
|
+
ex = example(param)
|
74
|
+
next unless ex
|
75
|
+
|
76
|
+
if ex.is_a?(String)
|
77
|
+
# Remove line breaks
|
78
|
+
ex = ex.gsub('<br />', ' ')
|
79
|
+
end
|
80
|
+
output[parameter_name] = ex
|
81
|
+
end
|
82
|
+
end
|
83
|
+
output
|
84
|
+
end
|
85
|
+
|
86
|
+
def items(parameter)
|
87
|
+
return parameter['items'] unless parameter.respond_to?(:items)
|
88
|
+
|
89
|
+
parameter.items
|
90
|
+
end
|
91
|
+
|
92
|
+
def example(parameter)
|
93
|
+
return parameter['example'] unless parameter.respond_to?(:example)
|
94
|
+
|
95
|
+
parameter.example
|
96
|
+
end
|
97
|
+
|
98
|
+
def name(parameter)
|
99
|
+
return parameter['name'] unless parameter.respond_to?(:name)
|
100
|
+
|
101
|
+
parameter.name
|
102
|
+
end
|
103
|
+
|
104
|
+
def properties(parameter)
|
105
|
+
return parameter['properties'] unless parameter.respond_to?(:properties)
|
106
|
+
|
107
|
+
parameter.properties
|
108
|
+
end
|
109
|
+
|
110
|
+
def array?(parameter)
|
111
|
+
return parameter['items'] unless parameter.respond_to?(:array?)
|
112
|
+
|
113
|
+
parameter.array?
|
114
|
+
end
|
115
|
+
|
116
|
+
def collection?(parameter)
|
117
|
+
return parameter['properties'] unless parameter.respond_to?(:collection?)
|
118
|
+
|
119
|
+
parameter.collection?
|
120
|
+
end
|
121
|
+
|
122
|
+
def properties?(parameter)
|
123
|
+
return parameter['properties'] unless parameter.respond_to?(:collection?)
|
124
|
+
|
125
|
+
parameter.properties.size.positive?
|
126
|
+
end
|
127
|
+
|
128
|
+
def optional?(parameter, allow_list)
|
129
|
+
return false if allow_list&.include?(name(parameter))
|
130
|
+
|
131
|
+
return false unless parameter.respond_to?(:required)
|
132
|
+
|
133
|
+
return false unless parameter.schema
|
134
|
+
|
135
|
+
!parameter.required
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|