nexmo-oas-renderer 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +1 -0
  3. data/.github/workflows/push-docker-publish.yml +19 -0
  4. data/.gitignore +6 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +135 -0
  7. data/.travis.yml +9 -0
  8. data/CHANGELOG.md +58 -0
  9. data/CONTRIBUTING.md +46 -0
  10. data/Dockerfile +6 -0
  11. data/Gemfile +6 -0
  12. data/Gemfile.lock +248 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +103 -0
  15. data/Rakefile +2 -0
  16. data/bin/console +14 -0
  17. data/bin/setup +8 -0
  18. data/exe/nexmo-oas-renderer +5 -0
  19. data/lib/nexmo/oas/engine.rb +11 -0
  20. data/lib/nexmo/oas/renderer.rb +13 -0
  21. data/lib/nexmo/oas/renderer/app.rb +201 -0
  22. data/lib/nexmo/oas/renderer/config.ru +9 -0
  23. data/lib/nexmo/oas/renderer/config/code_languages.yml +138 -0
  24. data/lib/nexmo/oas/renderer/config/dynamic_content.yml +1 -0
  25. data/lib/nexmo/oas/renderer/decorators/response_parser_decorator.rb +55 -0
  26. data/lib/nexmo/oas/renderer/helpers/navigation.rb +66 -0
  27. data/lib/nexmo/oas/renderer/helpers/render.rb +25 -0
  28. data/lib/nexmo/oas/renderer/helpers/summary.rb +33 -0
  29. data/lib/nexmo/oas/renderer/helpers/url.rb +19 -0
  30. data/lib/nexmo/oas/renderer/presenters/api_specification.rb +54 -0
  31. data/lib/nexmo/oas/renderer/presenters/endpoint.rb +21 -0
  32. data/lib/nexmo/oas/renderer/presenters/groups.rb +39 -0
  33. data/lib/nexmo/oas/renderer/presenters/navigation.rb +26 -0
  34. data/lib/nexmo/oas/renderer/presenters/open_api_specification.rb +62 -0
  35. data/lib/nexmo/oas/renderer/presenters/request_body_raw.rb +141 -0
  36. data/lib/nexmo/oas/renderer/presenters/response_format.rb +29 -0
  37. data/lib/nexmo/oas/renderer/presenters/response_tab/link.rb +36 -0
  38. data/lib/nexmo/oas/renderer/presenters/response_tab/panel.rb +45 -0
  39. data/lib/nexmo/oas/renderer/presenters/response_tabs.rb +58 -0
  40. data/lib/nexmo/oas/renderer/presenters/versions.rb +51 -0
  41. data/lib/nexmo/oas/renderer/public/500.html +66 -0
  42. data/lib/nexmo/oas/renderer/public/assets/images/brands/android.svg +1 -0
  43. data/lib/nexmo/oas/renderer/public/assets/images/brands/curl.svg +1 -0
  44. data/lib/nexmo/oas/renderer/public/assets/images/brands/dotnet.svg +1 -0
  45. data/lib/nexmo/oas/renderer/public/assets/images/brands/ios.svg +1 -0
  46. data/lib/nexmo/oas/renderer/public/assets/images/brands/java.svg +1 -0
  47. data/lib/nexmo/oas/renderer/public/assets/images/brands/javascript.svg +1 -0
  48. data/lib/nexmo/oas/renderer/public/assets/images/brands/node.svg +1 -0
  49. data/lib/nexmo/oas/renderer/public/assets/images/brands/php.svg +1 -0
  50. data/lib/nexmo/oas/renderer/public/assets/images/brands/python.svg +11 -0
  51. data/lib/nexmo/oas/renderer/public/assets/images/brands/ruby.svg +1 -0
  52. data/lib/nexmo/oas/renderer/public/assets/images/lost.svg +37 -0
  53. data/lib/nexmo/oas/renderer/public/assets/javascripts/components/format.js +47 -0
  54. data/lib/nexmo/oas/renderer/public/assets/javascripts/nexmo-oas-renderer.js +65 -0
  55. data/lib/nexmo/oas/renderer/public/assets/javascripts/popper.min.js +5 -0
  56. data/lib/nexmo/oas/renderer/public/assets/javascripts/prism.js +22 -0
  57. data/lib/nexmo/oas/renderer/public/assets/javascripts/tooltip.min.js +5 -0
  58. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.accordion.js +307 -0
  59. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.core.js +230 -0
  60. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.modal.js +300 -0
  61. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.tabs.js +143 -0
  62. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.tooltip.js +76 -0
  63. data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css +287 -0
  64. data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css.map +7 -0
  65. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/api.scss +341 -0
  66. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/mediaqueries.scss +48 -0
  67. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/style.scss +6 -0
  68. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/dark.scss +89 -0
  69. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/light.scss +68 -0
  70. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/variables.scss +91 -0
  71. data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta-prism.min.css +1 -0
  72. data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta.min.css +1 -0
  73. data/lib/nexmo/oas/renderer/public/assets/symbol/volta-icons.svg +1 -0
  74. data/lib/nexmo/oas/renderer/public/favicon.ico +0 -0
  75. data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.eot +0 -0
  76. data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.ttf +0 -0
  77. data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.woff +0 -0
  78. data/lib/nexmo/oas/renderer/public/fonts/Spezia-Regular.woff2 +0 -0
  79. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.eot +0 -0
  80. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.ttf +0 -0
  81. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.woff +0 -0
  82. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiBold.woff2 +0 -0
  83. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.eot +0 -0
  84. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.ttf +0 -0
  85. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.woff +0 -0
  86. data/lib/nexmo/oas/renderer/public/fonts/Spezia-SemiMonoSemiBold.woff2 +0 -0
  87. data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.eot +0 -0
  88. data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.ttf +0 -0
  89. data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.woff +0 -0
  90. data/lib/nexmo/oas/renderer/public/fonts/Spezia-WideMedium.woff2 +0 -0
  91. data/lib/nexmo/oas/renderer/services/oas_parser.rb +23 -0
  92. data/lib/nexmo/oas/renderer/services/open_api_definition_resolver.rb +35 -0
  93. data/lib/nexmo/oas/renderer/version.rb +9 -0
  94. data/lib/nexmo/oas/renderer/views/api/index.erb +19 -0
  95. data/lib/nexmo/oas/renderer/views/api/show.erb +1 -0
  96. data/lib/nexmo/oas/renderer/views/concepts/list/plain.html.erb +5 -0
  97. data/lib/nexmo/oas/renderer/views/layouts/_head.erb +6 -0
  98. data/lib/nexmo/oas/renderer/views/layouts/_javascripts.erb +14 -0
  99. data/lib/nexmo/oas/renderer/views/layouts/api.erb +23 -0
  100. data/lib/nexmo/oas/renderer/views/layouts/open_api.erb +12 -0
  101. data/lib/nexmo/oas/renderer/views/open_api/_auth.erb +74 -0
  102. data/lib/nexmo/oas/renderer/views/open_api/_available_endpoints.erb +25 -0
  103. data/lib/nexmo/oas/renderer/views/open_api/_callback.erb +5 -0
  104. data/lib/nexmo/oas/renderer/views/open_api/_callback_endpoint.erb +38 -0
  105. data/lib/nexmo/oas/renderer/views/open_api/_callbacks.erb +5 -0
  106. data/lib/nexmo/oas/renderer/views/open_api/_code_examples.erb +16 -0
  107. data/lib/nexmo/oas/renderer/views/open_api/_endpoint.erb +54 -0
  108. data/lib/nexmo/oas/renderer/views/open_api/_header.erb +71 -0
  109. data/lib/nexmo/oas/renderer/views/open_api/_model.erb +43 -0
  110. data/lib/nexmo/oas/renderer/views/open_api/_navigation.erb +68 -0
  111. data/lib/nexmo/oas/renderer/views/open_api/_parameter_groups.erb +56 -0
  112. data/lib/nexmo/oas/renderer/views/open_api/_parameters.erb +115 -0
  113. data/lib/nexmo/oas/renderer/views/open_api/_request_json.erb +4 -0
  114. data/lib/nexmo/oas/renderer/views/open_api/_request_one_of.erb +70 -0
  115. data/lib/nexmo/oas/renderer/views/open_api/_request_single.erb +53 -0
  116. data/lib/nexmo/oas/renderer/views/open_api/_requests.erb +22 -0
  117. data/lib/nexmo/oas/renderer/views/open_api/_response_description_parameters.erb +130 -0
  118. data/lib/nexmo/oas/renderer/views/open_api/_response_descriptions.erb +45 -0
  119. data/lib/nexmo/oas/renderer/views/open_api/_response_fields.erb +3 -0
  120. data/lib/nexmo/oas/renderer/views/open_api/_response_tabs.erb +18 -0
  121. data/lib/nexmo/oas/renderer/views/open_api/_responses.erb +51 -0
  122. data/lib/nexmo/oas/renderer/views/open_api/_tabbed_parameters.erb +64 -0
  123. data/lib/nexmo/oas/renderer/views/open_api/_tabbed_single_parameter.erb +56 -0
  124. data/lib/nexmo/oas/renderer/views/open_api/_webhooks.erb +30 -0
  125. data/lib/nexmo/oas/renderer/views/open_api/show.erb +26 -0
  126. data/lib/nexmo/oas/renderer/views/static/404.erb +6 -0
  127. data/nexmo-oas-renderer.gemspec +51 -0
  128. 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('<', '&lt;')
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