MuranoCLI 3.2.0.beta.9 → 3.2.1.pre.beta.3

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/Rakefile +5 -0
  4. data/dockers/README.rst +7 -0
  5. data/dockers/RELEASE.rst +6 -3
  6. data/dockers/docker-test.sh +45 -17
  7. data/docs/completions/murano_completion-bash +211 -86
  8. data/lib/MrMurano/Account.rb +72 -4
  9. data/lib/MrMurano/Business.rb +163 -2
  10. data/lib/MrMurano/Commander-Entry.rb +1 -2
  11. data/lib/MrMurano/Config.rb +19 -18
  12. data/lib/MrMurano/Content.rb +26 -19
  13. data/lib/MrMurano/Gateway.rb +51 -10
  14. data/lib/MrMurano/ReCommander.rb +1 -1
  15. data/lib/MrMurano/Solution-Services.rb +80 -35
  16. data/lib/MrMurano/Solution-Users.rb +1 -0
  17. data/lib/MrMurano/SyncRoot.rb +10 -3
  18. data/lib/MrMurano/SyncUpDown-Core.rb +47 -36
  19. data/lib/MrMurano/SyncUpDown-Item.rb +46 -14
  20. data/lib/MrMurano/SyncUpDown.rb +22 -20
  21. data/lib/MrMurano/Webservice-Endpoint.rb +20 -18
  22. data/lib/MrMurano/Webservice-File.rb +63 -20
  23. data/lib/MrMurano/commands/business.rb +14 -1
  24. data/lib/MrMurano/commands/child.rb +148 -0
  25. data/lib/MrMurano/commands/devices.rb +298 -149
  26. data/lib/MrMurano/commands/element.rb +2 -1
  27. data/lib/MrMurano/commands/globals.rb +3 -0
  28. data/lib/MrMurano/commands/network.rb +152 -33
  29. data/lib/MrMurano/commands/sync.rb +2 -2
  30. data/lib/MrMurano/commands.rb +1 -0
  31. data/lib/MrMurano/verbosing.rb +13 -2
  32. data/lib/MrMurano/version.rb +1 -1
  33. data/spec/Account_spec.rb +43 -11
  34. data/spec/Content_spec.rb +5 -3
  35. data/spec/GatewayBase_spec.rb +1 -1
  36. data/spec/GatewayDevice_spec.rb +47 -8
  37. data/spec/GatewayResource_spec.rb +1 -1
  38. data/spec/GatewaySettings_spec.rb +1 -1
  39. data/spec/HttpAuthed_spec.rb +17 -3
  40. data/spec/ProjectFile_spec.rb +59 -23
  41. data/spec/Setting_spec.rb +2 -1
  42. data/spec/Solution-ServiceConfig_spec.rb +1 -1
  43. data/spec/Solution-ServiceEventHandler_spec.rb +27 -20
  44. data/spec/Solution-ServiceModules_spec.rb +7 -5
  45. data/spec/Solution-UsersRoles_spec.rb +7 -1
  46. data/spec/Solution_spec.rb +9 -1
  47. data/spec/SyncRoot_spec.rb +5 -5
  48. data/spec/SyncUpDown_spec.rb +262 -211
  49. data/spec/Verbosing_spec.rb +49 -8
  50. data/spec/Webservice-Cors_spec.rb +10 -1
  51. data/spec/Webservice-Endpoint_spec.rb +84 -65
  52. data/spec/Webservice-File_spec.rb +16 -11
  53. data/spec/Webservice-Setting_spec.rb +7 -1
  54. data/spec/_workspace.rb +9 -0
  55. data/spec/cmd_business_spec.rb +5 -10
  56. data/spec/cmd_common.rb +67 -32
  57. data/spec/cmd_config_spec.rb +9 -14
  58. data/spec/cmd_content_spec.rb +15 -26
  59. data/spec/cmd_cors_spec.rb +9 -12
  60. data/spec/cmd_device_spec.rb +31 -45
  61. data/spec/cmd_domain_spec.rb +12 -10
  62. data/spec/cmd_element_spec.rb +18 -17
  63. data/spec/cmd_exchange_spec.rb +1 -4
  64. data/spec/cmd_init_spec.rb +56 -72
  65. data/spec/cmd_keystore_spec.rb +17 -26
  66. data/spec/cmd_link_spec.rb +13 -17
  67. data/spec/cmd_password_spec.rb +9 -10
  68. data/spec/cmd_setting_application_spec.rb +95 -68
  69. data/spec/cmd_setting_product_spec.rb +59 -37
  70. data/spec/cmd_status_spec.rb +46 -84
  71. data/spec/cmd_syncdown_application_spec.rb +28 -50
  72. data/spec/cmd_syncdown_both_spec.rb +44 -93
  73. data/spec/cmd_syncdown_unit_spec.rb +858 -0
  74. data/spec/cmd_syncup_spec.rb +21 -56
  75. data/spec/cmd_token_spec.rb +0 -3
  76. data/spec/cmd_usage_spec.rb +15 -10
  77. data/spec/dry_run_formatter.rb +1 -0
  78. data/spec/fixtures/dumped_config +4 -4
  79. data/spec/spec_helper.rb +3 -0
  80. metadata +4 -2
@@ -51,8 +51,10 @@ module MrMurano
51
51
  # @example Initializing with an Item
52
52
  # item = Item.new(:name => 'get')
53
53
  # Item.new(item)
54
- def initialize(hsh={})
55
- hsh.each_pair { |k, v| self[k] = v }
54
+ def initialize(hash={})
55
+ hash.each_pair do |key, val|
56
+ self[key] = val
57
+ end
56
58
  end
57
59
 
58
60
  def as_inst(key)
@@ -102,7 +104,7 @@ module MrMurano
102
104
 
103
105
  # @return [Hash{Symbol=>Object}] A hash that represents this Item
104
106
  def to_h
105
- Hash[instance_variables.map { |k| [as_sym(k), instance_variable_get(k)] }]
107
+ Hash[instance_variables.sort.map { |k| [as_sym(k), instance_variable_get(k)] }]
106
108
  end
107
109
 
108
110
  # Adds the contents of item to self.
@@ -153,25 +155,38 @@ module MrMurano
153
155
  dup.reject!(&block)
154
156
  end
155
157
 
158
+ # Scrub local, non-BizAPI/Pegasus attrs from Item (before uploading).
159
+ def reject_ephemeral!
160
+ reject do |attr_key, _|
161
+ [
162
+ # Server-siders:
163
+ # :name,
164
+ # :script,
165
+ :local_path,
166
+ :id,
167
+ :line_beg,
168
+ :line_end,
169
+ :header,
170
+ :diff,
171
+ :selected,
172
+ :synckey,
173
+ :synctype,
174
+ :updated_at,
175
+ :dup_count,
176
+ ].include? attr_key
177
+ end
178
+ end
179
+
156
180
  # For unit testing.
157
181
  include Comparable
158
182
  def <=>(other)
159
183
  # rubocop:disable Style/RedundantSelf: Redundant self detected.
160
- # MAYBE/2017-07-18: Permanently disable Style/RedundantSelf?
161
184
  self.to_h <=> other.to_h
162
185
  end
163
186
 
164
187
  def location_friendly(show_type: false, full_path: false)
165
- if !@local_path.nil?
166
- desc = @local_path
167
- desc = desc.relative_path_from(Pathname.pwd) unless full_path
168
- desc = desc.to_s
169
- desc = "#{desc}:#{@line_beg}" if !@line_beg.nil? && @line_beg > 0
170
- desc = "#{desc}-#{@line_end}" if !@line_end.nil? && @line_end > 0
171
- else
172
- desc = @synckey
173
- end
174
- desc += ' [phantom]' if @phantom
188
+ desc = location_path_and_line(full_path: full_path)
189
+ desc += ' [phantom]' if defined?(@phantom) && @phantom
175
190
  return desc unless show_type
176
191
  unless @pp_desc.to_s.empty? || (@pp_desc == @synckey)
177
192
  desc += " (#{@pp_desc})"
@@ -180,6 +195,23 @@ module MrMurano
180
195
  desc += " [#{@method} #{@path}]" if (@method && @path)
181
196
  desc
182
197
  end
198
+
199
+ def location_path_and_line(full_path: false)
200
+ if !defined?(@local_path) || @local_path.nil?
201
+ @synckey
202
+ else
203
+ desc = @local_path
204
+ desc = desc.relative_path_from(Pathname.pwd) unless full_path
205
+ desc = desc.to_s
206
+ if defined?(@line_beg) && !@line_beg.nil? && @line_beg > 0
207
+ desc = "#{desc}:#{@line_beg}"
208
+ end
209
+ if defined?(@line_end) && !@line_end.nil? && @line_end > 0
210
+ desc = "#{desc}-#{@line_end}"
211
+ end
212
+ desc
213
+ end
214
+ end
183
215
  end
184
216
  end
185
217
  end
@@ -48,7 +48,7 @@ module MrMurano
48
48
  # :nocov:
49
49
  end
50
50
 
51
- def remove_lite(itemkey, _thereitem, _modify=false)
51
+ def remove_or_clear(itemkey, _thereitem, _modify=false)
52
52
  remove(itemkey)
53
53
  end
54
54
 
@@ -88,13 +88,14 @@ module MrMurano
88
88
  # @param root [Pathname,String] Root path for this resource type from config files
89
89
  # @param path [Pathname,String] Path to local item
90
90
  # @return [Item] hash of the details for the remote item for this path
91
- def to_remote_item(root, path)
91
+ def to_remote_items(root, path)
92
92
  # This mess brought to you by Windows short path names.
93
93
  path = Dir.glob(path.to_s).first
94
94
  root = Dir.glob(root.to_s).first
95
95
  path = Pathname.new(path)
96
96
  root = Pathname.new(root)
97
- Item.new(name: path.realpath.relative_path_from(root.realpath).to_s)
97
+ item = Item.new(name: path.realpath.relative_path_from(root.realpath).to_s)
98
+ [item]
98
99
  end
99
100
 
100
101
  ##
@@ -120,7 +121,7 @@ module MrMurano
120
121
  # @param item [Item] listing details for the item.
121
122
  # @return [Pathname] path to save (or merge) remote item into
122
123
  def tolocalpath(into, item)
123
- return item[:local_path] unless item.local_path.nil?
124
+ return item[:local_path] unless item[:local_path].nil?
124
125
  itemkey = @itemkey.to_sym
125
126
  name = tolocalname(item, itemkey)
126
127
  raise "Bad key(#{itemkey}) for #{item}" if name.nil?
@@ -188,9 +189,12 @@ module MrMurano
188
189
  local.dirname.mkpath
189
190
  local.open('wb') do |io|
190
191
  # Do not modify remote content when diffing, e.g., do not add #ENDPOINT header.
191
- untainted = options[:diff]
192
+ untainted = options[:diff] || false
192
193
  fetch(id, untainted) do |chunk|
193
- io.write config_vars_encode chunk
194
+ # First chunk may be header, if not part of script.
195
+ # Second chunk (only only chunk), is script (which may include header).
196
+ encoded = is_tmp && chunk || config_vars_encode(chunk)
197
+ io.write(encoded)
194
198
  end
195
199
  end
196
200
  update_mtime(local, item)
@@ -405,7 +409,6 @@ module MrMurano
405
409
  # @param from [Pathname] Directory of items to scan
406
410
  # @return [Array<Item>] Items found
407
411
  def localitems(from)
408
- # TODO: Profile this.
409
412
  debug "#{self.class}: Getting local items from:\n #{from}"
410
413
  search_in = from.to_s
411
414
  sf = searchFor.map { |i| ::File.join(search_in, i) }
@@ -416,14 +419,15 @@ module MrMurano
416
419
  # about duplicate resources. I [lb] think this problem has existed
417
420
  # but was exacerbated by the change to support sub-directory scripts
418
421
  # (Nested Lua support).
419
- items = Dir[*sf].uniq.flatten.compact.reject do |path|
422
+ files = Dir[*sf].collect { |path| File.absolute_path(path) }
423
+ files = files.uniq.flatten.compact.reject do |path|
420
424
  if ::File.directory?(path)
421
425
  true
422
426
  else
423
427
  ignoring.any? { |pattern| ignore?(path, pattern) }
424
428
  end
425
429
  end
426
- items = items.map do |path|
430
+ items = files.map do |path|
427
431
  # Do not resolve symlinks, just relative paths (. and ..),
428
432
  # otherwise it makes nested Lua support tricky, because
429
433
  # symlinks might be outside the root item path, and then
@@ -433,24 +437,22 @@ module MrMurano
433
437
  else
434
438
  rpath = Pathname.new(path).expand_path
435
439
  end
436
- item = to_remote_item(from, rpath)
437
- if item.is_a?(Array)
438
- item.compact.map do |itm|
439
- itm[:local_path] = rpath
440
- itm
441
- end
442
- elsif !item.nil?
440
+ files_items = to_remote_items(from, rpath)
441
+ files_items.compact.map do |item|
443
442
  item[:local_path] = rpath
444
443
  item
445
444
  end
446
445
  end
447
- #items = items.flatten.compact.sort_by!(&:local_path)
448
- #debug "#{self.class}: items:\n #{items.map(&:local_path).join("\n ")}"
449
- items = items.flatten.compact.sort_by { |it| it[:local_path] }
446
+ items = items.flatten.compact.sort_by { |item| item[:local_path] }
447
+ debug_print_localitems(items)
448
+ sort_by_name(items)
449
+ end
450
+
451
+ def debug_print_localitems(items)
452
+ return unless $cfg['tool.debug']
450
453
  loci = items.map { |it| it.location_friendly(full_path: true) }
451
454
  item_list = loci.sort.join("\n ")
452
455
  debug "#{self.class}: localitems' matches:\n #{item_list}"
453
- sort_by_name(items)
454
456
  end
455
457
 
456
458
  def ignore?(path, pattern)
@@ -25,8 +25,20 @@ module MrMurano
25
25
  attr_accessor :path
26
26
  # @return [String] Acceptable Content-Type for this endpoint
27
27
  attr_accessor :content_type
28
- # ???? What is this?
28
+ # NOTE: We do not use use_basic_auth internally, but it's passed from
29
+ # the server, so the code actually indirectly sees it in `def []=`.
29
30
  attr_accessor :use_basic_auth
31
+
32
+ def reject_ephemeral!
33
+ super.reject do |attr_key, _|
34
+ [
35
+ # Server-siders:
36
+ # :method,
37
+ # :path,
38
+ # :content_type,
39
+ ].include? attr_key
40
+ end
41
+ end
30
42
  end
31
43
 
32
44
  def initialize
@@ -170,12 +182,12 @@ module MrMurano
170
182
  name
171
183
  end
172
184
 
173
- def to_remote_item(_from, path)
185
+ def to_remote_items(_from, path)
174
186
  # Path could be have multiple endpoints in side, so a loop.
175
- items = []
176
187
  path = Pathname.new(path) unless path.is_a? Pathname
188
+ items = []
177
189
  cur = nil
178
- lineno = 1
190
+ lineno = 0
179
191
  path.readlines.each do |line|
180
192
  lineno += 1
181
193
  md = @match_header.match(line)
@@ -183,20 +195,8 @@ module MrMurano
183
195
  # header line.
184
196
  cur[:line_end] = lineno - 1 unless cur.nil?
185
197
  items << cur unless cur.nil?
186
- # VERIFY/2017-07-03: The syncdown test is revealing a
187
- # problem with casing. The original file has a lowercase
188
- # HTTP verb, e.g., "post". This is what syncup uploaded.
189
- # But on murano status, the local route's method is upcased
190
- # in memory, so the status command says the route is diff.
191
- # But on murano diff, MurCLI makes two local temp files
192
- # to execute the diff, and it also upcases the method in
193
- # both files, so the diff runs clean!
194
- # 2017-07-03: (lb): Adding upcase; and recreating header.
195
- # 2018-06-11: (lb): Added :exclude_header to deal with
196
- # service side missing header. Which isn't an error, per se.
197
198
  script_header = "--#ENDPOINT #{md[:method].upcase} #{md[:path]}"
198
199
  script_header += " #{md[:ctype]}" unless md[:ctype].to_s.empty?
199
- script_header += "\n"
200
200
  cur = RouteItem.new(
201
201
  method: md[:method].upcase,
202
202
  path: md[:path],
@@ -211,8 +211,10 @@ module MrMurano
211
211
  cur[:script] += line
212
212
  end
213
213
  end
214
- cur[:line_end] = lineno unless cur.nil?
215
- items << cur unless cur.nil?
214
+ unless cur.nil?
215
+ cur[:line_end] = lineno
216
+ items << cur
217
+ end
216
218
  items
217
219
  end
218
220
 
@@ -34,6 +34,19 @@ module MrMurano
34
34
  attr_accessor :md5
35
35
  # @return [Integer] Size in bytes of the stored file.
36
36
  attr_accessor :size
37
+
38
+ def reject_ephemeral!
39
+ super.reject do |attr_key, _|
40
+ [
41
+ # Server-siders:
42
+ # :path,
43
+ # :mime_type,
44
+ :checksum,
45
+ :md5,
46
+ :size,
47
+ ].include? attr_key
48
+ end
49
+ end
37
50
  end
38
51
 
39
52
  def initialize
@@ -212,36 +225,63 @@ module MrMurano
212
225
  # @param root [Pathname,String] Root path for this resource type from config files
213
226
  # @param path [Pathname,String] Path to local item
214
227
  # @return [Item] hash of the details for the remote item for this path
215
- def to_remote_item(from, path)
216
- item = super(from, path)
217
- name = item[:name]
228
+ def to_remote_items(from, path)
229
+ items = super(from, path)
230
+
231
+ name = items[0][:name]
218
232
  name = '/' if name == $cfg['files.default_page']
219
233
  name = "/#{name}" unless name.chars.first == '/'
220
234
 
221
- mime = MIME::Types.type_for(path.to_s)[0] || MIME::Types['application/octet-stream'][0]
235
+ mime = MIME::Types.type_for(path.to_s)[0]
236
+ mime ||= MIME::Types['application/octet-stream'][0]
237
+
238
+ file_size = path.size
222
239
 
223
240
  # FIXME/2018-04-13: (lb): When did platform switch from SHA1 to MD5?
224
241
  # Leaving this dead code block as a reminder to verify the new
225
242
  # behavior is the intended behavior.
226
243
  xsum_is_sha1 = false
227
244
  if xsum_is_sha1
228
- # It does not actually take the SHA1 of the file.
229
- # It first converts the file to hex, then takes the SHA1 of that string
230
- #xsum = Digest::SHA1.file(path.to_s).hexdigest
231
- xsum = Digest::SHA1.new
232
- path.open('rb:ASCII-8BIT') do |io|
233
- # rubocop:disable Lint/AssignmentInCondition
234
- # "Assignment in condition - you probably meant to use ==."
235
- while chunk = io.read(1_048_576)
236
- xsum << Digest.hexencode(chunk)
237
- end
238
- end
245
+ digobj = remote_item_checksum_sha1(path)
239
246
  else
240
- xsum = Digest::MD5.file(path.to_s)
247
+ # MD5 of the empty string is not empty (it's d41d8cd98f00b204e9800998ecf8427e)
248
+ # but BizAPI (or Pegasus; whomever) sends an empty string for an empty file.
249
+ if file_size.zero?
250
+ checksum = ''
251
+ digobj = nil
252
+ else
253
+ digobj = remote_item_checksum_md5(path)
254
+ end
241
255
  end
242
- debug "Checking #{name} (#{mime.simplified} #{xsum.hexdigest})"
243
256
 
244
- FileItem.new(path: name, mime_type: mime.simplified, checksum: xsum.hexdigest)
257
+ checksum = digobj.hexdigest unless digobj.nil?
258
+ debug "Checking #{name} (#{mime.simplified} #{checksum})"
259
+
260
+ item = FileItem.new(
261
+ path: name,
262
+ mime_type: mime.simplified,
263
+ checksum: checksum,
264
+ size: file_size,
265
+ )
266
+ [item]
267
+ end
268
+
269
+ def remote_item_checksum_sha1(path)
270
+ # NOTE: It does not actually take the SHA1 of the file. It first
271
+ # converts the file to hex, then takes the SHA1 of that string.
272
+ xsum = Digest::SHA1.new
273
+ path.open('rb:ASCII-8BIT') do |io|
274
+ # rubocop:disable Lint/AssignmentInCondition
275
+ # "Assignment in condition - you probably meant to use ==."
276
+ while chunk = io.read(1_048_576)
277
+ xsum << Digest.hexencode(chunk)
278
+ end
279
+ end
280
+ xsum
281
+ end
282
+
283
+ def remote_item_checksum_md5(path)
284
+ Digest::MD5.file(path.to_s)
245
285
  end
246
286
 
247
287
  # @param item [FileItem] The item to get a key from
@@ -254,8 +294,11 @@ module MrMurano
254
294
  # @param item_a [FileItem]
255
295
  # @param item_b [FileItem]
256
296
  def docmp(item_a, item_b)
257
- (item_a[:mime_type] != item_b[:mime_type] ||
258
- item_a[:checksum] != item_b[:checksum])
297
+ (
298
+ item_a[:mime_type] != item_b[:mime_type] ||
299
+ item_a[:checksum] != item_b[:checksum] ||
300
+ item_a[:size] != item_b[:size]
301
+ )
259
302
  end
260
303
  end
261
304
 
@@ -30,6 +30,13 @@ If you need to set the business ID, try some of the following:
30
30
  Add the ID to the user config: #{MrMurano::EXE_NAME} config business.id <ID> --user
31
31
  Setup a project interactively: #{MrMurano::EXE_NAME} init
32
32
 
33
+ - Working with enterprise network child businesses:
34
+ Get a list of child business IDs: #{MrMurano::EXE_NAME} network children
35
+ Specify the child ID explicitly #{MrMurano::EXE_NAME} <cmd> --config business.child=<ID>
36
+ Add the child ID to a project config: #{MrMurano::EXE_NAME} config business.child <ID>
37
+ Add the child ID to the user config: #{MrMurano::EXE_NAME} config business.child <ID> --user
38
+
39
+
33
40
  ).strip
34
41
  c.project_not_required = true
35
42
  c.subcmdgrouphelp = true
@@ -162,6 +169,9 @@ arguments. For example,
162
169
  ).strip
163
170
  c.project_not_required = true
164
171
 
172
+ # Add --network option
173
+ c.option('--network')
174
+
165
175
  cmd_table_output_add_options(c)
166
176
 
167
177
  c.action do |args, options|
@@ -181,6 +191,9 @@ Find business by name or ID.
181
191
 
182
192
  cmd_table_output_add_options(c)
183
193
 
194
+ # Add --network option
195
+ c.option('--network')
196
+
184
197
  # Add --business/-id/-name options.
185
198
  cmd_option_business_pickers(c)
186
199
 
@@ -325,7 +338,7 @@ def cmd_business_find_businesses(acc, args, options)
325
338
  end
326
339
 
327
340
  MrMurano::Verbose.whirly_start 'Looking for businesses...'
328
- bizz = acc.businesses(bid: bid, name: name, fuzzy: fuzzy)
341
+ bizz = acc.businesses(bid: bid, name: name, fuzzy: fuzzy, options: options)
329
342
  MrMurano::Verbose.whirly_stop
330
343
 
331
344
  bizz
@@ -0,0 +1,148 @@
1
+ # Copyright © 2016-2017 Exosite LLC. All Rights Reserved
2
+ # License: PROPRIETARY. See LICENSE.txt.
3
+ # frozen_string_literal: true
4
+
5
+ # vim:tw=0:ts=2:sw=2:et:ai
6
+ # Unauthorized copying of this file is strictly prohibited.
7
+
8
+ require 'MrMurano/ReCommander'
9
+
10
+ # *** Base network command help
11
+ # ------------------------------
12
+ command :child do |c|
13
+ c.syntax = %(murano child)
14
+ c.summary = %(About child)
15
+ c.description = %(Commands for working with enterprise business network children.)
16
+ c.project_not_required = true
17
+ c.subcmdgrouphelp = true
18
+ c.action do |_args, _options|
19
+ ::Commander::UI.enable_paging unless $cfg['tool.no-page']
20
+ say MrMurano::SubCmdGroupHelp.new(c).get_help
21
+ end
22
+ end
23
+
24
+ # View Network Child Business
25
+ command 'child show' do |c|
26
+ c.syntax = %(murano child show)
27
+ c.summary = %(View details about a child business in a business network.)
28
+ c.description = %(
29
+ View details about a child businesses in a business network.
30
+ ).strip
31
+ c.example %(
32
+ View details about a child business using the parent bizid and child bizid from the config
33
+ ).strip, 'murano child show'
34
+ c.example %(
35
+ View details about a child business in a business network using a passed in business.id and business.child
36
+ ).strip, 'murano child show -c business.id=<PARENT_BUSINESS_ID> -c business.child=<CHILD_BUSINESS_ID>'
37
+ c.project_not_required = true
38
+
39
+ c.action do |_args, _options|
40
+ show_child
41
+ end
42
+
43
+ def show_child
44
+ biz = MrMurano::Business.new
45
+ biz.must_business_id!
46
+ biz.must_child!
47
+ MrMurano::Verbose.whirly_start 'Fetching network child business...'
48
+ business_network = biz.get_business_network(biz.bid)
49
+ all_child_businesses = biz.view_child_businesses(biz.bid)
50
+ child_business = all_child_businesses.select { |child_biz| child_biz[:bizid] == biz.cid }[0]
51
+ MrMurano::Verbose.whirly_stop
52
+
53
+ puts('Business Network Name: ' + business_network[:name])
54
+ puts('Child Business ID: ' + child_business[:bizid])
55
+ puts('Child Business Name: ' + child_business[:name])
56
+
57
+ table = Terminal::Table.new
58
+ table.title = 'Members'
59
+ table.add_row %w[role email]
60
+ table.add_separator
61
+
62
+ child_business[:members].each do |member|
63
+ table.add_row [
64
+ member[:role],
65
+ member[:email],
66
+ ]
67
+ end
68
+ puts table
69
+
70
+ return unless all_child_businesses.nil?
71
+ MrMurano::Verbose.error 'Error fetching network child businesses.'
72
+ exit 1
73
+ end
74
+ end
75
+
76
+ # Add a Member to a Network Child Business
77
+ command 'child add' do |c|
78
+ c.syntax = %(murano child add <NEW_MEMBER_EMAIL>)
79
+ c.summary = %(Add a member to a network child business.)
80
+ c.description = %(
81
+ Add a member to a network child business.
82
+ ).strip
83
+ c.example %(
84
+ Add a member to a child business using the parent bizid and child bizid from the config
85
+ ).strip, 'murano child add <NEW_MEMBER_EMAIL>'
86
+ c.example %(
87
+ Add a member to a child business using a passed in business.id and business.child
88
+ ).strip, 'murano child add <NEW_MEMBER_EMAIL> -c business.id=<PARENT_BUSINESS_ID> -c business.child=<CHILD_BUSINESS_ID>'
89
+ c.project_not_required = true
90
+
91
+ c.action do |args, _options|
92
+ if args.empty?
93
+ MrMurano::Verbose.error('Please include a new member email.')
94
+ exit 1
95
+ end
96
+ add_member_to_child_business(args)
97
+ end
98
+
99
+ def add_member_to_child_business(args)
100
+ new_member_email = args[0]
101
+ biz = MrMurano::Business.new
102
+ biz.must_business_id!
103
+ biz.must_child!
104
+ MrMurano::Verbose.whirly_start 'Adding member to child business...'
105
+ response = biz.add_member_to_child_business(biz.bid, biz.cid, new_member_email)
106
+ MrMurano::Verbose.whirly_stop
107
+ return unless response.nil?
108
+ MrMurano::Verbose.error 'Error adding member to child business.'
109
+ exit(1)
110
+ end
111
+ end
112
+
113
+ # Remove a Member from a Network Child Business
114
+ command 'child remove' do |c|
115
+ c.syntax = %(murano child remove <MEMBER_EMAIL>)
116
+ c.summary = %(Remove a member from a network child business.)
117
+ c.description = %(
118
+ Remove a member from a network child business.
119
+ ).strip
120
+ c.example %(Remove a member from a child business using the parent bizid and child bizid from the config
121
+ ).strip, 'murano child remove <MEMBER_EMAIL>'
122
+ c.example %(
123
+ Remove a member from a child business using a passed in business.id and business.child
124
+ ).strip, 'murano child remove <MEMBER_EMAIL> -c business.id=<PARENT_BUSINESS_ID> -c business.child=<CHILD_BUSINESS_ID>'
125
+ c.project_not_required = true
126
+
127
+ c.action do |args, _options|
128
+ if args.empty?
129
+ MrMurano::Verbose.error('Please include a member email.')
130
+ exit 1
131
+ end
132
+ remove_member_from_child_business(args)
133
+ end
134
+
135
+ def remove_member_from_child_business(args)
136
+ member_email = args[0]
137
+ biz = MrMurano::Business.new
138
+ biz.must_business_id!
139
+ biz.must_child!
140
+ MrMurano::Verbose.whirly_start 'Removing member from child business...'
141
+ response = biz.remove_member_from_child_business(biz.bid, biz.cid, member_email)
142
+ MrMurano::Verbose.whirly_stop
143
+ return unless response.nil?
144
+ MrMurano::Verbose.error 'Error removing member from child business.'
145
+ exit(1)
146
+ end
147
+ end
148
+