nexmo-oas-renderer 0.11.2 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +135 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile +2 -2
  6. data/Gemfile.lock +91 -71
  7. data/Rakefile +2 -2
  8. data/bin/console +3 -3
  9. data/lib/nexmo/oas/engine.rb +2 -0
  10. data/lib/nexmo/oas/renderer.rb +5 -5
  11. data/lib/nexmo/oas/renderer/app.rb +71 -46
  12. data/lib/nexmo/oas/renderer/config.ru +4 -2
  13. data/lib/nexmo/oas/renderer/decorators/response_parser_decorator.rb +18 -18
  14. data/lib/nexmo/oas/renderer/helpers/navigation.rb +2 -2
  15. data/lib/nexmo/oas/renderer/helpers/render.rb +2 -1
  16. data/lib/nexmo/oas/renderer/helpers/summary.rb +4 -1
  17. data/lib/nexmo/oas/renderer/helpers/url.rb +2 -0
  18. data/lib/nexmo/oas/renderer/presenters/api_specification.rb +12 -3
  19. data/lib/nexmo/oas/renderer/presenters/endpoint.rb +2 -0
  20. data/lib/nexmo/oas/renderer/presenters/groups.rb +4 -0
  21. data/lib/nexmo/oas/renderer/presenters/navigation.rb +2 -0
  22. data/lib/nexmo/oas/renderer/presenters/open_api_specification.rb +5 -2
  23. data/lib/nexmo/oas/renderer/presenters/request_body_raw.rb +141 -0
  24. data/lib/nexmo/oas/renderer/presenters/response_format.rb +4 -2
  25. data/lib/nexmo/oas/renderer/presenters/response_tab/link.rb +3 -0
  26. data/lib/nexmo/oas/renderer/presenters/response_tab/panel.rb +8 -5
  27. data/lib/nexmo/oas/renderer/presenters/response_tabs.rb +6 -2
  28. data/lib/nexmo/oas/renderer/presenters/versions.rb +11 -10
  29. data/lib/nexmo/oas/renderer/public/assets/javascripts/components/format.js +12 -7
  30. data/lib/nexmo/oas/renderer/public/assets/javascripts/nexmo-oas-renderer.js +61 -27
  31. data/lib/nexmo/oas/renderer/public/assets/javascripts/popper.min.js +5 -0
  32. data/lib/nexmo/oas/renderer/public/assets/javascripts/tooltip.min.js +5 -0
  33. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.accordion.js +301 -243
  34. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.tooltip.js +76 -0
  35. data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css +255 -823
  36. data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css.map +2 -2
  37. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/api.scss +287 -90
  38. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/style.scss +2 -6
  39. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/dark.scss +89 -0
  40. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/light.scss +68 -0
  41. data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta-prism.min.css +1 -1
  42. data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta.min.css +1 -1
  43. data/lib/nexmo/oas/renderer/services/oas_parser.rb +2 -0
  44. data/lib/nexmo/oas/renderer/services/open_api_definition_resolver.rb +3 -1
  45. data/lib/nexmo/oas/renderer/version.rb +3 -1
  46. data/lib/nexmo/oas/renderer/views/layouts/_head.erb +1 -0
  47. data/lib/nexmo/oas/renderer/views/layouts/_javascripts.erb +5 -4
  48. data/lib/nexmo/oas/renderer/views/layouts/open_api.erb +3 -1
  49. data/lib/nexmo/oas/renderer/views/open_api/_auth.erb +74 -0
  50. data/lib/nexmo/oas/renderer/views/open_api/_available_endpoints.erb +25 -0
  51. data/lib/nexmo/oas/renderer/views/open_api/_callback_endpoint.erb +18 -27
  52. data/lib/nexmo/oas/renderer/views/open_api/_callbacks.erb +5 -0
  53. data/lib/nexmo/oas/renderer/views/open_api/_endpoint.erb +39 -124
  54. data/lib/nexmo/oas/renderer/views/open_api/_header.erb +71 -0
  55. data/lib/nexmo/oas/renderer/views/open_api/_model.erb +31 -26
  56. data/lib/nexmo/oas/renderer/views/open_api/_navigation.erb +54 -78
  57. data/lib/nexmo/oas/renderer/views/open_api/_parameter_groups.erb +2 -5
  58. data/lib/nexmo/oas/renderer/views/open_api/_parameters.erb +98 -169
  59. data/lib/nexmo/oas/renderer/views/open_api/_request_json.erb +4 -0
  60. data/lib/nexmo/oas/renderer/views/open_api/_request_one_of.erb +70 -0
  61. data/lib/nexmo/oas/renderer/views/open_api/_request_single.erb +53 -0
  62. data/lib/nexmo/oas/renderer/views/open_api/_requests.erb +22 -0
  63. data/lib/nexmo/oas/renderer/views/open_api/_response_description_parameters.erb +88 -90
  64. data/lib/nexmo/oas/renderer/views/open_api/_response_descriptions.erb +32 -12
  65. data/lib/nexmo/oas/renderer/views/open_api/_response_fields.erb +1 -16
  66. data/lib/nexmo/oas/renderer/views/open_api/_response_tabs.erb +2 -2
  67. data/lib/nexmo/oas/renderer/views/open_api/_responses.erb +51 -0
  68. data/lib/nexmo/oas/renderer/views/open_api/_tabbed_parameters.erb +15 -4
  69. data/lib/nexmo/oas/renderer/views/open_api/_tabbed_single_parameter.erb +56 -0
  70. data/lib/nexmo/oas/renderer/views/open_api/_webhooks.erb +30 -0
  71. data/lib/nexmo/oas/renderer/views/open_api/show.erb +10 -115
  72. data/nexmo-oas-renderer.gemspec +26 -26
  73. metadata +59 -48
  74. data/lib/nexmo/oas/renderer/public/assets/javascripts/components/scroll.js +0 -21
  75. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/core.scss +0 -137
  76. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/navigation.scss +0 -102
  77. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/nexmo.scss +0 -61
  78. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/syntax.scss +0 -63
  79. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/typography.scss +0 -248
  80. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/volta-templates.scss +0 -119
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../helpers/navigation'
2
4
 
3
5
  module Nexmo
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require_relative './endpoint'
3
5
  require_relative './response_format'
@@ -9,7 +11,6 @@ module Nexmo
9
11
  module OAS
10
12
  module Renderer
11
13
  module Presenters
12
-
13
14
  class OpenApiSpecification
14
15
  extend Forwardable
15
16
 
@@ -30,9 +31,11 @@ module Nexmo
30
31
  end
31
32
 
32
33
  def definition_errors
34
+ return unless errors?
35
+
33
36
  @definition_errors ||= Nexmo::Markdown::Renderer.new.call(
34
37
  File.read("#{API.oas_path}/../../errors/#{@definition_name}.md")
35
- ) if errors?
38
+ )
36
39
  end
37
40
 
38
41
  def definition
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
@@ -7,7 +9,7 @@ module Nexmo
7
9
  'application/json' => 'JSON',
8
10
  'application/xml' => 'XML',
9
11
  'text/xml' => 'XML',
10
- }
12
+ }.freeze
11
13
 
12
14
  def self.to_dropdown(formats)
13
15
  MAPPINGS.slice(*formats)
@@ -18,7 +20,7 @@ module Nexmo
18
20
  end
19
21
 
20
22
  def extract
21
- @formats ||= @responses.flat_map(&:formats).uniq
23
+ @extract ||= @responses.flat_map(&:formats).uniq
22
24
  end
23
25
  end
24
26
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
@@ -19,6 +21,7 @@ module Nexmo
19
21
 
20
22
  def data_tab_link
21
23
  return nil unless @schema['x-tab-id']
24
+
22
25
  @schema['x-tab-id']
23
26
  end
24
27
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
@@ -6,12 +8,13 @@ module Nexmo
6
8
  class Panel
7
9
  attr_reader :index
8
10
 
9
- def initialize(schema:, index:, format:, content:, endpoint:)
11
+ def initialize(schema:, index:, format:, content:, endpoint:, theme_light: nil) # rubocop:disable Metrics/ParameterLists
10
12
  @schema = schema
11
13
  @index = index
12
14
  @format = format
13
15
  @content = content
14
16
  @endpoint = endpoint
17
+ @theme_light = theme_light
15
18
  end
16
19
 
17
20
  def css_classes
@@ -22,15 +25,15 @@ module Nexmo
22
25
 
23
26
  def content
24
27
  if @content == :responses
25
- Nexmo::OAS::Renderer::ResponseParserDecorator.
26
- new(@schema).
27
- html(@format, xml_options: @schema['xml'])
28
+ Nexmo::OAS::Renderer::ResponseParserDecorator
29
+ .new(@schema)
30
+ .html(@format, xml_options: @schema['xml'], theme_light: @theme_light)
28
31
  else
29
32
  [:'open_api/_response_fields', locals: {
30
33
  schema: @schema,
31
34
  index: @index,
32
35
  format: @format,
33
- endpoint: @endpoint
36
+ endpoint: @endpoint,
34
37
  }]
35
38
  end
36
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative './response_tab/link'
2
4
  require_relative './response_tab/panel'
3
5
 
@@ -8,11 +10,12 @@ module Nexmo
8
10
  class ResponseTabs
9
11
  attr_reader :format
10
12
 
11
- def initialize(format, response, content, endpoint)
13
+ def initialize(format, response, content, endpoint, theme_light: nil)
12
14
  @format = format
13
15
  @response = response
14
16
  @content = content
15
17
  @endpoint = endpoint
18
+ @theme_light = theme_light
16
19
  end
17
20
 
18
21
  def tab_links
@@ -33,7 +36,8 @@ module Nexmo
33
36
  index: index,
34
37
  format: @format,
35
38
  content: @content,
36
- endpoint: @endpoint
39
+ endpoint: @endpoint,
40
+ theme_light: @theme_light
37
41
  )
38
42
  end
39
43
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
4
6
  module Presenters
5
-
6
7
  class Versions
7
8
  def initialize(definition_name)
8
9
  @definition_name = definition_name
@@ -22,28 +23,28 @@ module Nexmo
22
23
  def available_versions
23
24
  @available_versions ||= begin
24
25
  matches = definitions.select do |definition|
25
- definition.starts_with?(base_name) && !definition.include?("#{base_name}/")
26
+ definition.match(/^#{base_name}(\.v\d+)?$/) && !definition.include?("#{base_name}/")
26
27
  end
27
28
 
28
- matches.map do |definition|
29
- name = definition.chomp('.yml')
30
- m = /\.v(\d+)/.match(name)
31
- next { 'version' => '1', 'name' => name } unless m
32
- { 'version' => m[1], 'name' => name }
29
+ matches = matches.map do |definition|
30
+ m = /\.v(\d+)/.match(definition)
31
+ next { 'version' => '1', 'name' => definition } unless m
33
32
 
34
- end.sort_by { |v| v['version'] }
33
+ { 'version' => m[1], 'name' => definition }
34
+ end
35
+
36
+ matches.sort_by { |v| v['version'] }
35
37
  end
36
38
  end
37
39
 
38
40
  def definitions
39
41
  @definitions ||= begin
40
42
  Dir.glob("#{API.oas_path}/**/*.yml").map do |file|
41
- definition = file.sub("#{API.oas_path}/", '').chomp('.yml')
43
+ file.sub("#{API.oas_path}/", '').chomp('.yml')
42
44
  end
43
45
  end
44
46
  end
45
47
  end
46
-
47
48
  end
48
49
  end
49
50
  end
@@ -1,11 +1,14 @@
1
1
  class Format {
2
2
  constructor() {
3
- this.formatSelector = document.getElementsByClassName('js-format-selector')[0]
4
- if (this.formatSelector) {
5
- this.formatChanged(this.formatSelector.value, false)
6
- this.formatSelector.addEventListener('change', (event) => {
7
- this.formatChanged(event.target.value)
8
- })
3
+ this.formatSelectors = document.getElementsByClassName('js-format-selector');
4
+
5
+ if (this.formatSelectors.length) {
6
+ this.formatChanged(this.formatSelectors[0].value, false)
7
+ for (let selector of this.formatSelectors) {
8
+ selector.addEventListener('change', (event) => {
9
+ this.formatChanged(event.target.value)
10
+ })
11
+ }
9
12
 
10
13
  this.restoreFormat()
11
14
  }
@@ -34,7 +37,9 @@ class Format {
34
37
  if (window.localStorage) {
35
38
  const format = window.localStorage.getItem('format')
36
39
  if (format) {
37
- this.formatSelector.value = format
40
+ for (let selector of this.formatSelectors) {
41
+ selector.value = format
42
+ }
38
43
  this.formatChanged(format, false)
39
44
  }
40
45
  }
@@ -1,31 +1,65 @@
1
- document.addEventListener("DOMContentLoaded", function() {
2
-
3
- // Toggle text on accordion button for response fields
4
- var responseDescriptionTogglers = document.getElementsByClassName('response-description-toggle');
5
-
6
- Array.from(responseDescriptionTogglers).forEach(function(element) {
7
- element.addEventListener('click', function(event) {
8
- var text = event.target.textContent;
9
- var newText;
10
- if (text.indexOf('View') != -1) {
11
- newText = text.replace("View", "Hide");
12
- } else {
13
- newText = text.replace("Hide", "View");
14
- }
15
- event.target.textContent = newText;
16
- });
17
- });
1
+ if(document.querySelector(".oas-wrapper")){
2
+ if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
3
+ var queryString = new URLSearchParams(window.location.search);
4
+ if (!queryString.get('theme')) {
5
+ queryString.set("theme", "dark");
6
+ window.location.search = queryString.toString();
7
+ }
8
+ }
18
9
 
19
- // Handle people clicking on oneOf tabs by changing every one on the page
20
- var oneOfTabs = document.querySelectorAll('[data-tab-link]');
21
- Array.from(oneOfTabs).forEach(function(element) {
22
- element.addEventListener('click', function (event) {
23
- var link = event.target.getAttribute('data-tab-link');
24
- var matchingTabs = document.querySelectorAll('[data-tab-link="'+link+'"]');
25
- Array.from(matchingTabs).forEach(function(element) {
26
- element.dispatchEvent(new Event('toggle'));
27
- });
10
+ document.addEventListener("DOMContentLoaded", function() {
11
+ // Handle people clicking on oneOf tabs by changing every one on the page
12
+ var oneOfTabs = document.querySelectorAll('[data-tab-link]');
13
+ Array.from(oneOfTabs).forEach(function (element) {
14
+ element.addEventListener('click', function (event) {
15
+ var link = event.target.getAttribute('data-tab-link');
16
+ var matchingTabs = document.querySelectorAll('[data-tab-link="' + link + '"]');
17
+ Array.from(matchingTabs).forEach(function (element) {
18
+ element.dispatchEvent(new Event('toggle'));
28
19
  });
20
+ });
29
21
  });
30
22
 
31
- });
23
+ var toggleTopNav = function(event, closeOnly) {
24
+
25
+ if (event) {
26
+ if (!Array.from(event.target.classList).includes("oas-trigger")) {
27
+ return;
28
+ }
29
+ }
30
+
31
+ var c = document.querySelector('.oas-trigger-content');
32
+ if (closeOnly === true) {
33
+ c.style.display = "none";
34
+ return;
35
+ }
36
+ c.style.display = c.style.display == 'block' ? 'none' : "block";
37
+ };
38
+
39
+ if (document.querySelector('.oas-trigger')) {
40
+ document.querySelector('.oas-trigger').addEventListener("click", toggleTopNav);
41
+ }
42
+
43
+ document.querySelectorAll('a').forEach((element) => {
44
+ var href = element.getAttribute("href");
45
+ if (!href){ return; }
46
+ if (href.slice(0,1) !== "#"){ return; }
47
+ element.addEventListener('click', function (event) {
48
+ event.preventDefault();
49
+
50
+ toggleTopNav(null, true);
51
+
52
+ var to = document.querySelector(event.target.hash)
53
+ history.pushState({}, '', event.target.href);
54
+ window.scrollTo({
55
+ top: (window.scrollY + to.getBoundingClientRect().y) - 70,
56
+ left: 0,
57
+ })
58
+
59
+
60
+ });
61
+ });
62
+
63
+ });
64
+ }
65
+