MuranoCLI 3.0.1 → 3.0.2

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.agignore +1 -0
  3. data/.rubocop.yml +67 -5
  4. data/Gemfile +6 -3
  5. data/MuranoCLI.gemspec +14 -10
  6. data/README.markdown +299 -126
  7. data/Rakefile +6 -1
  8. data/bin/murano +2 -2
  9. data/docs/completions/murano_completion-bash +93 -0
  10. data/lib/MrMurano.rb +19 -2
  11. data/lib/MrMurano/Business.rb +22 -19
  12. data/lib/MrMurano/Config.rb +19 -9
  13. data/lib/MrMurano/Content.rb +4 -4
  14. data/lib/MrMurano/Exchange-Element.rb +99 -0
  15. data/lib/MrMurano/Exchange.rb +137 -0
  16. data/lib/MrMurano/Gateway.rb +9 -9
  17. data/lib/MrMurano/Keystore.rb +4 -2
  18. data/lib/MrMurano/ReCommander.rb +3 -5
  19. data/lib/MrMurano/Solution-ServiceConfig.rb +12 -12
  20. data/lib/MrMurano/Solution-Services.rb +15 -14
  21. data/lib/MrMurano/Solution-Users.rb +2 -2
  22. data/lib/MrMurano/Solution.rb +43 -49
  23. data/lib/MrMurano/SolutionId.rb +28 -28
  24. data/lib/MrMurano/SyncUpDown.rb +32 -22
  25. data/lib/MrMurano/Webservice-Endpoint.rb +2 -1
  26. data/lib/MrMurano/Webservice.rb +5 -5
  27. data/lib/MrMurano/commands.rb +2 -1
  28. data/lib/MrMurano/commands/business.rb +21 -19
  29. data/lib/MrMurano/commands/domain.rb +16 -2
  30. data/lib/MrMurano/commands/exchange.rb +272 -0
  31. data/lib/MrMurano/commands/globals.rb +17 -1
  32. data/lib/MrMurano/commands/init.rb +3 -3
  33. data/lib/MrMurano/commands/link.rb +16 -16
  34. data/lib/MrMurano/commands/postgresql.rb +2 -2
  35. data/lib/MrMurano/commands/show.rb +13 -7
  36. data/lib/MrMurano/commands/solution.rb +23 -17
  37. data/lib/MrMurano/commands/solution_picker.rb +49 -44
  38. data/lib/MrMurano/commands/sync.rb +2 -1
  39. data/lib/MrMurano/commands/timeseries.rb +2 -2
  40. data/lib/MrMurano/commands/tsdb.rb +2 -2
  41. data/lib/MrMurano/hash.rb +19 -7
  42. data/lib/MrMurano/http.rb +12 -2
  43. data/lib/MrMurano/orderedhash.rb +200 -0
  44. data/lib/MrMurano/spec_commander.rb +98 -0
  45. data/lib/MrMurano/verbosing.rb +2 -2
  46. data/lib/MrMurano/version.rb +2 -2
  47. data/spec/Business_spec.rb +8 -6
  48. data/spec/Solution-ServiceConfig_spec.rb +1 -1
  49. data/spec/SyncUpDown_spec.rb +6 -6
  50. data/spec/_workspace.rb +9 -4
  51. data/spec/cmd_business_spec.rb +8 -2
  52. data/spec/cmd_common.rb +266 -25
  53. data/spec/cmd_exchange_spec.rb +118 -0
  54. data/spec/cmd_help_spec.rb +54 -13
  55. data/spec/cmd_init_spec.rb +1 -12
  56. data/spec/cmd_link_spec.rb +94 -72
  57. data/spec/spec_helper.rb +11 -16
  58. metadata +23 -17
@@ -0,0 +1,137 @@
1
+ # Last Modified: 2017.08.31 /coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2016-2017 Exosite LLC.
5
+ # License: MIT. See LICENSE.txt.
6
+ # vim:tw=0:ts=2:sw=2:et:ai
7
+
8
+ require 'MrMurano/Exchange-Element'
9
+
10
+ module MrMurano
11
+ # The Exchange class represents an end user's Murano IoT Exchange Elements.
12
+ class Exchange < Business
13
+ include Http
14
+ include Verbose
15
+
16
+ def get(path='', query=nil, &block)
17
+ super
18
+ end
19
+
20
+ def element(element_id)
21
+ ret = get('exchange/' + bid + '/element/' + element_id)
22
+ return nil unless ret.is_a?(Hash) && !ret.key?(:error)
23
+ ret
24
+ end
25
+
26
+ def fetch_type(part)
27
+ whirly_start('Fetching Elements...')
28
+ ret = get('exchange/' + bid + part) do |request, http|
29
+ response = http.request(request)
30
+ case response
31
+ when Net::HTTPSuccess
32
+ workit_response(response)
33
+ else
34
+ showHttpError(request, response)
35
+ end
36
+ end
37
+ whirly_stop
38
+ return [] unless ret.is_a?(Hash) && !ret.key?(:error)
39
+ return [] unless ret.key?(:items)
40
+ unless ret[:count] == ret[:items].length
41
+ warning(
42
+ 'Unexpected: ret[:count] != ret[:items].length: ' \
43
+ "#{ret[:count]} != #{ret[:items].length}"
44
+ )
45
+ end
46
+ ret[:items]
47
+ end
48
+
49
+ def elements(**opts)
50
+ lookp = {}
51
+ # Get the user's Business metadata, including their Business tier.
52
+ overview if @ometa.nil?
53
+ # Fetch the list of Elements, including Added, Available, and Upgradeable.
54
+ items = fetch_type('/element/')
55
+ # Prepare a lookup of the Elements.
56
+ elems = items.map do |meta|
57
+ elem = MrMurano::ExchangeElement.new(meta)
58
+ lookp[elem.elementId] = elem
59
+ elem
60
+ end
61
+ # Fetch the list of Purchased elements.
62
+ items = fetch_type('/purchase/')
63
+ # Update the list of all Elements to indicate which have been purchased.
64
+ items.each do |meta|
65
+ elem = lookp[meta[:elementId]]
66
+ if !elem.nil?
67
+ elem.purchaseId = meta[:purchaseId]
68
+ # Sanity check.
69
+ meta[:element].each do |key, val|
70
+ next if elem.send(key) == val
71
+ warning(
72
+ 'Unexpected: Exchange Purchase element meta differs: ' \
73
+ "key: #{key} / elem: #{elem.send(key)} / purchase: #{val}"
74
+ )
75
+ end
76
+ else
77
+ warning("Unexpected: No Element found for Exchange Purchase: elementId: #{meta[:elementId]}")
78
+ end
79
+ end
80
+ prepare_elements(elems, **opts)
81
+ end
82
+
83
+ def prepare_elements(elems, filter_id: nil, filter_name: nil, filter_fuzzy: nil)
84
+ if filter_id || filter_name || filter_fuzzy
85
+ elems.select! do |elem|
86
+ if (
87
+ (filter_id && elem.elementId == filter_id) || \
88
+ (filter_name && elem.name == filter_name) || \
89
+ (filter_fuzzy &&
90
+ (
91
+ elem.elementId =~ /#{Regexp.escape(filter_fuzzy)}/i || \
92
+ elem.name =~ /#{Regexp.escape(filter_fuzzy)}/i
93
+ )
94
+ )
95
+ )
96
+ true
97
+ else
98
+ false
99
+ end
100
+ end
101
+ end
102
+
103
+ available = []
104
+ purchased = []
105
+ elems.sort_by(&:name)
106
+ elems.each do |elem|
107
+ if elem.purchaseId.nil?
108
+ available.push(elem)
109
+ if !@ometa[:tier].nil? && elem.tiers.include?(@ometa[:tier][:id])
110
+ elem.statusable = :available
111
+ else
112
+ elem.statusable = :upgrade
113
+ end
114
+ else
115
+ purchased.push(elem)
116
+ elem.statusable = :added
117
+ end
118
+ #@ometa[:status] = elem.status
119
+ end
120
+
121
+ [elems, available, purchased]
122
+ end
123
+
124
+ def purchase(element_id)
125
+ whirly_start('Purchasing Element...')
126
+ ret = post(
127
+ 'exchange/' + bid + '/purchase/',
128
+ elementId: element_id,
129
+ )
130
+ # Returns, e.g.,
131
+ # { bizid: "XXX", elementId: "YYY", purchaseId: "ZZZ" }
132
+ whirly_stop
133
+ ret
134
+ end
135
+ end
136
+ end
137
+
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.23 /coding: utf-8
1
+ # Last Modified: 2017.09.11 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -29,10 +29,10 @@ module MrMurano
29
29
 
30
30
  def initialize
31
31
  @solntype = 'product.id'
32
- @uriparts_sidex = 1
33
- init_sid!
34
- @uriparts = [:service, @sid, :device2]
35
- @uriparts_sidex = 1
32
+ @uriparts_apidex = 1
33
+ init_api_id!
34
+ @uriparts = [:service, @api_id, :device2]
35
+ @uriparts_apidex = 1
36
36
  @itemkey = :id
37
37
  end
38
38
 
@@ -195,7 +195,7 @@ module MrMurano
195
195
  @here = locallist
196
196
  end
197
197
 
198
- def download(_local, item)
198
+ def download(_local, item, _options=nil)
199
199
  # Not calling, e.g., `return unless download_item_allowed(item[@itemkey])`
200
200
  # See instead: syncup_after and syncdown_after/resources_write
201
201
  @here = locallist if @here.nil?
@@ -463,7 +463,7 @@ module MrMurano
463
463
  raise "Gateway info not found for #{identifier}" if info.nil?
464
464
  fqdn = info[:fqdn]
465
465
  debug "Found FQDN: #{fqdn}"
466
- fqdn = "#{@sid}.m2.exosite.io" if fqdn.nil?
466
+ fqdn = "#{@api_id}.m2.exosite.io" if fqdn.nil?
467
467
 
468
468
  uri = URI("https://#{fqdn}/provision/activate")
469
469
  http = Net::HTTP.new(uri.host, uri.port)
@@ -471,8 +471,8 @@ module MrMurano
471
471
  http.start
472
472
  request = Net::HTTP::Post.new(uri)
473
473
  request.form_data = {
474
- vendor: @sid,
475
- model: @sid,
474
+ vendor: @api_id,
475
+ model: @api_id,
476
476
  sn: identifier,
477
477
  }
478
478
  request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
@@ -1,13 +1,15 @@
1
- # Last Modified: 2017.08.16 /coding: utf-8
1
+ # Last Modified: 2017.09.11 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
5
5
  # License: MIT. See LICENSE.txt.
6
6
  # vim:tw=0:ts=2:sw=2:et:ai
7
7
 
8
+ require 'MrMurano/Solution-ServiceConfig'
9
+
8
10
  module MrMurano
9
11
  class Keystore < ServiceConfig
10
- def initialize(sid=nil)
12
+ def initialize(api_id=nil)
11
13
  # FIXME/2017-07-03: Do products have a keystore service? What about other soln types?
12
14
  @solntype = 'application.id'
13
15
  super
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.23 /coding: utf-8
1
+ # Last Modified: 2017.08.29 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -77,7 +77,7 @@ module Commander
77
77
  # We override -- monkey patch -- it to do other stuff.
78
78
  alias old_run_active_command run_active_command
79
79
  def run_active_command
80
- exit @command_exit if @command_exit
80
+ exit @command_exit if defined?(@command_exit) && @command_exit
81
81
  section = active_command.name
82
82
  hooked = MrMurano::Hooked.new(section)
83
83
  hooked.check_run_pre_hook
@@ -227,9 +227,7 @@ module Commander
227
227
  opt[:switches].map { |sw| MrMurano::Verbose.fancy_ticks(sw) }.join('|')
228
228
  end
229
229
  match = match.flatten
230
- if match.length > 1
231
- match[-1] = "and #{match[-1]}"
232
- end
230
+ match[-1] = "and #{match[-1]}" if match.length > 1
233
231
  match = match.join(', ')
234
232
  MrMurano::Verbose.error("Ambiguous option: #{ambig} matches: #{match}")
235
233
  exit 2
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.22 /coding: utf-8
1
+ # Last Modified: 2017.09.11 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -10,7 +10,7 @@ require 'MrMurano/Solution'
10
10
  module MrMurano
11
11
  # …/serviceconfig
12
12
  class ServiceConfig < SolutionBase
13
- def initialize(sid=nil)
13
+ def initialize(api_id=nil)
14
14
  super
15
15
  @uriparts << :serviceconfig
16
16
  @scid = nil
@@ -58,7 +58,7 @@ module MrMurano
58
58
  post(
59
59
  '/',
60
60
  {
61
- solution_id: @sid,
61
+ solution_id: @api_id,
62
62
  service: pid,
63
63
  # 2017-06-26: "name" seems to work, but "script_key" is what web UI uses.
64
64
  # See yeti-ui/bridge/src/js/api/services.js::linkApplicationService
@@ -112,20 +112,20 @@ module MrMurano
112
112
  # A much better UI/UX happens with human intervention.
113
113
  # :nocov:
114
114
  class Services < SolutionBase
115
- def initialize(sid=nil)
115
+ def initialize(api_id=nil)
116
116
  super
117
117
  @uriparts << :service
118
118
  end
119
119
 
120
- def sid_for_name(name)
120
+ def api_id_for_name(name)
121
121
  name = name.to_s unless name.is_a? String
122
122
  scr = list.select { |i| i[:alias] == name }.first
123
123
  scr[:id]
124
124
  end
125
125
 
126
- def sid
127
- return @sid unless @sid.nil?
128
- @sid = sid_for_name(@service_name)
126
+ def api_id
127
+ return @api_id unless @api_id.nil?
128
+ @api_id = api_id_for_name(@service_name)
129
129
  end
130
130
 
131
131
  def list
@@ -137,13 +137,13 @@ module MrMurano
137
137
  # sort_by_name(ret[:items])
138
138
  end
139
139
 
140
- def schema(id=sid)
140
+ def schema(id=api_id)
141
141
  # TODO: cache schema in user dir?
142
142
  get("/#{id}/schema")
143
143
  end
144
144
 
145
145
  ## Get list of call operations from a schema
146
- def callable(id=sid, all=false)
146
+ def callable(id=api_id, all=false)
147
147
  scm = schema(id)
148
148
  calls = []
149
149
  scm[:paths].each do |path, methods|
@@ -174,14 +174,14 @@ module MrMurano
174
174
  # :nocov:
175
175
 
176
176
  class ServiceConfigApplication < ServiceConfig
177
- def initialize(sid=nil)
177
+ def initialize(api_id=nil)
178
178
  @solntype = 'application.id'
179
179
  super
180
180
  end
181
181
  end
182
182
 
183
183
  class ServiceConfigProduct < ServiceConfig
184
- def initialize(sid=nil)
184
+ def initialize(api_id=nil)
185
185
  @solntype = 'product.id'
186
186
  super
187
187
  end
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.23 /coding: utf-8
1
+ # Last Modified: 2017.09.11 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -21,7 +21,7 @@ module MrMurano
21
21
  ##
22
22
  # Things that servers do that is common.
23
23
  class ServiceBase < SolutionBase
24
- def initialize(sid=nil)
24
+ def initialize(api_id=nil)
25
25
  super
26
26
  end
27
27
 
@@ -67,6 +67,7 @@ module MrMurano
67
67
  else
68
68
  # I.e., thereitem.phantom, an "undeletable" file that does not
69
69
  # exist locally but should not be deleted from server.
70
+ # MAYBE/2017-09-07: Honor options.ignore_errors here?
70
71
  raise 'no file' unless thereitem.script
71
72
  script = thereitem.script
72
73
  end
@@ -77,7 +78,7 @@ module MrMurano
77
78
  name = mkname(thereitem)
78
79
  pst = thereitem.to_h.merge(
79
80
  #solution_id: $cfg[@solntype],
80
- solution_id: @sid,
81
+ solution_id: @api_id,
81
82
  script: script,
82
83
  alias: mkalias(thereitem),
83
84
  name: name,
@@ -174,7 +175,7 @@ module MrMurano
174
175
  [
175
176
  'cache',
176
177
  self.class.to_s.gsub(/\W+/, '_'),
177
- @sid,
178
+ @api_id,
178
179
  'yaml',
179
180
  ].join('.')
180
181
  end
@@ -258,7 +259,7 @@ module MrMurano
258
259
  attr_accessor :solution_id
259
260
  end
260
261
 
261
- def initialize(sid=nil)
262
+ def initialize(api_id=nil)
262
263
  @solntype = 'application.id'
263
264
  super
264
265
  @uriparts << :module
@@ -283,7 +284,7 @@ module MrMurano
283
284
  def mkalias(remote)
284
285
  raise "Missing parts! #{remote.to_h.to_json}" if remote.name.nil?
285
286
  #[$cfg[@solntype], remote[:name]].join('_')
286
- [@sid, remote[:name]].join('_')
287
+ [@api_id, remote[:name]].join('_')
287
288
  end
288
289
 
289
290
  def mkname(remote)
@@ -349,7 +350,7 @@ module MrMurano
349
350
  attr_accessor :updated_at
350
351
  # @return [String] Timestamp when this was created.
351
352
  attr_accessor :created_at
352
- # @return [String] The soln's product.id or application.id (Murano's apiId).
353
+ # @return [String] The soln's product.id or application.id (Murano's api_id).
353
354
  attr_accessor :solution_id
354
355
  # @return [String] Which service triggers this script.
355
356
  attr_accessor :service
@@ -366,7 +367,7 @@ module MrMurano
366
367
  attr_accessor :phantom
367
368
  end
368
369
 
369
- def initialize(sid=nil)
370
+ def initialize(api_id=nil)
370
371
  super
371
372
  @uriparts << :eventhandler
372
373
  @itemkey = :alias
@@ -378,7 +379,7 @@ module MrMurano
378
379
  def mkalias(remote)
379
380
  raise "Missing parts! #{remote.to_h.to_json}" if remote.service.nil? || remote.event.nil?
380
381
  #[$cfg[@solntype], remote[:service], remote[:event]].join('_')
381
- [@sid, remote[:service], remote[:event]].join('_')
382
+ [@api_id, remote[:service], remote[:event]].join('_')
382
383
  end
383
384
 
384
385
  def mkname(remote)
@@ -482,12 +483,12 @@ module MrMurano
482
483
  end
483
484
  end
484
485
 
485
- def default_event_script(service_or_sid, &block)
486
+ def default_event_script(service_or_api_id, &block)
486
487
  post(
487
488
  '/',
488
489
  {
489
- solution_id: @sid,
490
- service: service_or_sid,
490
+ solution_id: @api_id,
491
+ service: service_or_api_id,
491
492
  event: 'event',
492
493
  script: 'print(event)',
493
494
  },
@@ -712,7 +713,7 @@ module MrMurano
712
713
  PRODUCT_SERVICES = %w[device2 interface].freeze
713
714
 
714
715
  class EventHandlerSolnPrd < EventHandler
715
- def initialize(sid=nil)
716
+ def initialize(api_id=nil)
716
717
  @solntype = 'product.id'
717
718
  # FIXME/2017-06-20: Should we use separate directories for prod vs app?
718
719
  # See also :services in PrfFile and elsewhere;
@@ -748,7 +749,7 @@ module MrMurano
748
749
  end
749
750
 
750
751
  class EventHandlerSolnApp < EventHandler
751
- def initialize(sid=nil)
752
+ def initialize(api_id=nil)
752
753
  @solntype = 'application.id'
753
754
  # FIXME/2017-06-20: Should we use separate directories for prod vs app?
754
755
  @project_section = :services
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.22 /coding: utf-8
1
+ # Last Modified: 2017.09.07 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -56,7 +56,7 @@ module MrMurano
56
56
  post('/', remote)
57
57
  end
58
58
 
59
- def download(local, item)
59
+ def download(local, item, _options=nil)
60
60
  # needs to append/merge with file
61
61
  # for now, we'll read, modify, write
62
62
  here = []
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.23 /coding: utf-8
1
+ # Last Modified: 2017.09.11 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -20,19 +20,19 @@ module MrMurano
20
20
  include SolutionId
21
21
 
22
22
  def initialize(from=nil)
23
- @uriparts_sidex = 1
23
+ @uriparts_apidex = 1
24
24
  # Introspection. Feels hacky.
25
25
  if from.is_a? MrMurano::Solution
26
- init_sid!(from.sid)
27
- @valid_sid = from.valid_sid
26
+ init_api_id!(from.api_id)
27
+ @valid_api_id = from.valid_api_id
28
28
  # We shouldn't need to worry about other things...
29
29
  #@token = from.token
30
30
  #@http = from.http
31
31
  #@json_opts = from.json_opts
32
32
  else
33
- init_sid!(from)
33
+ init_api_id!(from)
34
34
  end
35
- @uriparts = [:solution, @sid]
35
+ @uriparts = [:solution, @api_id]
36
36
  @itemkey = :id
37
37
  @project_section = nil unless defined?(@project_section)
38
38
  end
@@ -44,7 +44,7 @@ module MrMurano
44
44
  protected
45
45
 
46
46
  def state
47
- [@sid, @valid_sid, @uriparts, @solntype, @itemkey, @project_section]
47
+ [@api_id, @valid_api_id, @sid, @uriparts, @solntype, @itemkey, @project_section]
48
48
  end
49
49
 
50
50
  public
@@ -69,7 +69,7 @@ module MrMurano
69
69
  while remaining != 0
70
70
  ret = super
71
71
  if ret.nil? && !@suppress_error
72
- warning "No solution with ID: #{@sid}"
72
+ warning "No solution with ID: #{@api_id}"
73
73
  whirly_interject { say 'Run `murano show` to see the business and list of solutions.' }
74
74
  MrMurano::SolutionBase.warn_configfile_env_maybe
75
75
  exit 1
@@ -146,11 +146,10 @@ module MrMurano
146
146
  end
147
147
 
148
148
  class Solution < SolutionBase
149
- def initialize(sid=nil)
150
- # Does it matter if we use :sid or :apiId?
151
- meta = sid if sid.is_a?(Hash)
152
- sid = sid[:sid] if sid.is_a?(Hash)
153
- super(sid)
149
+ def initialize(api_id=nil)
150
+ meta = api_id if api_id.is_a?(Hash)
151
+ api_id = api_id[:api_id] || api_id[:apiId] if api_id.is_a?(Hash)
152
+ super(api_id)
154
153
  set_name
155
154
  @meta = {}
156
155
  @valid = false
@@ -189,10 +188,10 @@ module MrMurano
189
188
  resp = get
190
189
  if resp.is_a?(Hash) && !resp.key?(:error)
191
190
  self.meta = resp
192
- @valid_sid = true
191
+ @valid_api_id = true
193
192
  else
194
193
  self.meta = {}
195
- @valid_sid = false
194
+ @valid_api_id = false
196
195
  end
197
196
  @suppress_error = false
198
197
  end
@@ -230,24 +229,22 @@ module MrMurano
230
229
  @meta = data
231
230
  # Verify the solution ID.
232
231
  # NOTE: The Solution info fetched from business/<bizid>/solutions endpoint
233
- # includes the keys, :name, :sid, and :domain (see calls to solutions()).
234
- # The solution details fetched from a call to Solution.get() include the
235
- # keys, :name, :id, and :domain, among others.
232
+ # includes the keys, :name, :api_id, :sid, and :domain (see calls to
233
+ # solutions()). The solution details fetched from a call to Solution.get()
234
+ # include the keys, :name, :id, and :domain, among others.
236
235
  # Note that the info() response does not include :type.
237
- # Also, :apiId is indicated by solutions(), too.
238
- sid = @meta[:sid] || @meta[:id] || nil
239
- unless @meta[:apiId].to_s.empty?
240
- if @meta[:apiId] != @meta[:sid]
241
- warning "Unexpected: apiId != sid: #{@meta[:apiId]} != #{@meta[:sid]}"
242
- end
243
- end
244
- unless @sid.to_s.empty? || sid.to_s == @sid.to_s
236
+ api_id = @meta[:apiId] || @meta[:id]
237
+ unless @api_id.to_s.empty? || api_id.to_s.empty? || api_id.to_s == @api_id.to_s
245
238
  warning(
246
- "#{type_name} ID mismatch. Server says #{fancy_ticks(sid)}, " \
247
- "but config says #{fancy_ticks(@sid)}."
239
+ "#{type_name} ID mismatch. Server says #{fancy_ticks(api_id)}, " \
240
+ "but config says #{fancy_ticks(@api_id)}."
248
241
  )
249
242
  end
250
- self.sid = sid
243
+ self.api_id = api_id
244
+ # NOTE: In Murano 1.0 (pre-ADC), api_id != sid; in Murano 1.1, they're ==.
245
+ # The sid is used in business/<bid>/solution/<sid>
246
+ # The apiId is used in solution/<apiId>
247
+ @sid = @meta[:sid]
251
248
  # Verify/set the name.
252
249
  unless @name.to_s.empty? || @meta[:name].to_s == @name.to_s
253
250
  warning(
@@ -256,21 +253,18 @@ module MrMurano
256
253
  )
257
254
  end
258
255
  if !@meta[:name].to_s.empty?
259
- set_name(@meta[:name])
260
- unless @valid_name || type == :solution
261
- warning(
262
- "Unexpected: Server returned invalid name: #{fancy_ticks(@meta[:name])}"
263
- )
256
+ # NOTE: Pre-ADC (a/k/a migrated) applications are not named, at least
257
+ # when you query the business/<bid>/solution/ endpoint. But when you
258
+ # call info_safe, which GETs the solution details, the name is
259
+ # the domain and contains dots, which is considered an illegal name!
260
+ if @meta[:name] != @meta[:domain]
261
+ set_name(@meta[:name])
262
+ unless @valid_name || type == :solution
263
+ warning(
264
+ "Unexpected: Server returned invalid name: #{fancy_ticks(@meta[:name])}"
265
+ )
266
+ end
264
267
  end
265
- elsif @meta[:domain]
266
- # This could be a pre-ADC/pre-Murano business.
267
- warning(
268
- "Unexpected: Server returned no name for domain: #{fancy_ticks(@meta[:domain])}"
269
- )
270
- else
271
- warning(
272
- "Unexpected: Server returned no name for solution: #{fancy_ticks(@meta)}"
273
- )
274
268
  end
275
269
  end
276
270
 
@@ -283,9 +277,9 @@ module MrMurano
283
277
  # classes should not be formatting output), but this seems okay.
284
278
  desc = ''
285
279
  desc += "#{type.to_s.capitalize}: " if add_type
286
- name = self.name || '~Unnamed~'
287
- sid = self.sid || '~No-ID~'
288
- desc += "#{Rainbow(name).underline} <#{sid}>"
280
+ name = !self.name.empty? && self.name || '~Unnamed~'
281
+ api_id = !self.api_id.empty? && self.api_id || '~No-ID~'
282
+ desc += "#{Rainbow(name).underline} <#{api_id}>"
289
283
  if domain
290
284
  desc += ' '
291
285
  desc += 'https://' unless raw_url
@@ -341,7 +335,7 @@ module MrMurano
341
335
  end
342
336
 
343
337
  def valid?
344
- @valid_sid && @valid_name
338
+ @valid_api_id && @valid_name
345
339
  end
346
340
 
347
341
  def valid_name?
@@ -358,7 +352,7 @@ module MrMurano
358
352
  end
359
353
 
360
354
  class Application < Solution
361
- def initialize(sid=nil)
355
+ def initialize(api_id=nil)
362
356
  @solntype = 'application.id'
363
357
  super
364
358
  end
@@ -391,7 +385,7 @@ The name must contain at least 1 character and no more than 63.
391
385
  end
392
386
 
393
387
  class Product < Solution
394
- def initialize(sid=nil)
388
+ def initialize(api_id=nil)
395
389
  # Code path for `murano domain`.
396
390
  @solntype = 'product.id'
397
391
  super