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.
- 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
|