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.
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