MuranoCLI 3.0.1 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.agignore +1 -0
- data/.rubocop.yml +67 -5
- data/Gemfile +6 -3
- data/MuranoCLI.gemspec +14 -10
- data/README.markdown +299 -126
- data/Rakefile +6 -1
- data/bin/murano +2 -2
- data/docs/completions/murano_completion-bash +93 -0
- data/lib/MrMurano.rb +19 -2
- data/lib/MrMurano/Business.rb +22 -19
- data/lib/MrMurano/Config.rb +19 -9
- data/lib/MrMurano/Content.rb +4 -4
- data/lib/MrMurano/Exchange-Element.rb +99 -0
- data/lib/MrMurano/Exchange.rb +137 -0
- data/lib/MrMurano/Gateway.rb +9 -9
- data/lib/MrMurano/Keystore.rb +4 -2
- data/lib/MrMurano/ReCommander.rb +3 -5
- data/lib/MrMurano/Solution-ServiceConfig.rb +12 -12
- data/lib/MrMurano/Solution-Services.rb +15 -14
- data/lib/MrMurano/Solution-Users.rb +2 -2
- data/lib/MrMurano/Solution.rb +43 -49
- data/lib/MrMurano/SolutionId.rb +28 -28
- data/lib/MrMurano/SyncUpDown.rb +32 -22
- data/lib/MrMurano/Webservice-Endpoint.rb +2 -1
- data/lib/MrMurano/Webservice.rb +5 -5
- data/lib/MrMurano/commands.rb +2 -1
- data/lib/MrMurano/commands/business.rb +21 -19
- data/lib/MrMurano/commands/domain.rb +16 -2
- data/lib/MrMurano/commands/exchange.rb +272 -0
- data/lib/MrMurano/commands/globals.rb +17 -1
- data/lib/MrMurano/commands/init.rb +3 -3
- data/lib/MrMurano/commands/link.rb +16 -16
- data/lib/MrMurano/commands/postgresql.rb +2 -2
- data/lib/MrMurano/commands/show.rb +13 -7
- data/lib/MrMurano/commands/solution.rb +23 -17
- data/lib/MrMurano/commands/solution_picker.rb +49 -44
- data/lib/MrMurano/commands/sync.rb +2 -1
- data/lib/MrMurano/commands/timeseries.rb +2 -2
- data/lib/MrMurano/commands/tsdb.rb +2 -2
- data/lib/MrMurano/hash.rb +19 -7
- data/lib/MrMurano/http.rb +12 -2
- data/lib/MrMurano/orderedhash.rb +200 -0
- data/lib/MrMurano/spec_commander.rb +98 -0
- data/lib/MrMurano/verbosing.rb +2 -2
- data/lib/MrMurano/version.rb +2 -2
- data/spec/Business_spec.rb +8 -6
- data/spec/Solution-ServiceConfig_spec.rb +1 -1
- data/spec/SyncUpDown_spec.rb +6 -6
- data/spec/_workspace.rb +9 -4
- data/spec/cmd_business_spec.rb +8 -2
- data/spec/cmd_common.rb +266 -25
- data/spec/cmd_exchange_spec.rb +118 -0
- data/spec/cmd_help_spec.rb +54 -13
- data/spec/cmd_init_spec.rb +1 -12
- data/spec/cmd_link_spec.rb +94 -72
- data/spec/spec_helper.rb +11 -16
- 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
|
+
|
data/lib/MrMurano/Gateway.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.
|
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
|
-
@
|
33
|
-
|
34
|
-
@uriparts = [:service, @
|
35
|
-
@
|
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 = "#{@
|
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: @
|
475
|
-
model: @
|
474
|
+
vendor: @api_id,
|
475
|
+
model: @api_id,
|
476
476
|
sn: identifier,
|
477
477
|
}
|
478
478
|
request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
|
data/lib/MrMurano/Keystore.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
-
# Last Modified: 2017.
|
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(
|
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
|
data/lib/MrMurano/ReCommander.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
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.
|
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(
|
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: @
|
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(
|
115
|
+
def initialize(api_id=nil)
|
116
116
|
super
|
117
117
|
@uriparts << :service
|
118
118
|
end
|
119
119
|
|
120
|
-
def
|
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
|
127
|
-
return @
|
128
|
-
@
|
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=
|
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=
|
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(
|
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(
|
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.
|
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(
|
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: @
|
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
|
-
@
|
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(
|
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
|
-
[@
|
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
|
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(
|
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
|
-
[@
|
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(
|
486
|
+
def default_event_script(service_or_api_id, &block)
|
486
487
|
post(
|
487
488
|
'/',
|
488
489
|
{
|
489
|
-
solution_id: @
|
490
|
-
service:
|
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(
|
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(
|
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.
|
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 = []
|
data/lib/MrMurano/Solution.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.
|
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
|
-
@
|
23
|
+
@uriparts_apidex = 1
|
24
24
|
# Introspection. Feels hacky.
|
25
25
|
if from.is_a? MrMurano::Solution
|
26
|
-
|
27
|
-
@
|
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
|
-
|
33
|
+
init_api_id!(from)
|
34
34
|
end
|
35
|
-
@uriparts = [:solution, @
|
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
|
-
[@
|
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: #{@
|
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(
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
-
@
|
191
|
+
@valid_api_id = true
|
193
192
|
else
|
194
193
|
self.meta = {}
|
195
|
-
@
|
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
|
234
|
-
# The solution details fetched from a call to Solution.get()
|
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
|
-
|
238
|
-
|
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(
|
247
|
-
"but config says #{fancy_ticks(@
|
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.
|
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
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
288
|
-
desc += "#{Rainbow(name).underline} <#{
|
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
|
-
@
|
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(
|
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(
|
388
|
+
def initialize(api_id=nil)
|
395
389
|
# Code path for `murano domain`.
|
396
390
|
@solntype = 'product.id'
|
397
391
|
super
|