nexmo-oas-renderer 2.1.1
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 +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
|