nexmo-oas-renderer 0.10.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +135 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +15 -0
  5. data/Gemfile +2 -2
  6. data/Gemfile.lock +94 -74
  7. data/README.md +8 -0
  8. data/Rakefile +2 -2
  9. data/bin/console +3 -3
  10. data/lib/nexmo/oas/engine.rb +2 -0
  11. data/lib/nexmo/oas/renderer.rb +5 -5
  12. data/lib/nexmo/oas/renderer/app.rb +79 -40
  13. data/lib/nexmo/oas/renderer/config.ru +4 -2
  14. data/lib/nexmo/oas/renderer/decorators/response_parser_decorator.rb +18 -18
  15. data/lib/nexmo/oas/renderer/helpers/navigation.rb +2 -2
  16. data/lib/nexmo/oas/renderer/helpers/render.rb +2 -1
  17. data/lib/nexmo/oas/renderer/helpers/summary.rb +4 -1
  18. data/lib/nexmo/oas/renderer/helpers/url.rb +2 -0
  19. data/lib/nexmo/oas/renderer/presenters/api_specification.rb +2 -1
  20. data/lib/nexmo/oas/renderer/presenters/endpoint.rb +2 -0
  21. data/lib/nexmo/oas/renderer/presenters/groups.rb +4 -0
  22. data/lib/nexmo/oas/renderer/presenters/navigation.rb +2 -0
  23. data/lib/nexmo/oas/renderer/presenters/open_api_specification.rb +5 -2
  24. data/lib/nexmo/oas/renderer/presenters/request_body_raw.rb +141 -0
  25. data/lib/nexmo/oas/renderer/presenters/response_format.rb +4 -2
  26. data/lib/nexmo/oas/renderer/presenters/response_tab/link.rb +3 -0
  27. data/lib/nexmo/oas/renderer/presenters/response_tab/panel.rb +8 -5
  28. data/lib/nexmo/oas/renderer/presenters/response_tabs.rb +6 -2
  29. data/lib/nexmo/oas/renderer/presenters/versions.rb +11 -10
  30. data/lib/nexmo/oas/renderer/public/assets/javascripts/components/format.js +12 -7
  31. data/lib/nexmo/oas/renderer/public/assets/javascripts/nexmo-oas-renderer.js +51 -24
  32. data/lib/nexmo/oas/renderer/public/assets/javascripts/popper.min.js +5 -0
  33. data/lib/nexmo/oas/renderer/public/assets/javascripts/tooltip.min.js +5 -0
  34. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.accordion.js +301 -243
  35. data/lib/nexmo/oas/renderer/public/assets/javascripts/volta.tooltip.js +76 -0
  36. data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css +255 -823
  37. data/lib/nexmo/oas/renderer/public/assets/stylesheets/nexmo-oas-renderer.css.map +2 -2
  38. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/api.scss +287 -90
  39. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/style.scss +2 -6
  40. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/dark.scss +89 -0
  41. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/themes/light.scss +68 -0
  42. data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta-prism.min.css +1 -1
  43. data/lib/nexmo/oas/renderer/public/assets/stylesheets/volta.min.css +1 -1
  44. data/lib/nexmo/oas/renderer/services/oas_parser.rb +2 -0
  45. data/lib/nexmo/oas/renderer/services/open_api_definition_resolver.rb +3 -1
  46. data/lib/nexmo/oas/renderer/version.rb +3 -1
  47. data/lib/nexmo/oas/renderer/views/layouts/_head.erb +1 -0
  48. data/lib/nexmo/oas/renderer/views/layouts/_javascripts.erb +5 -4
  49. data/lib/nexmo/oas/renderer/views/layouts/open_api.erb +3 -1
  50. data/lib/nexmo/oas/renderer/views/open_api/_auth.erb +74 -0
  51. data/lib/nexmo/oas/renderer/views/open_api/_available_endpoints.erb +25 -0
  52. data/lib/nexmo/oas/renderer/views/open_api/_callback_endpoint.erb +18 -27
  53. data/lib/nexmo/oas/renderer/views/open_api/_callbacks.erb +5 -0
  54. data/lib/nexmo/oas/renderer/views/open_api/_endpoint.erb +39 -124
  55. data/lib/nexmo/oas/renderer/views/open_api/_header.erb +71 -0
  56. data/lib/nexmo/oas/renderer/views/open_api/_model.erb +31 -26
  57. data/lib/nexmo/oas/renderer/views/open_api/_navigation.erb +54 -78
  58. data/lib/nexmo/oas/renderer/views/open_api/_parameter_groups.erb +2 -5
  59. data/lib/nexmo/oas/renderer/views/open_api/_parameters.erb +98 -153
  60. data/lib/nexmo/oas/renderer/views/open_api/_request_json.erb +4 -0
  61. data/lib/nexmo/oas/renderer/views/open_api/_request_one_of.erb +70 -0
  62. data/lib/nexmo/oas/renderer/views/open_api/_request_single.erb +53 -0
  63. data/lib/nexmo/oas/renderer/views/open_api/_requests.erb +22 -0
  64. data/lib/nexmo/oas/renderer/views/open_api/_response_description_parameters.erb +88 -90
  65. data/lib/nexmo/oas/renderer/views/open_api/_response_descriptions.erb +17 -12
  66. data/lib/nexmo/oas/renderer/views/open_api/_response_fields.erb +1 -16
  67. data/lib/nexmo/oas/renderer/views/open_api/_response_tabs.erb +2 -2
  68. data/lib/nexmo/oas/renderer/views/open_api/_responses.erb +35 -0
  69. data/lib/nexmo/oas/renderer/views/open_api/_tabbed_parameters.erb +15 -4
  70. data/lib/nexmo/oas/renderer/views/open_api/_tabbed_single_parameter.erb +56 -0
  71. data/lib/nexmo/oas/renderer/views/open_api/_webhooks.erb +30 -0
  72. data/lib/nexmo/oas/renderer/views/open_api/show.erb +10 -115
  73. data/nexmo-oas-renderer.gemspec +26 -26
  74. metadata +59 -48
  75. data/lib/nexmo/oas/renderer/public/assets/javascripts/components/scroll.js +0 -21
  76. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/core.scss +0 -137
  77. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/navigation.scss +0 -102
  78. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/nexmo.scss +0 -61
  79. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/syntax.scss +0 -63
  80. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/typography.scss +0 -248
  81. data/lib/nexmo/oas/renderer/public/assets/stylesheets/sass/volta-templates.scss +0 -119
data/README.md CHANGED
@@ -19,6 +19,8 @@ Sinatra application that provides a preview of how the OAS documents will be ren
19
19
 
20
20
  You can run using Docker and it will serve the current directory (this will usually be the api-specification repo):
21
21
 
22
+ #### Mac/Linux
23
+
22
24
  ```bash
23
25
  docker run --rm -p 4567:4567 -v `pwd`:/definitions -e 'OAS_PATH=/definitions' nexmodev/nexmo-oas-renderer:latest
24
26
  ```
@@ -31,6 +33,12 @@ function nexmo-oas-renderer() {
31
33
  }
32
34
  ```
33
35
 
36
+ #### Windows
37
+
38
+ ```ps
39
+ docker run --rm -p 4567:4567 -v %CD%:/definitions -e 'OAS_PATH=/definitions' nexmodev/nexmo-oas-renderer:latest
40
+ ```
41
+
34
42
  ### As a standalone application
35
43
 
36
44
  Install the gem:
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
- require "bundler/gem_tasks"
2
- task :default => :spec
1
+ require 'bundler/gem_tasks'
2
+ task default: :spec
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "nexmo/oas/renderer"
3
+ require 'bundler/setup'
4
+ require 'nexmo/oas/renderer'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "nexmo/oas/renderer"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start(__FILE__)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
@@ -1,9 +1,9 @@
1
- require "nexmo/oas/renderer/version"
2
- require "nexmo/oas/renderer/app"
1
+ # frozen_string_literal: true
3
2
 
4
- if defined?(NexmoDeveloper::Application)
5
- require "nexmo/oas/engine"
6
- end
3
+ require 'nexmo/oas/renderer/version'
4
+ require 'nexmo/oas/renderer/app'
5
+
6
+ require 'nexmo/oas/engine' if defined?(NexmoDeveloper::Application)
7
7
 
8
8
  module Nexmo
9
9
  module OAS
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sinatra/base'
2
4
  require 'active_support'
3
5
  require 'active_support/core_ext/array/conversions'
@@ -5,15 +7,16 @@ require 'active_support/core_ext/string/output_safety'
5
7
  require 'active_model'
6
8
  require 'nexmo_markdown_renderer'
7
9
 
8
- require_relative'./decorators/response_parser_decorator'
9
- require_relative'./presenters/api_specification'
10
- require_relative'./presenters/open_api_specification'
11
- require_relative'./presenters/navigation'
12
- require_relative'./presenters/response_tabs'
13
- require_relative'./helpers/render'
14
- require_relative'./helpers/navigation'
15
- require_relative'./helpers/summary'
16
- require_relative'./helpers/url'
10
+ require_relative './decorators/response_parser_decorator'
11
+ require_relative './presenters/api_specification'
12
+ require_relative './presenters/open_api_specification'
13
+ require_relative './presenters/navigation'
14
+ require_relative './presenters/request_body_raw'
15
+ require_relative './presenters/response_tabs'
16
+ require_relative './helpers/render'
17
+ require_relative './helpers/navigation'
18
+ require_relative './helpers/summary'
19
+ require_relative './helpers/url'
17
20
 
18
21
  require 'dotenv/load'
19
22
 
@@ -21,17 +24,17 @@ module Nexmo
21
24
  module OAS
22
25
  module Renderer
23
26
  class API < Sinatra::Base
24
-
25
27
  Tilt.register Tilt::ERBTemplate, 'html.erb'
26
28
 
27
29
  if defined?(NexmoDeveloper::Application)
28
- view_paths = [views, NexmoDeveloper::Application.root.join("app", "views")]
30
+ view_paths = [views, NexmoDeveloper::Application.root.join('app', 'views')]
29
31
  set :views, view_paths
30
32
  end
31
33
 
32
34
  set :mustermann_opts, { type: :rails }
33
35
  set :oas_path, (ENV['OAS_PATH'] || './')
34
36
  set :bind, '0.0.0.0'
37
+ set :github_path, (proc { load_business_yaml })
35
38
 
36
39
  helpers do
37
40
  include Helpers::Render
@@ -44,9 +47,9 @@ module Nexmo
44
47
  extensions = extension.split('.')
45
48
  case extensions.size
46
49
  when 1
47
- { definition: extensions.first}
50
+ { definition: extensions.first }
48
51
  when 2
49
- if extensions.second.match? /v\d+/
52
+ if extensions.second.match?(/v\d+/)
50
53
  { definition: extensions.first, version: extensions.second }
51
54
  else
52
55
  { definition: extensions.first, format: extensions.second }
@@ -58,17 +61,30 @@ module Nexmo
58
61
  end
59
62
  end
60
63
 
61
- def check_redirect!
62
- if defined?(NexmoDeveloper::Application)
63
- redirect_path = Redirector.find(request)
64
- redirect(redirect_path) if redirect_path
64
+ def self.load_business_yaml
65
+ raise "Application requires a 'config/business_info.yml' file to be defined inside the documentation path." if defined?(NexmoDeveloper::Application) && !File.exist?("#{Rails.configuration.docs_base_path}/config/business_info.yml")
66
+
67
+ if defined?(NexmoDeveloper::Application) && File.exist?("#{Rails.configuration.docs_base_path}/config/business_info.yml")
68
+ @url ||= begin
69
+ config = YAML.load_file("#{Rails.configuration.docs_base_path}/config/business_info.yml")
70
+ config['oas_url']
71
+ end
72
+ else
73
+ 'https://www.github.com/nexmo/api-specification/blob/master/definitions'
65
74
  end
66
75
  end
67
76
 
77
+ def check_redirect!
78
+ return unless defined?(NexmoDeveloper::Application)
79
+
80
+ redirect_path = Redirector.find(request)
81
+ redirect(redirect_path) if redirect_path
82
+ end
83
+
68
84
  def check_oas_constraints!(definition)
69
- if defined?(NexmoDeveloper::Application)
70
- pass unless OpenApiConstraint.match?(definition)
71
- end
85
+ return unless defined?(NexmoDeveloper::Application)
86
+
87
+ pass unless OpenApiConstraint.match?(definition)
72
88
  end
73
89
 
74
90
  error Errno::ENOENT do
@@ -82,21 +98,44 @@ module Nexmo
82
98
 
83
99
  unless defined?(NexmoDeveloper::Application)
84
100
  get '/' do
85
- prefix = "#{API.oas_path}"
101
+ prefix = API.oas_path.to_s
86
102
  @definitions = Dir.glob("#{prefix}/**/*.yml").map do |d|
87
103
  d.gsub("#{prefix}/", '').gsub('.yml', '')
88
- end.sort.reject { |d| d.include? 'common/' }
104
+ end
105
+
106
+ @definitions = @definitions.sort.reject { |d| d.include? 'common/' }
89
107
  erb :'api/index', layout: false
90
108
  end
91
109
  end
92
110
 
93
111
  def set_code_language
94
112
  return if params[:code_language] == 'templates'
113
+
95
114
  @code_language = params[:code_language]
96
115
  end
97
116
 
117
+ def set_theme
118
+ persisted_theme = nil
119
+
120
+ if defined?(NexmoDeveloper::Application)
121
+ session[:persisted_theme] = params[:theme] if params[:theme]
122
+ persisted_theme = session[:persisted_theme]
123
+ end
124
+
125
+ @theme = params[:theme] || persisted_theme
126
+
127
+ @theme = 'light' unless %w[light dark].include?(@theme)
128
+
129
+ @theme_light = @theme == 'light'
130
+
131
+ alternate_theme = @theme == 'light' ? 'dark' : 'light'
132
+ @theme_link = "#{request.path_info}?theme=#{alternate_theme}"
133
+ @theme_link = "/api#{@theme_link}" if defined?(NexmoDeveloper::Application)
134
+ end
135
+
98
136
  before do
99
137
  set_code_language
138
+ set_theme
100
139
  end
101
140
 
102
141
  get '(/api)/*definition' do
@@ -108,30 +147,30 @@ module Nexmo
108
147
 
109
148
  @specification = Presenters::OpenApiSpecification.new(
110
149
  definition_name: definition,
111
- expand_responses: params.fetch(:expandResponses, nil),
150
+ expand_responses: params.fetch(:expandResponses, nil)
112
151
  )
113
152
 
114
- if ['yml', 'json'].include?(parameters[:format])
115
- send_file @specification.definition.path, disposition: :attachment
153
+ if %w[yml json].include?(parameters[:format])
154
+ next send_file @specification.definition.path, disposition: :attachment
155
+ end
156
+
157
+ if defined?(NexmoDeveloper::Application)
158
+ erb :'open_api/show', layout: :'layouts/open-api.html'
116
159
  else
117
- if defined?(NexmoDeveloper::Application)
118
- erb :'open_api/show', layout: :'layouts/page-full.html'
119
- else
120
- erb :'open_api/show', layout: :'layouts/open_api'
121
- end
160
+ erb :'open_api/show', layout: :'layouts/open_api'
122
161
  end
123
162
  end
124
163
 
125
164
  def set_document
126
- if params[:code_language] == 'templates'
127
- @document = 'verify/templates'
128
- elsif params[:code_language] == 'ncco'
129
- @document = 'voice/ncco'
130
- elsif ::Nexmo::Markdown::CodeLanguage.exists?(params[:code_language])
131
- @document = params[:document]
132
- else
133
- @document = "#{params[:document]}/#{params[:code_language]}"
134
- end
165
+ @document = if params[:code_language] == 'templates'
166
+ 'verify/templates'
167
+ elsif params[:code_language] == 'ncco'
168
+ 'voice/ncco'
169
+ elsif ::Nexmo::Markdown::CodeLanguage.exists?(params[:code_language])
170
+ params[:document]
171
+ else
172
+ "#{params[:document]}/#{params[:code_language]}"
173
+ end
135
174
  end
136
175
 
137
176
  get '(/api)/*document(/:code_language)' do
@@ -144,7 +183,7 @@ module Nexmo
144
183
 
145
184
  @navigation = Presenters::Navigation.new(
146
185
  content: @specification.content,
147
- title: @specification.side_navigation_title,
186
+ title: @specification.side_navigation_title
148
187
  )
149
188
 
150
189
  if defined?(NexmoDeveloper::Application)
@@ -1,5 +1,7 @@
1
- require_relative "./app"
2
- require "sass/plugin/rack"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './app'
4
+ require 'sass/plugin/rack'
3
5
 
4
6
  Sass::Plugin.options[:style] = :compressed
5
7
  use Sass::Plugin::Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rouge'
2
4
  require 'neatjson'
3
5
  require_relative '../services/oas_parser'
@@ -8,43 +10,41 @@ module Nexmo
8
10
  class ResponseParserDecorator < ::OasParser::ResponseParser
9
11
  def formatted_json
10
12
  JSON.neat_generate(parse, {
11
- wrap: true,
12
- after_colon: 1,
13
- })
13
+ wrap: true,
14
+ after_colon: 1,
15
+ })
14
16
  end
15
17
 
16
18
  def formatted_xml(xml_options = {})
17
19
  xml_options[:root] = xml_options['name'] if xml_options
18
20
  xml_string = xml(xml_options)
19
- xml_string.gsub!(%r{^(\s+?)(<(?:\w|\=|\"|\_|\s)+?\>)(.+?)(</.+?>)}).each do |s|
20
- indentation = $1
21
- indentation_plus_one = "#{$1} "
22
- opening_tag = $2
23
- content = $3
24
- closing_tag = $4
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)
25
27
 
26
28
  next(s) if (indentation.size + opening_tag.size + content.size) < 60
27
29
 
28
30
  next "#{indentation}#{opening_tag}\n#{indentation_plus_one}#{content}\n#{indentation}#{closing_tag}"
29
31
  end
30
32
 
31
- xml_string
33
+ xml_string.gsub('<', '&lt;')
32
34
  end
33
35
 
34
- def html(format = 'application/json', xml_options: nil)
35
- formatter = Rouge::Formatters::HTML.new
36
-
36
+ def html(format = 'application/json', xml_options: nil, theme_light: nil)
37
37
  case format
38
38
  when 'application/json'
39
- lexer = Rouge::Lexer.find('json')
40
- highlighted_response = formatter.format(lexer.lex(formatted_json))
39
+ language = 'json'
40
+ response = formatted_json
41
41
  when 'text/xml', 'application/xml'
42
- lexer = Rouge::Lexer.find('xml')
43
- highlighted_response = formatter.format(lexer.lex(formatted_xml(xml_options)))
42
+ language = 'xml'
43
+ response = formatted_xml(xml_options)
44
44
  end
45
45
 
46
46
  output = <<~HEREDOC
47
- <pre class="language-#{lexer && lexer.tag || 'json'} Vlt-prism--dark Vlt-prism--copy-disabled"><code>#{highlighted_response}</code></pre>
47
+ <pre class="pre-wrap language-#{language} #{theme_light ? 'Vlt-prism--dark' : ''} Vlt-prism--copy-disabled"><code>#{response}</code></pre>
48
48
  HEREDOC
49
49
 
50
50
  output
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
4
6
  module Helpers
5
7
  module Navigation
6
-
7
8
  HEADING_TAG_DEPTHS = {
8
9
  'h0' => 0,
9
10
  'h1' => 1,
@@ -58,7 +59,6 @@ module Nexmo
58
59
  def build_document(content)
59
60
  Nokogiri::HTML::DocumentFragment.parse(content)
60
61
  end
61
-
62
62
  end
63
63
  end
64
64
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
4
6
  module Helpers
5
7
  module Render
6
-
7
8
  def find_template(views, name, engine, &block)
8
9
  Array(views).each do |v|
9
10
  super(v, name, engine, &block)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
@@ -17,9 +19,10 @@ module Nexmo
17
19
  operation_id = operation_id.gsub(/(_|-)/, ' ').titleize
18
20
 
19
21
  # Some terms need to be capitalised all the time
20
- uppercase_array = ['SMS', 'DTMF']
22
+ uppercase_array = %w[SMS DTMF]
21
23
  operation_id.split(' ').map do |c|
22
24
  next c.upcase if uppercase_array.include?(c.upcase)
25
+
23
26
  c
24
27
  end.join(' ')
25
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
4
6
  module Presenters
5
7
  class ApiSpecification
6
-
7
8
  def initialize(document_name:, code_language: nil)
8
9
  @document_name = document_name
9
10
  @code_language = code_language
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative './response_format'
2
4
 
3
5
  module Nexmo
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nexmo
2
4
  module OAS
3
5
  module Renderer
@@ -12,6 +14,7 @@ module Nexmo
12
14
  # For now we only use the first tag in the list as an equivalent for the old x-group functionality
13
15
  @groups = @definition.endpoints.group_by do |endpoint|
14
16
  next nil unless tags
17
+
15
18
  endpoint.raw['tags']&.first
16
19
  end
17
20
 
@@ -25,6 +28,7 @@ module Nexmo
25
28
  # Sort by the order in which they're defined in the definition
26
29
  @groups = @groups.sort_by do |name, _|
27
30
  next -1 if name.nil?
31
+
28
32
  ordering[name.capitalize] || 999
29
33
  end
30
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../helpers/navigation'
2
4
 
3
5
  module Nexmo