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
@@ -0,0 +1,547 @@
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/Business'
10
+ require 'MrMurano/Config'
11
+ require 'MrMurano/Solution'
12
+
13
+ MSG_SOLUTIONS_NONE_FOUND = 'No solutions found' unless defined? MSG_SOLUTIONS_NONE_FOUND
14
+
15
+ # *** For some commands: let user restrict to specific solution --type.
16
+ # ---------------------------------------------------------------------
17
+
18
+ def cmd_add_solntype_pickers(c, exclude_all: false)
19
+ # 2017-07-26: HA! The --type option can get masked by aliases.
20
+ # For instance, if the option is required ("--type TYPE"), then
21
+ # murano domain --type product
22
+ # fails, because the "domain product" alias steals the --type argument,
23
+ # so the parser exits, complaining that --type is missing an argument!
24
+ # This, however, works:
25
+ # murano domain --type=product
26
+ # as does
27
+ # murano domain -t product
28
+ # To work around this, make the argument optional ("--type [TYPE]") and
29
+ # then do some extra processing later to check for this special case.
30
+ allowed_types = MrMurano::Business::ALLOWED_TYPES.dup
31
+ allowed_types += [:all] unless exclude_all
32
+ allowed_types.map!(&:to_s).sort!
33
+ default = exclude_all && 'application' || 'all'
34
+ c.option(
35
+ '--type [TYPE]',
36
+ allowed_types,
37
+ %(Apply to solution(s) of type [#{allowed_types.join('|')}] (default: #{default}))
38
+ )
39
+ end
40
+
41
+ def cmd_defaults_solntype_pickers(options, default=:all)
42
+ cmd_defaults_type_kludge(options)
43
+
44
+ if options.type.to_s.empty?
45
+ options.type = default.to_sym
46
+ else
47
+ options.type = options.type.to_sym
48
+ end
49
+ end
50
+
51
+ def cmd_defaults_type_kludge(options)
52
+ # KLUDGE/2017-07-26: Work around rb-commander peculiarity.
53
+ # The alias_command steals the --type parameter, e.g.,
54
+ # murano domain --type product
55
+ # is interpreted using the "domain product" alias,
56
+ # so the command that's parsed is actually
57
+ # murano domain --type product --type
58
+ # and the latter --type wins [which is something that [lb]
59
+ # really dislikes about rb-commander, is that it does not
60
+ # support more than one of the same options, taking only the
61
+ # last one's argument].
62
+ next_posit = 1
63
+ ARGV.each do |arg|
64
+ if arg.casecmp('--type').zero?
65
+ if ARGV.length == next_posit
66
+ MrMurano::Verbose.error('missing argument: --type')
67
+ exit 1
68
+ else
69
+ # NOTE: Commander treats arguments case sensitively, but not --options.
70
+ possible_type = ARGV[next_posit].to_sym
71
+ if MrMurano::Business::ALLOWED_TYPES.include?(possible_type)
72
+ options.type = possible_type
73
+ else
74
+ MrMurano::Verbose.error("unrecognized --type: #{possible_type}")
75
+ exit 1
76
+ end
77
+ break
78
+ end
79
+ end
80
+ next_posit += 1
81
+ end
82
+ return unless options.type == true
83
+ MrMurano::Verbose.error('missing argument: --type')
84
+ exit 1
85
+ end
86
+
87
+ # Get a list of solutions under the business.
88
+ # - Optionally filter by --type: in the command block, call
89
+ # cmd_add_solntype_pickers, and then in the action block, call
90
+ # cmd_defaults_solntype_pickers, and then call this method.
91
+ # - Optional restrict to just solutions in the current project.
92
+ def must_fetch_solutions!(options, args=[], biz=nil)
93
+ solz = []
94
+
95
+ if biz.nil?
96
+ biz = MrMurano::Business.new
97
+ biz.must_business_id!
98
+ end
99
+ if args.any?
100
+ #raise 'Cannot use options.all and solution pickers' unless options.all.nil?
101
+ flattened = args.map { |cell| cell.split(',') }.flatten
102
+ sid = []
103
+ name = []
104
+ fuzzy = []
105
+ if options.id
106
+ sid = flattened
107
+ elsif options.name
108
+ name = flattened
109
+ else
110
+ fuzzy = flattened
111
+ end
112
+ solz += solution_get_solutions(
113
+ biz, options.type, sid: sid, name: name, fuzzy: fuzzy
114
+ )
115
+ end
116
+
117
+ if any_solution_pickers!(options)
118
+ #raise 'Cannot use options.all and solution pickers' unless options.all.nil?
119
+ #
120
+ # MAYBE: DRY this code. Rather than copy-paste-find-replace block of code.
121
+ # See also: any_business_pickers?
122
+ #
123
+ sid = []
124
+ name = []
125
+ fuzzy = []
126
+ if options.application_id
127
+ sid = [options.application_id]
128
+ elsif options.application_name
129
+ name = [options.application_name]
130
+ elsif options.application
131
+ fuzzy = [options.application]
132
+ end
133
+ if !sid.empty? || !name.empty? || !fuzzy.empty?
134
+ solz += solution_get_solutions(
135
+ biz, :application, sid: sid, name: name, fuzzy: fuzzy
136
+ )
137
+ end
138
+ #
139
+ sid = []
140
+ name = []
141
+ fuzzy = []
142
+ if options.product_id
143
+ sid = [options.product_id]
144
+ elsif options.product_name
145
+ name = [options.product_name]
146
+ elsif options.product
147
+ fuzzy = [options.product]
148
+ end
149
+ if !sid.empty? || !name.empty? || !fuzzy.empty?
150
+ solz += solution_get_solutions(
151
+ biz, :product, sid: sid, name: name, fuzzy: fuzzy
152
+ )
153
+ end
154
+ #
155
+ end
156
+
157
+ if args.none? && !any_solution_pickers!(options)
158
+ if !options.all
159
+ if $cfg['application.id']
160
+ solz += solution_get_solutions(
161
+ biz, :application, sid: $cfg['application.id']
162
+ )
163
+ end
164
+ if $cfg['product.id']
165
+ solz += solution_get_solutions(
166
+ biz, :product, sid: $cfg['product.id']
167
+ )
168
+ end
169
+ else
170
+ solz += solution_get_solutions(biz, options.type)
171
+ end
172
+ end
173
+
174
+ culled = {}
175
+ solz.select! do |sol|
176
+ if culled[sol.sid]
177
+ false
178
+ else
179
+ culled[sol.sid] = true
180
+ true
181
+ end
182
+ end
183
+
184
+ if solz.empty?
185
+ MrMurano::Verbose.error(MSG_SOLUTIONS_NONE_FOUND)
186
+ exit 0
187
+ end
188
+
189
+ biz.sort_solutions!(solz)
190
+
191
+ solz
192
+ end
193
+
194
+ # *** For murano init: specify --business, --application, and/or --product.
195
+ # -------------------------------------------------------------------------
196
+
197
+ def cmd_option_application_pickers(c)
198
+ c.option('--application-id ID', String, %(ID of Application to use))
199
+ c.option('--application-name NAME', String, %(Name of Application to use))
200
+ c.option('--application APPLICATION', String, %(Name or ID of Application to use))
201
+ end
202
+
203
+ def cmd_option_product_pickers(c)
204
+ c.option('--product-id ID', String, %(ID of Product to use))
205
+ c.option('--product-name NAME', String, %(Name of Product to use))
206
+ c.option('--product PRODUCT', String, %(Name or ID of Product to use))
207
+ end
208
+
209
+ def any_solution_pickers!(options)
210
+ any_application_pickers!(options) || any_product_pickers!(options)
211
+ end
212
+
213
+ def any_application_pickers!(options)
214
+ num_ways = 0
215
+ num_ways += 1 unless options.application_id.to_s.empty?
216
+ num_ways += 1 unless options.application_name.to_s.empty?
217
+ num_ways += 1 unless options.application.to_s.empty?
218
+ #if num_ways > 1
219
+ # MrMurano::Verbose.error(
220
+ # 'Please specify only one of: --application, --application-id, or --application-name'
221
+ # )
222
+ # exit 1
223
+ #end
224
+ num_ways > 0
225
+ end
226
+
227
+ def any_product_pickers!(options)
228
+ num_ways = 0
229
+ num_ways += 1 unless options.product_id.to_s.empty?
230
+ num_ways += 1 unless options.product_name.to_s.empty?
231
+ num_ways += 1 unless options.product.to_s.empty?
232
+ #if num_ways > 1
233
+ # MrMurano::Verbose.error(
234
+ # 'Please specify only one of: --product, --product-id, or --product-name'
235
+ # )
236
+ # exit 1
237
+ #end
238
+ num_ways > 0
239
+ end
240
+
241
+ def solution_get_solutions(biz, type, sid: nil, name: nil, fuzzy: nil)
242
+ if type == :all
243
+ inflection = 'solutions'
244
+ else
245
+ inflection = MrMurano::Verbose.pluralize?(type.to_s, 0)
246
+ end
247
+ MrMurano::Verbose.whirly_start("Fetching #{inflection}...")
248
+ solz = biz.solutions(
249
+ type: type, sid: sid, name: name, fuzzy: fuzzy, invalidate: false
250
+ )
251
+ MrMurano::Verbose.whirly_stop
252
+ solz
253
+ end
254
+
255
+ def solution_ask_for_name(sol)
256
+ asking = true
257
+ while asking
258
+ solname = ask("Please enter the #{sol.type_name} name: ")
259
+ puts ''
260
+ if solname == ''
261
+ confirmed = ask("\nReally skip the #{sol.type_name}? [Y/n] ", true)
262
+ if confirmed
263
+ puts ''
264
+ return '', '', false
265
+ end
266
+ else
267
+ #unless sol.name.match(sol.name_validate_regex) { say ... }
268
+ begin
269
+ sol.set_name!(solname)
270
+ break
271
+ rescue MrMurano::ConfigError => _err
272
+ say(sol.name_validate_help)
273
+ # keep looping
274
+ end
275
+ end
276
+ end
277
+ sol.name
278
+ end
279
+
280
+ # *** Interact with the user to identify the solution.
281
+ # ----------------------------------------------------
282
+
283
+ # For more on the ** doublesplat, and the **_ starsnake, see:
284
+ # https://flushentitypacket.github.io/ruby/2015/03/31/ruby-keyword-arguments-the-double-splat-and-starsnake.html
285
+ # "Basically, _ is Ruby’s variable name for storing values you don’t need."
286
+ # Ruby 2.0 and above. I don't think we support 1.x...
287
+
288
+ def get_two_solutions!(sol_a_id=nil, sol_b_id=nil, **options)
289
+ app_srchs = []
290
+ prd_srchs = []
291
+
292
+ #app_srchs += [[:application, :sid, sol_a_id]] unless sol_a_id.to_s.empty?
293
+ #prd_srchs += [[:product, :sid, sol_b_id]] unless sol_b_id.to_s.empty?
294
+ app_srchs += [[nil, :sid, sol_a_id]] unless sol_a_id.to_s.empty?
295
+ prd_srchs += [[nil, :sid, sol_b_id]] unless sol_b_id.to_s.empty?
296
+
297
+ app_srchs += get_soln_searches(:application, options)
298
+ prd_srchs += get_soln_searches(:product, options)
299
+
300
+ if app_srchs.length.zero? && prd_srchs.length < 2
301
+ # TEST/2017-08-16: Clear application.id and test.
302
+ app_srchs = [[:application, :sid, $cfg['application.id']]]
303
+ end
304
+ if prd_srchs.length.zero? && app_srchs.length < 2
305
+ # TEST/2017-08-16: Clear product.id and test.
306
+ prd_srchs = [[:product, :sid, $cfg['product.id']]]
307
+ end
308
+
309
+ sol_srchs = app_srchs + prd_srchs
310
+
311
+ if sol_srchs.length > 2
312
+ MrMurano::Verbose.error('too many solutions specified: specify 2 solutions')
313
+ exit 1
314
+ end
315
+
316
+ biz = MrMurano::Business.new
317
+ solz = []
318
+ sol_srchs.each do |type, desc, value|
319
+ sol_opts = {}
320
+ case desc
321
+ when :sid
322
+ sol_opts[:match_sid] = value
323
+ when :name
324
+ sol_opts[:match_name] = value
325
+ when :term
326
+ sol_opts[:match_fuzzy] = value
327
+ else
328
+ raise false
329
+ end
330
+ sol = solution_find_or_create(**sol_opts, biz: biz, type: type)
331
+ solz += [sol]
332
+ end
333
+
334
+ solz
335
+ end
336
+
337
+ def get_soln_searches(sol_type, options)
338
+ sol_type = sol_type.to_sym
339
+ sol_srchs = []
340
+ # E.g., :application_id
341
+ if options["#{sol_type}_id".to_sym]
342
+ app_ids = options["#{sol_type}_id".to_sym].split(',')
343
+ app_ids.each { |sid| sol_srchs += [[sol_type, :sid, sid]] }
344
+ end
345
+ # E.g., :application_name
346
+ if options["#{sol_type}_name".to_sym]
347
+ app_names = options["#{sol_type}_name".to_sym].split(',')
348
+ app_names.each { |name| sol_srchs += [[sol_type, :name, name]] }
349
+ end
350
+ # E.g., :application
351
+ if options[sol_type]
352
+ app_finds = options[sol_type].split(',')
353
+ app_finds.each { |term| sol_srchs += [[sol_type, :term, term]] }
354
+ end
355
+ sol_srchs
356
+ end
357
+
358
+ def solution_find_or_create(biz: nil, type: nil, **options)
359
+ type = options[:type] if type.nil?
360
+ raise 'You mush specify the :type of solution' if type.nil? && options[:create_ok]
361
+ options[:match_enable] = true if options[:match_enable].nil?
362
+ finder = MrMurano::InteractiveSolutionFinder.new(options)
363
+ model = biz.solution_from_type!(type)
364
+ finder.find_or_create(model)
365
+ end
366
+
367
+ module MrMurano
368
+ # A class for finding solutions, either automatically or interactively.
369
+ class InteractiveSolutionFinder
370
+ def initialize(
371
+ skip_verify: false,
372
+ create_ok: false,
373
+ update_cfg: false,
374
+ ignore_cfg: false,
375
+ verbose: false,
376
+ match_enable: false,
377
+ match_sid: nil,
378
+ match_name: nil,
379
+ match_fuzzy: nil
380
+ )
381
+ @skip_verify = skip_verify
382
+ @create_ok = create_ok
383
+ @update_cfg = update_cfg
384
+ @ignore_cfg = ignore_cfg
385
+ @verbose = verbose
386
+ @match_enable = match_enable
387
+ @match_sid = match_sid
388
+ @match_name = match_name
389
+ @match_fuzzy = match_fuzzy
390
+ @match_sid = nil if @match_sid.to_s.empty?
391
+ @match_name = nil if @match_name.to_s.empty?
392
+ @match_fuzzy = nil if @match_fuzzy.to_s.empty?
393
+ @searching = @match_enable && (@match_sid || @match_name || @match_fuzzy)
394
+ end
395
+
396
+ def find_or_create(model)
397
+ # First, try to find the solution by solution ID.
398
+ sol = solution_find_by_sid(model)
399
+ # If not found, search existing solutions, and maybe ask user.
400
+ if sol.nil?
401
+ if @searching
402
+ sol = solution_search_by_term(model)
403
+ sol = solution_create_new_solution(model) if sol.nil? && @create_ok && @match_sid.nil?
404
+ else
405
+ sol = solution_lookup_or_ask(model)
406
+ end
407
+ end
408
+ # Finally, if asked, update the config.
409
+ if @update_cfg && !sol.nil?
410
+ # Update the config in memory and on disk/file.
411
+ $cfg.set(sol.cfg_key_id, sol.sid, :project)
412
+ $cfg.set(sol.cfg_key_name, sol.name, :project)
413
+ end
414
+ sol
415
+ end
416
+
417
+ def solution_find_by_sid(sol)
418
+ exists = false
419
+ if @searching || @ignore_cfg
420
+ sol.sid = @match_sid || @match_fuzzy
421
+ else
422
+ # Note that we verify the solution ID we find in the config,
423
+ # since the user could've, e.g., deleted it via the web UI.
424
+ # LATER: This only works so long as there's only one Product
425
+ # or one Application. Eventually we'll add support for more.
426
+ sol.sid = $cfg[sol.cfg_key_id].to_s
427
+ return sol if @skip_verify
428
+ end
429
+ if sol.sid?
430
+ tried_sid = sol.sid
431
+ if @searching
432
+ whirly_msg = "Searching #{sol.type_name} by ID..."
433
+ else
434
+ whirly_msg = "Verifying #{sol.type_name}..."
435
+ end
436
+ MrMurano::Verbose.whirly_start(whirly_msg)
437
+ sol.info_safe
438
+ if sol.valid_sid
439
+ exists = true
440
+ # Convert from Solution to proper subclass, perhaps.
441
+ sol = solution_factory_reset(sol)
442
+ else
443
+ sol.sid = nil
444
+ end
445
+ MrMurano::Verbose.whirly_stop
446
+ # Spit out some messages, maybe.
447
+ if @verbose
448
+ if exists
449
+ say "Found #{sol.type_name} #{sol.pretty_desc}"
450
+ elsif !@searching
451
+ # The solution ID in the config was not found for this business.
452
+ say "The #{sol.type_name} ID ‘#{tried_sid}’ from the config was not found"
453
+ end
454
+ puts ''
455
+ end
456
+ end
457
+ (exists && sol) || nil
458
+ end
459
+
460
+ def solution_lookup_or_ask(sol)
461
+ solz = solution_get_solutions(sol.biz, sol.type)
462
+ if solz.count == 1
463
+ sol = solz.first
464
+ #say "This business has one #{sol.type_name}. Using #{Rainbow(sol.domain).underline}"
465
+ say "This business has one #{sol.type_name}. Using #{sol.pretty_desc}" if @verbose
466
+ puts '' if @verbose
467
+ elsif solz.count.zero?
468
+ if @create_ok
469
+ sol = solution_create_new_solution(sol)
470
+ else
471
+ sol.error("No #{Inflecto.pluralize(sol.type.to_s)} found")
472
+ sol = nil
473
+ end
474
+ else
475
+ sol = solution_choose_solution(solz, sol.type_name)
476
+ end
477
+ sol
478
+ end
479
+
480
+ def solution_create_new_solution(sol)
481
+ # See if user specified name using a switch option.
482
+ solname = nil
483
+ solname = @match_name if solname.nil?
484
+ solname = @match_fuzzy if solname.nil?
485
+ if solname.nil?
486
+ #say "You do not have any #{type}s. Let's create one."
487
+ if @verbose
488
+ say("This business does not have any #{Inflecto.pluralize(sol.type.to_s)}. Let's create one")
489
+ puts ''
490
+ end
491
+ solution_ask_for_name(sol)
492
+ else
493
+ sol.set_name!(solname)
494
+ end
495
+ # MAYBE/2017-07-20: Detect if Business is ADC enabled. If not,
496
+ # creating a solution fails, e.g.,
497
+ # Request Failed: 409: [409] upgrade
498
+ sol = sol.biz.new_solution!(sol.name, sol.type) unless sol.name.to_s.empty?
499
+ say "Created new #{sol.pretty_desc(add_type: true)}" if @verbose
500
+ puts '' if @verbose
501
+ sol
502
+ end
503
+
504
+ def solution_choose_solution(solz, type_name)
505
+ sol = nil
506
+ choose do |menu|
507
+ menu.prompt = "Select which #{type_name} to use:"
508
+ menu.flow = :columns_across
509
+ # NOTE: There are 2 human friendly identifiers, :name and :domain.
510
+ solz.sort_by(&:domain).each do |option|
511
+ menu.choice(option.domain.sub(/\..*$/, '')) do
512
+ sol = option
513
+ end
514
+ end
515
+ end
516
+ sol
517
+ end
518
+
519
+ def solution_search_by_term(sol)
520
+ solz = solution_get_solutions(
521
+ sol.biz, sol.type, sid: @match_sid, name: @match_name, fuzzy: @match_fuzzy
522
+ )
523
+ if solz.length > 1
524
+ sol.error("More than one matching #{sol.type_name} found. Please be more specific")
525
+ sol = nil
526
+ # MAYBE/2017-07-01: Show interactive menu.
527
+ # For now, if we didn't exit, weird behavior might occur, e.g., if
528
+ # user calls `murano init --application foo` and 2 matches are found,
529
+ # if we returned nil, the code would create a new application.
530
+ exit 1
531
+ elsif solz.length.zero?
532
+ inflection = MrMurano::Verbose.pluralize?(sol.type_name, 0)
533
+ # Only blather an error if we're not about to create a new solution.
534
+ sol.error("No matching #{inflection} found.") unless @create_ok
535
+ sol = nil
536
+ # It's okay not to exit. If `murano init` was called, a new
537
+ # solution will be created; otherwise, the command will exit.
538
+ else
539
+ sol = solz.first
540
+ say "Found one matching #{sol.type_name}. Using #{sol.pretty_desc}" if @verbose
541
+ puts '' if @verbose
542
+ end
543
+ sol
544
+ end
545
+ end
546
+ end
547
+