MuranoCLI 2.2.4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.agignore +3 -0
  3. data/.gitignore +18 -1
  4. data/.rubocop.yml +222 -0
  5. data/.trustme.sh +185 -0
  6. data/.trustme.vim +24 -0
  7. data/Gemfile +23 -4
  8. data/LICENSE.txt +1 -1
  9. data/MuranoCLI.gemspec +43 -8
  10. data/README.markdown +9 -11
  11. data/Rakefile +187 -143
  12. data/TODO.taskpaper +2 -2
  13. data/bin/murano +51 -52
  14. data/docs/basic_example.rst +436 -0
  15. data/docs/completions/murano_completion-bash +3484 -0
  16. data/docs/demo.md +32 -32
  17. data/docs/develop.rst +391 -0
  18. data/lib/MrMurano.rb +21 -7
  19. data/lib/MrMurano/Account.rb +159 -174
  20. data/lib/MrMurano/Business.rb +381 -0
  21. data/lib/MrMurano/Config-Migrate.rb +32 -26
  22. data/lib/MrMurano/Config.rb +407 -128
  23. data/lib/MrMurano/Content.rb +191 -0
  24. data/lib/MrMurano/Gateway.rb +489 -0
  25. data/lib/MrMurano/Keystore.rb +48 -0
  26. data/lib/MrMurano/Passwords.rb +103 -0
  27. data/lib/MrMurano/ProjectFile.rb +121 -79
  28. data/lib/MrMurano/ReCommander.rb +114 -10
  29. data/lib/MrMurano/Setting.rb +90 -0
  30. data/lib/MrMurano/Solution-ServiceConfig.rb +89 -45
  31. data/lib/MrMurano/Solution-Services.rb +461 -166
  32. data/lib/MrMurano/Solution-Users.rb +70 -31
  33. data/lib/MrMurano/Solution.rb +372 -13
  34. data/lib/MrMurano/SolutionId.rb +73 -0
  35. data/lib/MrMurano/SyncRoot.rb +137 -0
  36. data/lib/MrMurano/SyncUpDown.rb +594 -284
  37. data/lib/MrMurano/Webservice-Cors.rb +71 -0
  38. data/lib/MrMurano/Webservice-Endpoint.rb +234 -0
  39. data/lib/MrMurano/Webservice-File.rb +193 -0
  40. data/lib/MrMurano/Webservice.rb +51 -0
  41. data/lib/MrMurano/commands.rb +18 -15
  42. data/lib/MrMurano/commands/business.rb +300 -6
  43. data/lib/MrMurano/commands/completion-bash.erb +166 -0
  44. data/lib/MrMurano/commands/{zshcomplete.erb → completion-zsh.erb} +0 -0
  45. data/lib/MrMurano/commands/completion.rb +76 -39
  46. data/lib/MrMurano/commands/config.rb +108 -44
  47. data/lib/MrMurano/commands/content.rb +115 -72
  48. data/lib/MrMurano/commands/cors.rb +29 -14
  49. data/lib/MrMurano/commands/devices.rb +286 -0
  50. data/lib/MrMurano/commands/domain.rb +52 -12
  51. data/lib/MrMurano/commands/gb.rb +24 -9
  52. data/lib/MrMurano/commands/globals.rb +64 -0
  53. data/lib/MrMurano/commands/init.rb +377 -155
  54. data/lib/MrMurano/commands/keystore.rb +92 -82
  55. data/lib/MrMurano/commands/link.rb +300 -0
  56. data/lib/MrMurano/commands/login.rb +74 -11
  57. data/lib/MrMurano/commands/logs.rb +63 -32
  58. data/lib/MrMurano/commands/mock.rb +57 -29
  59. data/lib/MrMurano/commands/password.rb +57 -39
  60. data/lib/MrMurano/commands/postgresql.rb +127 -94
  61. data/lib/MrMurano/commands/settings.rb +203 -0
  62. data/lib/MrMurano/commands/show.rb +79 -38
  63. data/lib/MrMurano/commands/solution.rb +423 -5
  64. data/lib/MrMurano/commands/solution_picker.rb +547 -0
  65. data/lib/MrMurano/commands/status.rb +195 -61
  66. data/lib/MrMurano/commands/sync.rb +78 -39
  67. data/lib/MrMurano/commands/timeseries.rb +71 -55
  68. data/lib/MrMurano/commands/tsdb.rb +113 -87
  69. data/lib/MrMurano/commands/usage.rb +57 -15
  70. data/lib/MrMurano/hash.rb +100 -10
  71. data/lib/MrMurano/http.rb +187 -43
  72. data/lib/MrMurano/makePretty.rb +16 -14
  73. data/lib/MrMurano/optparse.rb +2178 -0
  74. data/lib/MrMurano/progress.rb +138 -0
  75. data/lib/MrMurano/schema/resource-v1.0.0.yaml +32 -0
  76. data/lib/MrMurano/template/projectFile.murano.erb +16 -13
  77. data/lib/MrMurano/verbosing.rb +166 -29
  78. data/lib/MrMurano/version.rb +30 -1
  79. data/spec/Account-Passwords_spec.rb +21 -4
  80. data/spec/Account_spec.rb +69 -146
  81. data/spec/Business_spec.rb +290 -0
  82. data/spec/ConfigFile_spec.rb +1 -0
  83. data/spec/ConfigMigrate_spec.rb +12 -8
  84. data/spec/Config_spec.rb +40 -34
  85. data/spec/Content_spec.rb +363 -0
  86. data/spec/GatewayBase_spec.rb +54 -0
  87. data/spec/GatewayDevice_spec.rb +321 -0
  88. data/spec/GatewayResource_spec.rb +266 -0
  89. data/spec/GatewaySettings_spec.rb +120 -0
  90. data/spec/Http_spec.rb +18 -8
  91. data/spec/Mock_spec.rb +2 -2
  92. data/spec/ProjectFile_spec.rb +25 -14
  93. data/spec/Setting_spec.rb +110 -0
  94. data/spec/Solution-ServiceConfig_spec.rb +44 -5
  95. data/spec/Solution-ServiceEventHandler_spec.rb +23 -14
  96. data/spec/Solution-ServiceModules_spec.rb +47 -37
  97. data/spec/Solution-UsersRoles_spec.rb +10 -8
  98. data/spec/Solution_spec.rb +17 -8
  99. data/spec/SyncRoot_spec.rb +46 -20
  100. data/spec/SyncUpDown_spec.rb +437 -201
  101. data/spec/Verbosing_spec.rb +12 -4
  102. data/spec/{Solution-Cors_spec.rb → Webservice-Cors_spec.rb} +23 -20
  103. data/spec/{Solution-Endpoint_spec.rb → Webservice-Endpoint_spec.rb} +43 -41
  104. data/spec/{Solution-File_spec.rb → Webservice-File_spec.rb} +44 -33
  105. data/spec/Webservice-Setting_spec.rb +89 -0
  106. data/spec/_workspace.rb +4 -4
  107. data/spec/cmd_business_spec.rb +9 -4
  108. data/spec/cmd_common.rb +44 -1
  109. data/spec/cmd_content_spec.rb +43 -17
  110. data/spec/cmd_cors_spec.rb +4 -4
  111. data/spec/cmd_device_spec.rb +61 -16
  112. data/spec/cmd_domain_spec.rb +29 -6
  113. data/spec/cmd_init_spec.rb +281 -126
  114. data/spec/cmd_keystore_spec.rb +3 -3
  115. data/spec/cmd_link_spec.rb +98 -0
  116. data/spec/cmd_password_spec.rb +1 -1
  117. data/spec/cmd_setting_application_spec.rb +260 -0
  118. data/spec/cmd_setting_product_spec.rb +220 -0
  119. data/spec/cmd_status_spec.rb +223 -114
  120. data/spec/cmd_syncdown_spec.rb +115 -35
  121. data/spec/cmd_syncup_spec.rb +68 -15
  122. data/spec/cmd_usage_spec.rb +35 -8
  123. data/spec/fixtures/dumped_config +6 -4
  124. data/spec/fixtures/gateway_resource_files/resources.notyaml +12 -0
  125. data/spec/fixtures/gateway_resource_files/resources.yaml +13 -0
  126. data/spec/fixtures/gateway_resource_files/resources_invalid.yaml +13 -0
  127. data/spec/fixtures/mrmuranorc_deleted_bob +0 -2
  128. data/spec/fixtures/product_spec_files/lightbulb.yaml +20 -13
  129. data/spec/fixtures/{syncable_content → syncable_conflict}/services/devdata.lua +1 -1
  130. data/spec/fixtures/{syncable_content → syncable_conflict}/services/timers.lua +0 -0
  131. data/spec/spec_helper.rb +5 -0
  132. metadata +262 -171
  133. data/bin/mr +0 -8
  134. data/lib/MrMurano/Product-1P-Device.rb +0 -145
  135. data/lib/MrMurano/Product-Resources.rb +0 -205
  136. data/lib/MrMurano/Product.rb +0 -358
  137. data/lib/MrMurano/Solution-Cors.rb +0 -47
  138. data/lib/MrMurano/Solution-Endpoint.rb +0 -191
  139. data/lib/MrMurano/Solution-File.rb +0 -166
  140. data/lib/MrMurano/commands/assign.rb +0 -57
  141. data/lib/MrMurano/commands/businessList.rb +0 -45
  142. data/lib/MrMurano/commands/product.rb +0 -14
  143. data/lib/MrMurano/commands/productCreate.rb +0 -39
  144. data/lib/MrMurano/commands/productDelete.rb +0 -33
  145. data/lib/MrMurano/commands/productDevice.rb +0 -87
  146. data/lib/MrMurano/commands/productDeviceIdCmds.rb +0 -89
  147. data/lib/MrMurano/commands/productList.rb +0 -45
  148. data/lib/MrMurano/commands/productWrite.rb +0 -27
  149. data/lib/MrMurano/commands/solutionCreate.rb +0 -41
  150. data/lib/MrMurano/commands/solutionDelete.rb +0 -34
  151. data/lib/MrMurano/commands/solutionList.rb +0 -45
  152. data/spec/ProductBase_spec.rb +0 -113
  153. data/spec/ProductContent_spec.rb +0 -162
  154. data/spec/ProductResources_spec.rb +0 -329
  155. data/spec/Product_1P_Device_spec.rb +0 -202
  156. data/spec/Product_1P_RPC_spec.rb +0 -175
  157. data/spec/Product_spec.rb +0 -153
  158. data/spec/Solution-ServiceDevice_spec.rb +0 -176
  159. data/spec/cmd_assign_spec.rb +0 -51
@@ -1,67 +1,105 @@
1
+ # Last Modified: 2017.08.16 /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/verbosing'
9
+ require 'MrMurano/Business'
10
+ require 'MrMurano/ReCommander'
11
+ require 'MrMurano/Solution'
1
12
 
2
13
  command 'show' do |c|
3
14
  c.syntax = %(murano show)
4
15
  c.summary = %(Show readable information about the current configuration)
5
- c.description = %(Show readable information about the current configuration)
6
-
7
- c.action do |args, options|
16
+ c.description = %(
17
+ Show readable information about the current configuration.
18
+ ).strip
19
+ c.project_not_required = true
8
20
 
9
- if args.include?('help') then
21
+ c.action do |args, _options|
22
+ if args.include?('help')
10
23
  ::Commander::UI.enable_paging
11
24
  say MrMurano::SubCmdGroupHelp.new(c).get_help
12
25
  else
13
- acc = MrMurano::Account.new
26
+ acc = MrMurano::Account.instance
27
+ biz = MrMurano::Business.new
14
28
 
15
- selectedBusinessId = $cfg['business.id']
16
- selectedBusiness = nil
17
- acc.businesses.each do |row|
18
- selectedBusiness = row if row[:bizid] == selectedBusinessId
29
+ selected_business = nil
30
+ selected_business_id = $cfg['business.id']
31
+ unless selected_business_id.to_s.empty?
32
+ acc.businesses.each do |sol|
33
+ selected_business = sol if sol.bizid == selected_business_id
34
+ end
19
35
  end
20
36
 
21
- selectedProductId = $cfg['product.id']
22
- selectedProduct = nil
23
- acc.products.each do |row|
24
- selectedProduct = row if row[:modelId] == selectedProductId
37
+ selected_application = nil
38
+ selected_application_id = $cfg['application.id']
39
+ unless selected_application_id.to_s.empty?
40
+ MrMurano::Verbose.whirly_start('Fetching Applications...')
41
+ biz.applications.each do |sol|
42
+ selected_application = sol if sol.apiId == selected_application_id
43
+ end
44
+ MrMurano::Verbose.whirly_stop
25
45
  end
26
46
 
27
- selectedSolutionId = $cfg['solution.id']
28
- selectedSolution = nil
29
- acc.solutions.each do |row|
30
- selectedSolution = row if row[:apiId] == selectedSolutionId
47
+ selected_product = nil
48
+ selected_product_id = $cfg['product.id']
49
+ unless selected_product_id.to_s.empty?
50
+ MrMurano::Verbose.whirly_start('Fetching Products...')
51
+ biz.products.each do |sol|
52
+ selected_product = sol if sol.apiId == selected_product_id
53
+ end
54
+ MrMurano::Verbose.whirly_stop
31
55
  end
32
56
 
33
- if $cfg['user.name'] then
57
+ if $cfg['user.name']
34
58
  puts %(user: #{$cfg['user.name']})
35
59
  else
36
60
  puts 'no user selected'
37
61
  end
38
62
 
39
- if selectedBusiness then
40
- puts %(business: #{selectedBusiness[:name]})
63
+ if selected_business
64
+ puts %(business: #{selected_business.name})
41
65
  else
42
- puts 'no business selected'
66
+ #puts 'no business selected'
67
+ MrMurano::Verbose.error MrMurano::Business.missing_business_id_msg
43
68
  end
44
69
 
45
- if selectedProduct then
46
- puts %(product: #{selectedProduct[:label]})
70
+ id_not_in_biz = false
71
+
72
+ # E.g., {:bizid=>"ABC", :type=>"application", :apiId=>"XXX", :sid=>"XXX",
73
+ # :name=>"ABC", :domain=>"ABC.apps.exosite.io" }
74
+ if selected_application
75
+ puts %(application: https://#{selected_application.domain})
76
+ elsif selected_application_id
77
+ #puts 'selected application not in business'
78
+ puts "application ID from config is not in business (#{selected_application_id})"
79
+ id_not_in_biz = true
80
+ elsif !selected_business
81
+ puts 'application ID depends on business ID'
47
82
  else
48
- if selectedProductId then
49
- puts 'selected product not in business'
50
- else
51
- puts 'no product selected'
52
- end
83
+ #puts 'no application selected'
84
+ puts 'application ID not found in config'
53
85
  end
54
86
 
55
- if selectedSolution then
56
- puts %(solution: https://#{selectedSolution[:domain]})
87
+ # E.g., {:bizid=>"ABC", :type=>"product", :apiId=>"XXX", :sid=>"XXX",
88
+ # :name=>"ABC", :domain=>"ABC.m2.exosite.io" }
89
+ if selected_product
90
+ puts %(product: #{selected_product.name})
91
+ elsif selected_product_id
92
+ #puts 'selected product not in business'
93
+ puts "product ID from config is not in business (#{selected_product_id})"
94
+ id_not_in_biz = true
95
+ elsif !selected_business
96
+ puts 'product ID depends on business ID'
57
97
  else
58
- if selectedSolutionId then
59
- puts 'selected solution not in business'
60
- else
61
- puts 'no solution selected'
62
- end
98
+ #puts 'no product selected'
99
+ puts 'product ID not found in config'
63
100
  end
64
101
 
102
+ MrMurano::SolutionBase.warn_configfile_env_maybe if id_not_in_biz
65
103
  end
66
104
  end
67
105
  end
@@ -69,12 +107,15 @@ end
69
107
  command 'show location' do |c|
70
108
  c.syntax = %(murano show location)
71
109
  c.summary = %(Show readable location information)
72
- c.description = %(Show readable information about the current configuration)
110
+ c.description = %(
111
+ Show readable information about the current configuration.
112
+ ).strip
113
+ c.project_not_required = true
73
114
 
74
- c.action do |args, options|
115
+ c.action do |args, _options|
116
+ c.verify_arg_count!(args)
75
117
  puts %(base: #{$cfg['location.base']})
76
118
  $cfg['location'].each { |key, value| puts %(#{key}: #{$cfg['location.base']}/#{value}) }
77
119
  end
78
120
  end
79
121
 
80
- # vim: set ai et sw=2 ts=2 :
@@ -1,14 +1,432 @@
1
+ # Last Modified: 2017.08.17 /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/verbosing'
9
+ require 'MrMurano/Account'
10
+ require 'MrMurano/ReCommander'
1
11
  require 'MrMurano/SubCmdGroupContext'
12
+ require 'MrMurano/commands/business'
13
+ require 'MrMurano/commands/solution_picker'
2
14
 
3
15
  command :solution do |c|
4
- c.syntax = %{murano solution}
5
- c.summary = %{About Solution}
6
- c.description = %{Sub-commands for working with solutions}
16
+ c.syntax = %(murano solution)
17
+ c.summary = %(About solution)
18
+ c.description = %(
19
+ Commands for working with Application and Product solutions.
20
+ ).strip
21
+ c.project_not_required = true
7
22
 
8
- c.action do |args, options|
23
+ c.action do |_args, _options|
9
24
  ::Commander::UI.enable_paging
10
25
  say MrMurano::SubCmdGroupHelp.new(c).get_help
11
26
  end
12
27
  end
13
28
 
14
- # vim: set ai et sw=2 ts=2 :
29
+ # *** Create solution.
30
+ # --------------------
31
+
32
+ command 'solution create' do |c|
33
+ c.syntax = %(murano solution create [--options] <solution-name>)
34
+ c.summary = %(Create a new solution)
35
+ c.description = %(
36
+ Create a new solution in the current business.
37
+ ).strip
38
+ c.project_not_required = true
39
+
40
+ # Add flag: --type [application|product].
41
+ cmd_add_solntype_pickers(c, exclude_all: true)
42
+
43
+ c.option('--save', %(Save new solution ID to config))
44
+
45
+ c.action do |args, options|
46
+ c.verify_arg_count!(args, 1)
47
+ cmd_defaults_solntype_pickers(options, :application)
48
+
49
+ biz = MrMurano::Business.new
50
+ biz.must_business_id!
51
+
52
+ if args.count.zero?
53
+ sol = biz.solution_from_type!(options.type)
54
+ name = solution_ask_for_name(sol)
55
+ names = [name]
56
+ else
57
+ names = args
58
+ end
59
+
60
+ # MAYBE: Support making multiple solutions.
61
+ if names.length > 1
62
+ MrMurano::Verbose.error('Can only make one solution at a time')
63
+ exit 2
64
+ end
65
+
66
+ sol = biz.new_solution!(names.first, options.type)
67
+
68
+ if options.save
69
+ section = options.type.to_s
70
+ $cfg.set("#{section}.id", sol.sid)
71
+ $cfg.set("#{section}.name", sol.name)
72
+ end
73
+
74
+ biz.outf(sol.sid)
75
+ end
76
+ end
77
+ alias_command 'create application', 'solution create', '--type', 'application'
78
+ alias_command 'create product', 'solution create', '--type', 'product'
79
+ alias_command 'application create', 'solution create', '--type', 'application'
80
+ alias_command 'product create', 'solution create', '--type', 'product'
81
+
82
+ # *** Delete solution(s).
83
+ # -----------------------
84
+
85
+ command 'solution delete' do |c|
86
+ c.syntax = %(murano solution delete [--options] [<name-or-ID,...>])
87
+ c.summary = %(Delete a solution)
88
+ c.description = %(
89
+ Delete a solution from the business.
90
+ ).strip
91
+ c.project_not_required = true
92
+
93
+ # Add flag: --type [application|product|all].
94
+ cmd_add_solntype_pickers(c)
95
+
96
+ # Add --id and --name options.
97
+ cmd_options_add_id_and_name(c)
98
+
99
+ # Add soln pickers: --application* and --product*
100
+ cmd_option_application_pickers(c)
101
+ cmd_option_product_pickers(c)
102
+
103
+ c.option('--recursive', %(If a solution is not specified, find all solutions and prompt before deleting.))
104
+ c.option('-y', '--[no-]yes', %(Answer "yes" to all prompts and run non-interactively))
105
+
106
+ c.action do |args, options|
107
+ # SKIP: c.verify_arg_count!(args)
108
+ cmd_defaults_solntype_pickers(options)
109
+ cmd_defaults_id_and_name(options)
110
+ # Set all: true, so that cmd_solution_del_get_names_and_ids finds 'em all.
111
+ options.default(all: true)
112
+
113
+ biz = MrMurano::Business.new
114
+ biz.must_business_id!
115
+
116
+ nmorids = cmd_solution_del_get_names_and_ids!(biz, args, options)
117
+
118
+ n_deleted = 0
119
+ n_faulted = 0
120
+
121
+ nmorids.each do |name_or_id|
122
+ next unless biz.cmd_confirm_delete!(
123
+ name_or_id[0], options.yes, "Skipping #{name_or_id[0]}!"
124
+ ) do |confirmed|
125
+ confirmed
126
+ end
127
+ m_deleted, m_faulted = solution_delete(
128
+ name_or_id[0], use_sol: name_or_id[2], type: options.type, yes: options.yes
129
+ )
130
+ n_deleted += m_deleted
131
+ n_faulted += m_faulted
132
+ end
133
+ # Print results if something happened and was recursive (otherwise
134
+ # behave like, say, /bin/rm, and just be silent on success).
135
+ solution_delete_report(n_deleted, n_faulted) if options.recursive
136
+ end
137
+ end
138
+ alias_command 'delete application', 'solution delete', '--type', 'application'
139
+ alias_command 'delete product', 'solution delete', '--type', 'product'
140
+ alias_command 'application delete', 'solution delete', '--type', 'application'
141
+ alias_command 'product delete', 'solution delete', '--type', 'product'
142
+
143
+ def cmd_solution_del_get_names_and_ids!(biz, args, options)
144
+ nmorids = []
145
+ if args.count.zero?
146
+ if any_solution_pickers!(options)
147
+ exit_cmd_not_recursive!(options)
148
+ elsif !options.recursive
149
+ MrMurano::Verbose.error(
150
+ 'Please specify the name or ID of the solution to delete, or use --recursive.'
151
+ )
152
+ exit 1
153
+ end
154
+ else
155
+ exit_cmd_not_recursive!(options)
156
+ end
157
+ solz = must_fetch_solutions!(options, args, biz)
158
+ solz.each do |sol|
159
+ nmorids += [[sol.sid, "‘#{sol.name}’ <#{sol.sid}>", sol]]
160
+ end
161
+ nmorids
162
+ end
163
+
164
+ def exit_cmd_not_recursive!(options)
165
+ return unless options.recursive
166
+ MrMurano::Verbose.error(
167
+ 'The --recursive option does not apply when specifing solution IDs or names.'
168
+ )
169
+ exit 1
170
+ end
171
+
172
+ # The `murano solutions expunge -y` command simplifies what be done
173
+ # craftily other ways, e.g.,:
174
+ #
175
+ # $ for pid in $(murano product list --idonly) ; do murano product delete $pid ; done
176
+ command 'solutions expunge' do |c|
177
+ c.syntax = %(murano solution expunge)
178
+ c.summary = %(Delete all solutions)
179
+ c.description = %(
180
+ Delete all solutions in business.
181
+ ).strip
182
+ c.project_not_required = true
183
+
184
+ c.option('--[no-]all', 'Delete all Solutions in Business, not just Project')
185
+ c.option('-y', '--[no-]yes', %(Answer "yes" to all prompts and run non-interactively))
186
+
187
+ c.action do |args, options|
188
+ c.verify_arg_count!(args)
189
+ name_or_id = '*'
190
+ n_deleted, n_faulted = solution_delete(name_or_id, yes: options.yes)
191
+ solution_delete_report(n_deleted, n_faulted)
192
+ end
193
+ end
194
+
195
+ def solution_delete(name_or_id, use_sol: nil, type: :all, yes: false)
196
+ biz = MrMurano::Business.new
197
+ biz.must_business_id!
198
+
199
+ if name_or_id == '*'
200
+ return unless biz.cmd_confirm_delete!(
201
+ 'all solutions', yes, 'abort!'
202
+ ) do |confirmed|
203
+ confirmed
204
+ end
205
+ name_or_id = ''
206
+ end
207
+
208
+ if !use_sol.nil?
209
+ solz = [use_sol]
210
+ else
211
+ MrMurano::Verbose.whirly_start('Looking for solutions...')
212
+ solz = biz.solutions(type: type)
213
+ # This used to use Hash.value? to see if the name exactly matches
214
+ # any key's value. But we should be able to stick to using name.
215
+ # (E.g., it used to call sol.meta.value?(name_or_id), but this
216
+ # would return true if, say, sol.meta[:any_key] equaled name_or_id.)
217
+ unless name_or_id.empty?
218
+ solz.select! do |sol|
219
+ sol.sid == name_or_id || sol.name == name_or_id || sol.domain =~ /#{Regexp.escape(name_or_id)}\./i
220
+ end
221
+ end
222
+ MrMurano::Verbose.whirly_stop
223
+ if $cfg['tool.debug']
224
+ say 'Matches found:'
225
+ biz.outf(solz)
226
+ end
227
+ end
228
+
229
+ n_deleted = 0
230
+ n_faulted = 0
231
+ if solz.empty?
232
+ if !name_or_id.empty?
233
+ MrMurano::Verbose.error("No solution matching ‘#{name_or_id}’ found")
234
+ else
235
+ MrMurano::Verbose.error(MSG_SOLUTIONS_NONE_FOUND)
236
+ end
237
+ exit 1
238
+ else
239
+ # Solutions of different types can have the same name, so warning that
240
+ # more than one solution was found when searching by name is not valid.
241
+ #unless name_or_id.empty? or solz.length == 1
242
+ # MrMurano::Verbose.warning(
243
+ # "Unexpected number of solutions: found #{solz.length} for #{name_or_id} but expected 1"
244
+ # )
245
+ #end
246
+ MrMurano::Verbose.whirly_start('Deleting solutions...')
247
+ solz.each do |sol|
248
+ ret = biz.delete_solution(sol.sid)
249
+ if !ret.is_a?(Hash) && !ret.empty?
250
+ MrMurano::Verbose.error("Delete failed: #{ret}")
251
+ n_faulted += 1
252
+ else
253
+ n_deleted += 1
254
+ # Clear the ID from the config.
255
+ MrMurano::Config::CFG_SOLUTION_ID_KEYS.each do |keyn|
256
+ $cfg.set(keyn, nil) if $cfg[keyn] == sol.sid
257
+ end
258
+ end
259
+ end
260
+ MrMurano::Verbose.whirly_stop
261
+ end
262
+
263
+ [n_deleted, n_faulted]
264
+ end
265
+
266
+ def solution_delete_report(n_deleted, n_faulted)
267
+ unless n_deleted.nil? || n_deleted.zero?
268
+ # FIXME: Should this use 'say' or 'outf'?
269
+ inflection = MrMurano::Verbose.pluralize?('solution', n_deleted)
270
+ say "Deleted #{n_deleted} #{inflection}"
271
+ end
272
+ return if n_faulted.nil? || n_faulted.zero?
273
+ inflection = MrMurano::Verbose.pluralize?('solution', n_faulted)
274
+ MrMurano::Verbose.error("Failed to delete #{n_faulted} #{inflection}")
275
+ end
276
+
277
+ # *** List and Find solutions.
278
+ # ----------------------------
279
+
280
+ def cmd_solution_find_add_options(c)
281
+ c.option '--idonly', 'Only return the ids'
282
+ c.option '--[no-]brief', 'Show fewer fields: only Solution ID and domain'
283
+ c.option '--[no-]all', 'Find all Solutions in Business, not just Project'
284
+ c.option '-o', '--output FILE', %(Download to file instead of STDOUT)
285
+ end
286
+
287
+ command 'solution list' do |c|
288
+ c.syntax = %(murano solution list [--options])
289
+ c.summary = %(List solutions)
290
+ c.description = %(
291
+ List solutions in the current business.
292
+ ).strip
293
+ c.project_not_required = true
294
+
295
+ cmd_solution_find_add_options(c)
296
+
297
+ # Add flag: --type [application|product|all].
298
+ cmd_add_solntype_pickers(c)
299
+
300
+ c.action do |args, options|
301
+ c.verify_arg_count!(args)
302
+ # Set all: true, so that must_fetch_solutions! finds 'em all.
303
+ options.default(all: true)
304
+ cmd_defaults_solntype_pickers(options)
305
+ cmd_solution_find_and_output(args, options)
306
+ end
307
+ end
308
+ alias_command 'list application', 'solution list', '--type', 'application', '--brief'
309
+ alias_command 'list product', 'solution list', '--type', 'product', '--brief'
310
+ alias_command 'application list', 'solution list', '--type', 'application', '--brief'
311
+ alias_command 'product list', 'solution list', '--type', 'product', '--brief'
312
+ #
313
+ alias_command 'list solutions', 'solution list'
314
+ alias_command 'list applications', 'application list'
315
+ alias_command 'list products', 'product list'
316
+ alias_command 'solutions list', 'solution list'
317
+ alias_command 'applications list', 'application list'
318
+ alias_command 'products list', 'product list'
319
+
320
+ command 'solution find' do |c|
321
+ c.syntax = %(murano solution find [--options] [<name-or-ID,...>])
322
+ c.summary = %(Find solution by name or ID)
323
+ c.description = %(
324
+ Find solution by name or ID.
325
+ ).strip
326
+ c.project_not_required = true
327
+
328
+ c.example %(
329
+ Find any solution named "mysolution"
330
+ ).strip, 'murano solution find mysolution --name'
331
+
332
+ c.example %(
333
+ Find product with ID "abcdef123456"
334
+ ).strip, 'murano product find abcdef123456 --id'
335
+
336
+ c.example %(
337
+ Find application with name or ID matching "1234"
338
+ ).strip, 'murano application find 1234'
339
+
340
+ cmd_solution_find_add_options(c)
341
+
342
+ cmd_add_solntype_pickers(c)
343
+
344
+ # Add --id and --name options.
345
+ cmd_options_add_id_and_name(c)
346
+
347
+ # Add soln pickers: --application* and --product*
348
+ cmd_option_application_pickers(c)
349
+ cmd_option_product_pickers(c)
350
+
351
+ c.action do |args, options|
352
+ # SKIP: c.verify_arg_count!(args)
353
+ cmd_defaults_solntype_pickers(options)
354
+ cmd_defaults_id_and_name(options)
355
+ # Set all: true, so that must_fetch_solutions! finds 'em all.
356
+ options.default(all: true)
357
+ if args.none? && !any_solution_pickers!(options)
358
+ MrMurano::Verbose.error('What would you like to find?')
359
+ exit 1
360
+ end
361
+ cmd_solution_find_and_output(args, options)
362
+ end
363
+ end
364
+ alias_command 'find application', 'solution find', '--type', 'application', '--brief'
365
+ alias_command 'find product', 'solution find', '--type', 'product', '--brief'
366
+ alias_command 'application find', 'solution find', '--type', 'application', '--brief'
367
+ alias_command 'product find', 'solution find', '--type', 'product', '--brief'
368
+
369
+ def cmd_solution_find_and_output(args, options)
370
+ cmd_verify_args_and_id_or_name!(args, options)
371
+ biz = MrMurano::Business.new
372
+ biz.must_business_id!
373
+ solz = cmd_solution_find_solutions(biz, args, options)
374
+ if solz.empty? && !options.idonly
375
+ MrMurano::Verbose.error(MSG_SOLUTIONS_NONE_FOUND)
376
+ exit 0
377
+ end
378
+ cmd_solution_output_solutions(biz, solz, options)
379
+ end
380
+
381
+ def cmd_solution_find_solutions(biz, args, options)
382
+ must_fetch_solutions!(options, args, biz)
383
+ end
384
+
385
+ def cmd_solution_output_solutions(biz, solz, options)
386
+ if options.idonly
387
+ headers = %i[apiId]
388
+ solz = solz.map { |row| [row.apiId] }
389
+ elsif options.brief
390
+ #headers = %i[apiId domain]
391
+ #solz = solz.map { |row| [row.apiId, row.domain] }
392
+ headers = %i[apiId domain name]
393
+ solz = solz.map { |row| [row.apiId, row.domain, row.name] }
394
+ else
395
+ headers = (solz.first && solz.first.meta || {}).keys
396
+ headers.delete(:sid) if headers.include?(:apiId) && headers.include?(:sid)
397
+ headers.sort_by! do |hdr|
398
+ case hdr
399
+ when :bizid
400
+ 0
401
+ when :type
402
+ 1
403
+ when :apiId
404
+ 2
405
+ when :domain
406
+ 3
407
+ when :name
408
+ 4
409
+ else
410
+ 5
411
+ end
412
+ end
413
+ solz = solz.map { |row| headers.map { |hdr| row.meta[hdr] } }
414
+ end
415
+
416
+ io = File.open(options.output, 'w') if options.output
417
+ biz.outf(solz, io) do |dd, ios|
418
+ if options.idonly
419
+ ios.puts(dd.join(' '))
420
+ else
421
+ biz.tabularize(
422
+ {
423
+ headers: headers.map(&:to_s),
424
+ rows: dd,
425
+ },
426
+ ios,
427
+ )
428
+ end
429
+ end
430
+ io.close unless io.nil?
431
+ end
432
+