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

Sign up to get free protection for your applications and to get access to all the features.
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
+