nexmo-oas-renderer 0.11.0 → 2.0.1

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 +12 -0
  5. data/Gemfile +2 -2
  6. data/Gemfile.lock +92 -72
  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 +71 -46
  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 +61 -49
  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,18 +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'
35
- set :github_path, Proc.new { load_business_yaml }
37
+ set :github_path, (proc { load_business_yaml })
36
38
 
37
39
  helpers do
38
40
  include Helpers::Render
@@ -45,9 +47,9 @@ module Nexmo
45
47
  extensions = extension.split('.')
46
48
  case extensions.size
47
49
  when 1
48
- { definition: extensions.first}
50
+ { definition: extensions.first }
49
51
  when 2
50
- if extensions.second.match? /v\d+/
52
+ if extensions.second.match?(/v\d+/)
51
53
  { definition: extensions.first, version: extensions.second }
52
54
  else
53
55
  { definition: extensions.first, format: extensions.second }
@@ -60,29 +62,29 @@ module Nexmo
60
62
  end
61
63
 
62
64
  def self.load_business_yaml
63
- if defined?(NexmoDeveloper::Application) && !File.exist?("#{Rails.configuration.docs_base_path}/config/business_info.yml")
64
- raise "Application requires a 'config/business_info.yml' file to be defined inside the documentation path."
65
- elsif defined?(NexmoDeveloper::Application) && File.exist?("#{Rails.configuration.docs_base_path}/config/business_info.yml")
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")
66
68
  @url ||= begin
67
69
  config = YAML.load_file("#{Rails.configuration.docs_base_path}/config/business_info.yml")
68
- "https://www.github.com/#{config['oas_repo']}/blob/master/definitions"
70
+ config['oas_url']
69
71
  end
70
72
  else
71
- "https://www.github.com/nexmo/api-specification/blob/master/definitions"
73
+ 'https://www.github.com/nexmo/api-specification/blob/master/definitions'
72
74
  end
73
75
  end
74
76
 
75
77
  def check_redirect!
76
- if defined?(NexmoDeveloper::Application)
77
- redirect_path = Redirector.find(request)
78
- redirect(redirect_path) if redirect_path
79
- end
78
+ return unless defined?(NexmoDeveloper::Application)
79
+
80
+ redirect_path = Redirector.find(request)
81
+ redirect(redirect_path) if redirect_path
80
82
  end
81
83
 
82
84
  def check_oas_constraints!(definition)
83
- if defined?(NexmoDeveloper::Application)
84
- pass unless OpenApiConstraint.match?(definition)
85
- end
85
+ return unless defined?(NexmoDeveloper::Application)
86
+
87
+ pass unless OpenApiConstraint.match?(definition)
86
88
  end
87
89
 
88
90
  error Errno::ENOENT do
@@ -96,21 +98,44 @@ module Nexmo
96
98
 
97
99
  unless defined?(NexmoDeveloper::Application)
98
100
  get '/' do
99
- prefix = "#{API.oas_path}"
101
+ prefix = API.oas_path.to_s
100
102
  @definitions = Dir.glob("#{prefix}/**/*.yml").map do |d|
101
103
  d.gsub("#{prefix}/", '').gsub('.yml', '')
102
- end.sort.reject { |d| d.include? 'common/' }
104
+ end
105
+
106
+ @definitions = @definitions.sort.reject { |d| d.include? 'common/' }
103
107
  erb :'api/index', layout: false
104
108
  end
105
109
  end
106
110
 
107
111
  def set_code_language
108
112
  return if params[:code_language] == 'templates'
113
+
109
114
  @code_language = params[:code_language]
110
115
  end
111
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
+
112
136
  before do
113
137
  set_code_language
138
+ set_theme
114
139
  end
115
140
 
116
141
  get '(/api)/*definition' do
@@ -122,30 +147,30 @@ module Nexmo
122
147
 
123
148
  @specification = Presenters::OpenApiSpecification.new(
124
149
  definition_name: definition,
125
- expand_responses: params.fetch(:expandResponses, nil),
150
+ expand_responses: params.fetch(:expandResponses, nil)
126
151
  )
127
152
 
128
- if ['yml', 'json'].include?(parameters[:format])
129
- 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'
130
159
  else
131
- if defined?(NexmoDeveloper::Application)
132
- erb :'open_api/show', layout: :'layouts/page-full.html'
133
- else
134
- erb :'open_api/show', layout: :'layouts/open_api'
135
- end
160
+ erb :'open_api/show', layout: :'layouts/open_api'
136
161
  end
137
162
  end
138
163
 
139
164
  def set_document
140
- if params[:code_language] == 'templates'
141
- @document = 'verify/templates'
142
- elsif params[:code_language] == 'ncco'
143
- @document = 'voice/ncco'
144
- elsif ::Nexmo::Markdown::CodeLanguage.exists?(params[:code_language])
145
- @document = params[:document]
146
- else
147
- @document = "#{params[:document]}/#{params[:code_language]}"
148
- 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
149
174
  end
150
175
 
151
176
  get '(/api)/*document(/:code_language)' do
@@ -158,7 +183,7 @@ module Nexmo
158
183
 
159
184
  @navigation = Presenters::Navigation.new(
160
185
  content: @specification.content,
161
- title: @specification.side_navigation_title,
186
+ title: @specification.side_navigation_title
162
187
  )
163
188
 
164
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