morpheus-cli 6.2.3 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/README.md +4 -1
- data/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/library_cluster_packages_interface.rb +41 -0
- data/lib/morpheus/api/option_type_forms_interface.rb +9 -0
- data/lib/morpheus/cli/cli_command.rb +3 -1
- data/lib/morpheus/cli/cli_registry.rb +2 -4
- data/lib/morpheus/cli/commands/backups_command.rb +1 -1
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +54 -12
- data/lib/morpheus/cli/commands/certificates_command.rb +1 -1
- data/lib/morpheus/cli/commands/clusters.rb +1 -2
- data/lib/morpheus/cli/commands/library_cluster_packages_command.rb +485 -0
- data/lib/morpheus/cli/commands/library_forms_command.rb +625 -0
- data/lib/morpheus/cli/commands/network_servers_command.rb +19 -9
- data/lib/morpheus/cli/commands/policies_command.rb +112 -128
- data/lib/morpheus/cli/commands/roles.rb +3 -0
- data/lib/morpheus/cli/commands/self_service_command.rb +17 -0
- data/lib/morpheus/cli/commands/service_catalog_command.rb +70 -30
- data/lib/morpheus/cli/commands/virtual_images.rb +1 -1
- data/lib/morpheus/cli/error_handler.rb +18 -3
- data/lib/morpheus/cli/mixins/accounts_helper.rb +2 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +20 -6
- data/lib/morpheus/cli/mixins/prompt_helper.rb +132 -11
- data/lib/morpheus/cli/option_types.rb +104 -7
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/routes.rb +9 -0
- metadata +7 -2
@@ -0,0 +1,625 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
class Morpheus::Cli::LibraryFormsCommand
|
5
|
+
include Morpheus::Cli::CliCommand
|
6
|
+
include Morpheus::Cli::LibraryHelper
|
7
|
+
|
8
|
+
set_command_name :'library-forms'
|
9
|
+
register_subcommands :list, :get, :add, :update, :remove
|
10
|
+
|
11
|
+
def connect(opts)
|
12
|
+
@api_client = establish_remote_appliance_connection(opts)
|
13
|
+
@library_instance_types_interface = @api_client.library_instance_types
|
14
|
+
@provision_types_interface = @api_client.provision_types
|
15
|
+
@option_types_interface = @api_client.option_types
|
16
|
+
@option_type_forms_interface = @api_client.option_type_forms
|
17
|
+
end
|
18
|
+
|
19
|
+
def handle(args)
|
20
|
+
handle_subcommand(args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def list(args)
|
24
|
+
options = {}
|
25
|
+
params = {}
|
26
|
+
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
27
|
+
opts.banner = subcommand_usage()
|
28
|
+
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
29
|
+
add_query_parameter(params, 'labels', parse_labels(val))
|
30
|
+
end
|
31
|
+
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
32
|
+
add_query_parameter(params, 'allLabels', parse_labels(val))
|
33
|
+
end
|
34
|
+
build_standard_list_options(opts, options)
|
35
|
+
opts.footer = "List option forms."
|
36
|
+
end
|
37
|
+
optparse.parse!(args)
|
38
|
+
# verify_args!(args:args, optparse:optparse, count:0)
|
39
|
+
if args.count > 0
|
40
|
+
options[:phrase] = args.join(" ")
|
41
|
+
end
|
42
|
+
connect(options)
|
43
|
+
params.merge!(parse_list_options(options))
|
44
|
+
@option_type_forms_interface.setopts(options)
|
45
|
+
if options[:dry_run]
|
46
|
+
print_dry_run @option_type_forms_interface.dry.list(params)
|
47
|
+
return
|
48
|
+
end
|
49
|
+
json_response = @option_type_forms_interface.list(params)
|
50
|
+
render_response(json_response, options, rest_list_key) do
|
51
|
+
option_type_forms = json_response[rest_list_key]
|
52
|
+
subtitles = []
|
53
|
+
subtitles += parse_list_subtitles(options)
|
54
|
+
print_h1 "Morpheus Forms", subtitles, options
|
55
|
+
if option_type_forms.empty?
|
56
|
+
print cyan,"No forms found.",reset,"\n"
|
57
|
+
else
|
58
|
+
rows = option_type_forms.collect do |option_type_form|
|
59
|
+
{
|
60
|
+
id: option_type_form['id'],
|
61
|
+
name: option_type_form['name'],
|
62
|
+
code: option_type_form['code'],
|
63
|
+
labels: option_type_form['labels'],
|
64
|
+
description: option_type_form['description'],
|
65
|
+
}
|
66
|
+
end
|
67
|
+
columns = [
|
68
|
+
:id,
|
69
|
+
:name,
|
70
|
+
:code,
|
71
|
+
{:labels => {:display_method => lambda {|it| format_list(it[:labels], '', 3) rescue '' }}},
|
72
|
+
:description,
|
73
|
+
]
|
74
|
+
print cyan
|
75
|
+
print as_pretty_table(rows, columns, options)
|
76
|
+
print reset
|
77
|
+
print_results_pagination(json_response)
|
78
|
+
end
|
79
|
+
print reset,"\n"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def get(args)
|
84
|
+
options = {}
|
85
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
86
|
+
opts.banner = subcommand_usage("[form]")
|
87
|
+
build_standard_get_options(opts, options)
|
88
|
+
opts.footer = <<-EOT
|
89
|
+
Get details about a form.
|
90
|
+
[form] is required. This is the name or id of a form.
|
91
|
+
EOT
|
92
|
+
end
|
93
|
+
optparse.parse!(args)
|
94
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
95
|
+
connect(options)
|
96
|
+
id_list = parse_id_list(args)
|
97
|
+
return run_command_for_each_arg(id_list) do |arg|
|
98
|
+
_get(arg, options)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def _get(id, options)
|
103
|
+
params = {}
|
104
|
+
params.merge!(parse_query_options(options))
|
105
|
+
if id.to_s !~ /\A\d{1,}\Z/
|
106
|
+
# option_type_form = find_by_name(:option_type_form, id)
|
107
|
+
option_type_form = find_option_type_form_by_name_or_id(id)
|
108
|
+
return 1, "Form not found" if option_type_form.nil?
|
109
|
+
id = option_type_form['id']
|
110
|
+
end
|
111
|
+
@option_type_forms_interface.setopts(options)
|
112
|
+
if options[:dry_run]
|
113
|
+
print_dry_run @option_type_forms_interface.dry.get(id.to_i, params)
|
114
|
+
return
|
115
|
+
end
|
116
|
+
json_response = @option_type_forms_interface.get(id.to_i, params)
|
117
|
+
render_response(json_response, options, rest_object_key) do
|
118
|
+
option_type_form = json_response[rest_object_key]
|
119
|
+
print_h1 "Form Details", options
|
120
|
+
print cyan
|
121
|
+
print_description_list({
|
122
|
+
"ID" => 'id',
|
123
|
+
"Name" => 'name',
|
124
|
+
"Code" => 'code',
|
125
|
+
"Description" => 'description',
|
126
|
+
"Labels" => lambda {|it| format_list(it['labels']) },
|
127
|
+
}, option_type_form, options)
|
128
|
+
|
129
|
+
# show option types
|
130
|
+
print_h2 "Form Inputs"
|
131
|
+
#print format_option_types_table(option_type_form['options'], options, nil, true)
|
132
|
+
print format_option_types_table(option_type_form['options'], options, 'config.customOptions')
|
133
|
+
print reset,"\n"
|
134
|
+
|
135
|
+
# show Field Groups
|
136
|
+
field_groups = option_type_form['fieldGroups']
|
137
|
+
if field_groups && field_groups.size > 0
|
138
|
+
field_groups.each do |field_group|
|
139
|
+
print_h2 "#{field_group['name']}"
|
140
|
+
#print format_option_types_table(field_group['options'], options, nil, true)
|
141
|
+
if field_group['options'] && !field_group['options'].empty?
|
142
|
+
print format_option_types_table(field_group['options'], options, 'config.customOptions')
|
143
|
+
print reset,"\n"
|
144
|
+
else
|
145
|
+
print cyan, "This field group is empty", reset, "\n\n"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def add(args)
|
153
|
+
options = {}
|
154
|
+
my_option_types = nil
|
155
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
156
|
+
opts.banner = subcommand_usage("[name] [options]")
|
157
|
+
build_option_type_options(opts, options, new_option_type_form_option_types())
|
158
|
+
# maybe use --inputs for less confusion
|
159
|
+
opts.on('--options OPTIONS', String, "List of option type inputs to add to the form, this list can include full JSON objects or just the id to use an existing option eg. --options '[5,6,7,{\"fieldName\":\"input\",\"fieldLabel\":\"Input\"}]'") do |val|
|
160
|
+
val = "[#{val.strip}]" unless val.strip.to_s[0] == '[' && val.strip.to_s[-1] == ']'
|
161
|
+
begin
|
162
|
+
options[:selected_options] = JSON.parse(val)
|
163
|
+
rescue
|
164
|
+
raise_command_error "Failed to parse --options value '#{val}' as JSON"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
build_standard_add_options(opts, options)
|
168
|
+
opts.footer = "Create a new form."
|
169
|
+
end
|
170
|
+
optparse.parse!(args)
|
171
|
+
verify_args!(args:args, optparse:optparse, max:1)
|
172
|
+
if args.count == 1
|
173
|
+
options[:options]['name'] = args[0]
|
174
|
+
end
|
175
|
+
connect(options)
|
176
|
+
parse_payload(options, rest_object_key) do |payload|
|
177
|
+
form_payload = prompt_new_form(options)
|
178
|
+
#form_payload.deep_compact!.booleanize! # remove empty values and convert checkbox "on" and "off" to true and false
|
179
|
+
payload.deep_merge!({rest_object_key => form_payload})
|
180
|
+
# cleanup payload
|
181
|
+
# remove transient option params used for prompting for list of inputs
|
182
|
+
payload[rest_object_key].keys.each do |k|
|
183
|
+
if k == "option" || k.to_s =~ /^option\d+$/ || k == "fieldGroup" || k.to_s =~ /^fieldGroup\d+$/
|
184
|
+
payload[rest_object_key].delete(k)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
execute_api(@option_type_forms_interface, :create, [], options, rest_object_key) do |json_response|
|
189
|
+
form = json_response[rest_object_key]
|
190
|
+
print_green_success "Added form #{form['name']}"
|
191
|
+
_get(form['id'], options)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def update(args)
|
196
|
+
options = {}
|
197
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
198
|
+
opts.banner = subcommand_usage("[form] [options]")
|
199
|
+
build_option_type_options(opts, options, update_option_type_form_option_types())
|
200
|
+
build_standard_update_options(opts, options)
|
201
|
+
opts.footer = <<-EOT
|
202
|
+
Update a form.
|
203
|
+
[form] is required. This is the name or id of a form.
|
204
|
+
EOT
|
205
|
+
end
|
206
|
+
optparse.parse!(args)
|
207
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
208
|
+
connect(options)
|
209
|
+
form = find_option_type_form_by_name_or_id(args[0])
|
210
|
+
return 1, "Form not found" if form.nil?
|
211
|
+
parse_payload(options, rest_object_key) do |payload|
|
212
|
+
option_types = update_option_type_form_option_types()
|
213
|
+
v_prompt = no_prompt(option_types, options)
|
214
|
+
#v_prompt.deep_compact!.booleanize! # remove empty values and convert checkbox "on" and "off" to true and false
|
215
|
+
payload.deep_merge!({rest_object_key => v_prompt})
|
216
|
+
if payload[rest_object_key].empty? # || options[:no_prompt]
|
217
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
execute_api(@option_type_forms_interface, :update, [form['id']], options, 'backup') do |json_response|
|
221
|
+
form = json_response[rest_object_key]
|
222
|
+
print_green_success "Updated form #{form['name']}"
|
223
|
+
_get(form['id'], options.merge(params:{}))
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def remove(args)
|
228
|
+
options = {}
|
229
|
+
params = {}
|
230
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
231
|
+
opts.banner = subcommand_usage("[form]")
|
232
|
+
build_standard_remove_options(opts, options)
|
233
|
+
opts.footer = <<-EOT
|
234
|
+
Delete a form.
|
235
|
+
[form] is required. This is the name or id of a form.
|
236
|
+
EOT
|
237
|
+
end
|
238
|
+
optparse.parse!(args)
|
239
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
240
|
+
connect(options)
|
241
|
+
form = find_option_type_form_by_name_or_id(args[0])
|
242
|
+
return 1, "Form not found" if form.nil?
|
243
|
+
parse_options(options, params)
|
244
|
+
confirm!("Are you sure you want to delete the form #{form['name']}?", options)
|
245
|
+
execute_api(@option_type_forms_interface, :destroy, [form['id']], options) do |json_response|
|
246
|
+
print_green_success "Removed form #{form['name']}"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
private
|
251
|
+
|
252
|
+
def rest_list_key
|
253
|
+
option_type_form_list_key
|
254
|
+
end
|
255
|
+
|
256
|
+
def rest_object_key
|
257
|
+
option_type_form_object_key
|
258
|
+
end
|
259
|
+
|
260
|
+
def option_type_form_list_key
|
261
|
+
"optionTypeForms"
|
262
|
+
end
|
263
|
+
|
264
|
+
def option_type_form_object_key
|
265
|
+
"optionTypeForm"
|
266
|
+
end
|
267
|
+
|
268
|
+
def find_option_type_form_by_name_or_id(val)
|
269
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
270
|
+
return find_option_type_form_by_id(val)
|
271
|
+
else
|
272
|
+
return find_option_type_form_by_name(val)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def find_option_type_form_by_id(id)
|
277
|
+
begin
|
278
|
+
json_response = @option_type_forms_interface.get(id.to_i)
|
279
|
+
return json_response[option_type_form_object_key]
|
280
|
+
rescue RestClient::Exception => e
|
281
|
+
if e.response && e.response.code == 404
|
282
|
+
print_red_alert "Form not found by id #{id}"
|
283
|
+
return nil
|
284
|
+
else
|
285
|
+
raise e
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def find_option_type_form_by_name(name)
|
291
|
+
json_results = @option_type_forms_interface.list({name: name.to_s})
|
292
|
+
records = json_results[option_type_form_list_key]
|
293
|
+
if json_results['optionTypeForms'].empty?
|
294
|
+
print_red_alert "Form not found by name '#{name}'"
|
295
|
+
return nil
|
296
|
+
elsif records.size > 1
|
297
|
+
print_red_alert "More than one #{label.downcase} found by name '#{val}'"
|
298
|
+
print_error "\n"
|
299
|
+
puts_error as_pretty_table(records, [:id, :name], {color:red})
|
300
|
+
print_red_alert "Try using ID instead"
|
301
|
+
print_error reset,"\n"
|
302
|
+
return nil
|
303
|
+
end
|
304
|
+
option_type_form = json_results['optionTypeForms'][0]
|
305
|
+
return option_type_form
|
306
|
+
end
|
307
|
+
|
308
|
+
# def get_available_option_list_types
|
309
|
+
# [
|
310
|
+
# {'name' => 'REST', 'value' => 'rest'},
|
311
|
+
# {'name' => 'Morpheus Api', 'value' => 'api'},
|
312
|
+
# {'name' => 'LDAP', 'value' => 'ldap'},
|
313
|
+
# {'name' => 'Manual', 'value' => 'manual'}
|
314
|
+
# ]
|
315
|
+
# end
|
316
|
+
|
317
|
+
# def find_option_list_type(code)
|
318
|
+
# get_available_option_list_types.find {|it| code == it['value'] || code == it['name'] }
|
319
|
+
# end
|
320
|
+
|
321
|
+
def new_option_type_form_option_types()
|
322
|
+
[
|
323
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Form name'},
|
324
|
+
{'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'required' => true, 'description' => 'Unique form code'},
|
325
|
+
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text'},
|
326
|
+
{'shorthand' => '-l', 'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text', 'required' => false, 'noPrompt' => true, 'processValue' => lambda {|val| parse_labels(val) }},
|
327
|
+
]
|
328
|
+
end
|
329
|
+
|
330
|
+
def update_option_type_form_option_types()
|
331
|
+
list = new_option_type_form_option_types()
|
332
|
+
list.each {|it|
|
333
|
+
it.delete('required')
|
334
|
+
it.delete('defaultValue')
|
335
|
+
it.delete('skipSingleOption')
|
336
|
+
}
|
337
|
+
list
|
338
|
+
end
|
339
|
+
|
340
|
+
# CLI Form builder
|
341
|
+
|
342
|
+
def prompt_new_form(options)
|
343
|
+
form_payload = {}
|
344
|
+
form_payload = prompt(new_option_type_form_option_types(), options)
|
345
|
+
# prompt for options
|
346
|
+
form_payload['options'] = prompt_new_form_options(options)
|
347
|
+
form_payload['fieldGroups'] = prompt_new_field_groups(options)
|
348
|
+
|
349
|
+
return form_payload
|
350
|
+
end
|
351
|
+
|
352
|
+
# prompts user to define a list of new option types
|
353
|
+
# returns array of option type objects {'fieldName' => "input1", 'fieldLabel' => "Input 1"}
|
354
|
+
def prompt_new_form_options(options={}, field_group_context=nil)
|
355
|
+
#puts "Source Headers:"
|
356
|
+
no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
|
357
|
+
option_types = []
|
358
|
+
i = 0
|
359
|
+
field_context = "option#{i+1}"
|
360
|
+
context_value_map = options[:options] # should just be options or options.deep_merge(options[:options] || {})
|
361
|
+
if field_group_context
|
362
|
+
context_value_map = context_value_map ? (context_value_map[field_group_context] || {}) : {}
|
363
|
+
end
|
364
|
+
selected_options = options[:selected_options] || [] # user passing in --options '[42,{"fieldName":"foo","fieldLabel":"Foo"}]'
|
365
|
+
# puts "selected_options: #{selected_options.inspect}"
|
366
|
+
has_another_option_type = context_value_map[field_context] || selected_options[0]
|
367
|
+
add_another_option_type = has_another_option_type || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add an Input?", {default: true}))
|
368
|
+
while add_another_option_type do
|
369
|
+
print_h2 "Input #{i+1}"
|
370
|
+
full_context = [field_group_context, field_context].select {|it| it.to_s != "" }.join('.')
|
371
|
+
option_type = prompt_new_option_type(options, selected_options[i], full_context)
|
372
|
+
print "\n"
|
373
|
+
option_types << option_type
|
374
|
+
i += 1
|
375
|
+
field_context = "option#{i+1}"
|
376
|
+
has_another_option_type = context_value_map && context_value_map[field_context]
|
377
|
+
add_another_option_type = has_another_option_type || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another Input?", {default: false}))
|
378
|
+
end
|
379
|
+
|
380
|
+
return option_types
|
381
|
+
end
|
382
|
+
|
383
|
+
def prompt_new_option_type(options, selected_value=nil, field_context=nil)
|
384
|
+
option_type = {}
|
385
|
+
if selected_value
|
386
|
+
if selected_value.is_a?(Hash)
|
387
|
+
option_type = selected_value
|
388
|
+
else
|
389
|
+
existing_option_type = find_option_type_by_name_or_id(selected_value)
|
390
|
+
raise_command_error "Option Type not found for '#{selected_value}'" if existing_option_type.nil?
|
391
|
+
option_type['id'] = existing_option_type['id']
|
392
|
+
end
|
393
|
+
return option_type
|
394
|
+
end
|
395
|
+
# Use Existing Input? then skip all other inputs
|
396
|
+
# context_value_map = field_context ? (options[:options][field_context] || {}) : {}
|
397
|
+
context_value_map = field_context ? get_object_value(options[:options], field_context) : options[:options]
|
398
|
+
if context_value_map && context_value_map['id']
|
399
|
+
context_value_map['existing'] = "on"
|
400
|
+
end
|
401
|
+
use_existing = prompt_value({'fieldContext' => field_context, 'fieldName' => 'existing', 'fieldLabel' => 'Use Existing', 'type' => 'checkbox', 'required' => true, 'defaultValue' => false, 'description' => "Use an existing input instead of customizing a new one for this form"}, options)
|
402
|
+
if use_existing.to_s == "on" || use_existing.to_s == "yes" || use_existing.to_s == "true"
|
403
|
+
existing_id = prompt_value({'fieldContext' => field_context, 'fieldName' => 'id', 'fieldLabel' => 'Existing Input', 'type' => 'select', 'optionSource' => 'optionTypes', 'required' => true, 'description' => "Choose an existing input"}, options)
|
404
|
+
option_type['id'] = existing_id.to_i
|
405
|
+
else
|
406
|
+
# prompt for a new option type
|
407
|
+
option_types = new_form_input_option_types
|
408
|
+
option_types.each {|it| it['fieldContext'] = field_context }
|
409
|
+
results = prompt(option_types, options)
|
410
|
+
results.booleanize! #.deep_compact!
|
411
|
+
# option_type = field_context ? results[field_context] : results
|
412
|
+
option_type = field_context ? get_object_value(results, field_context) : results
|
413
|
+
end
|
414
|
+
return option_type
|
415
|
+
end
|
416
|
+
|
417
|
+
# prompts user to define a list of field groups and their options
|
418
|
+
# returns array of option type objects {'fieldName' => "input1", 'fieldLabel' => "Input 1"}
|
419
|
+
def prompt_new_field_groups(options={})
|
420
|
+
#puts "Source Headers:"
|
421
|
+
no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
|
422
|
+
field_groups = []
|
423
|
+
selected_field_groups = options[:selected_field_groups] || [] # user passing in --options '[42,{"fieldName":"foo","fieldLabel":"Foo"}]'
|
424
|
+
# puts "selected_field_groups: #{selected_field_groups.inspect}"
|
425
|
+
i = 0
|
426
|
+
field_context = "fieldGroup#{i+1}"
|
427
|
+
has_another_field_group = options[:options][field_context] || selected_field_groups[0]
|
428
|
+
add_another_field_group = has_another_field_group || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add a Field Group?", {default: false}))
|
429
|
+
while add_another_field_group do
|
430
|
+
print_h2 "Field Group #{i+1}"
|
431
|
+
field_group = prompt_new_field_group(options, selected_field_groups[i], field_context)
|
432
|
+
print "\n"
|
433
|
+
field_groups << field_group
|
434
|
+
i += 1
|
435
|
+
field_context = "fieldGroup#{i+1}"
|
436
|
+
has_another_field_group = options[:options] && options[:options][field_context]
|
437
|
+
add_another_field_group = has_another_field_group || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another Field Group?", {default: false}))
|
438
|
+
end
|
439
|
+
|
440
|
+
return field_groups
|
441
|
+
end
|
442
|
+
|
443
|
+
def prompt_new_field_group(options, selected_value=nil, field_context=nil)
|
444
|
+
field_group = {}
|
445
|
+
# if selected_value
|
446
|
+
# if selected_value.is_a?(Hash)
|
447
|
+
# field_group = selected_value
|
448
|
+
# end
|
449
|
+
# return field_group
|
450
|
+
# end
|
451
|
+
|
452
|
+
# prompt for a field group
|
453
|
+
field_group_option_types = [
|
454
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'This field sets the name attribute for the field group.', 'defaultValue' => 'new fieldgroup'},
|
455
|
+
# {'fieldName' => 'code', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'This field sets the code attribute for the field group.', 'defaultValue' => SecureRandom.uuid},
|
456
|
+
# {'fieldName' => 'localizedName', 'fieldLabel' => 'Localized Name', 'type' => 'typeahead', 'optionSource' => 'messageCodes', 'description' => 'i18n code for the name'},
|
457
|
+
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'description' => 'This field sets the name attribute for the input.'},
|
458
|
+
{'fieldName' => 'collapsible', 'fieldLabel' => 'Collapsible', 'type' => 'checkbox', 'defaultValue' => true, 'description' => 'Field group is collapsible'},
|
459
|
+
{'fieldName' => 'defaultCollapsed', 'fieldLabel' => 'Default Collapsed', 'type' => 'checkbox', 'defaultValue' => true, 'description' => 'Collapse field group by default'},
|
460
|
+
{'fieldName' => 'visibleOnCode', 'fieldLabel' => 'Visibility Field', 'type' => 'text', 'description' => 'A fieldName that will trigger visibility of this field group'},
|
461
|
+
]
|
462
|
+
field_group_option_types.each {|it| it['fieldContext'] = field_context }
|
463
|
+
results = prompt(field_group_option_types, options)
|
464
|
+
results.booleanize! #.deep_compact!
|
465
|
+
field_group = field_context ? results[field_context] : results
|
466
|
+
field_group['code'] = SecureRandom.uuid unless field_group['code']
|
467
|
+
# prompt for options
|
468
|
+
field_group['options'] = prompt_new_form_options(options, field_context)
|
469
|
+
|
470
|
+
return field_group
|
471
|
+
end
|
472
|
+
|
473
|
+
# not used yet
|
474
|
+
def prompt_edit_form(form, params, options)
|
475
|
+
form_payload = {}
|
476
|
+
selected_options = options[:selected_options] || [] # user passing in --options [42,{"fieldName":"foo","fieldLabel":"Foo"}]
|
477
|
+
# this prompts for an action for each option on the list: keep, edit, remove
|
478
|
+
new_options = []
|
479
|
+
Array(form['options']).each_with_index do |option_type, i|
|
480
|
+
|
481
|
+
field_context = i == 0 ? "option" : "option#{i+1}"
|
482
|
+
results = prompt_edit_option_type(option_type, options, selected_options[i], field_context)
|
483
|
+
if results.nil?
|
484
|
+
# deleted
|
485
|
+
next
|
486
|
+
end
|
487
|
+
new_options << results[field_context]
|
488
|
+
end
|
489
|
+
form_payload['options'] = new_options
|
490
|
+
|
491
|
+
# todo: fieldGroups
|
492
|
+
Array(form['fieldGroups']).each do |option_type|
|
493
|
+
end
|
494
|
+
|
495
|
+
|
496
|
+
return form_payload
|
497
|
+
end
|
498
|
+
|
499
|
+
def prompt_edit_option_type(current_option_type, options, selected_value=nil, field_context=nil)
|
500
|
+
option_type = {'id' => current_option_type['id']}
|
501
|
+
# if selected_value
|
502
|
+
# if selected_value.is_a?(Hash)
|
503
|
+
# option_type = selected_value
|
504
|
+
# else
|
505
|
+
# existing_option_type = find_option_type_by_name_or_id(selected_value)
|
506
|
+
# raise_command_error "Option Type not found for '#{selected_value}'" if existing_option_type.nil?
|
507
|
+
# option_type['id'] = existing_option_type['id']
|
508
|
+
# end
|
509
|
+
# return option_type
|
510
|
+
# end
|
511
|
+
|
512
|
+
action_options = [{'name' => 'Modify', 'value' => 'modify'}, {'name' => 'Keep', 'value' => 'keep'}, {'name' => 'Delete', 'value' => 'delete'}]
|
513
|
+
v_prompt = prompt([{'fieldContext' => field_context, 'fieldName' => 'action', 'type' => 'select', 'fieldLabel' => "Modify/Keep/Delete Input '#{current_option_type['fieldLabel']}' (ID: #{current_option_type['id']})", 'selectOptions' => action_options, 'required' => true, 'defaultValue' => 'keep', 'description' => 'Modify, Keep or Remove form input?'}], options[:options])
|
514
|
+
action = v_prompt[field_context]['action']
|
515
|
+
|
516
|
+
if action == 'delete'
|
517
|
+
# deleted input is just excluded from list
|
518
|
+
option_type = nil
|
519
|
+
elsif action == 'keep'
|
520
|
+
# no changes
|
521
|
+
elsif action == 'modify'
|
522
|
+
# Modify existing input
|
523
|
+
|
524
|
+
# Use Existing Input?
|
525
|
+
# If yes then then skip all other inputs
|
526
|
+
if options[:options][field_context] && options[:options][field_context]['id']
|
527
|
+
options[:options][field_context]['existing'] = "on"
|
528
|
+
end
|
529
|
+
use_existing = prompt_value({'fieldContext' => field_context, 'fieldName' => 'existing', 'fieldLabel' => 'Use Existing', 'type' => 'checkbox', 'required' => true, 'defaultValue' => (current_option_type['formField'] ? false : true), 'description' => "Use an existing input instead of customizing a new one for this form"}, options)
|
530
|
+
if use_existing.to_s == "on" || use_existing.to_s == "yes" || use_existing.to_s == "true"
|
531
|
+
existing_id = prompt_value({'fieldContext' => field_context, 'fieldName' => 'id', 'fieldLabel' => 'Existing Input', 'type' => 'select', 'optionSource' => 'optionTypes', 'required' => true, 'defaultValue' => (current_option_type['formField'] ? nil : current_option_type['name']), 'description' => "Choose an existing input"}, options)
|
532
|
+
option_type['id'] = existing_id.to_i
|
533
|
+
else
|
534
|
+
# prompt to edit the existing option type
|
535
|
+
option_types = update_form_input_option_types
|
536
|
+
option_types.each {|it| it['fieldContext'] = field_context }
|
537
|
+
results = prompt(option_types, options)
|
538
|
+
results.booleanize! #.deep_compact!
|
539
|
+
if field_context
|
540
|
+
results = results[field_context]
|
541
|
+
end
|
542
|
+
option_type.deep_merge!(results) if results
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
return option_type
|
547
|
+
end
|
548
|
+
|
549
|
+
def new_form_input_option_types()
|
550
|
+
[
|
551
|
+
{'code' => 'optionType.type', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_form_input_types(), 'defaultValue' => 'text', 'required' => true},
|
552
|
+
{'fieldName' => 'optionList', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'optionTypeLists', 'required' => true, 'dependsOnCode' => 'optionType.type:select', 'description' => "The Option List to be the source of options when type is 'select'."},
|
553
|
+
{'fieldName' => 'fieldLabel', 'fieldLabel' => 'Field Label', 'type' => 'text', 'required' => true, 'description' => 'This is the input label that shows typically to the left of a custom option.'},
|
554
|
+
{'fieldName' => 'fieldCode', 'fieldLabel' => 'Localized Label', 'type' => 'typeahead', 'optionSource' => 'messageCodes', 'description' => 'i18n code for the label'},
|
555
|
+
{'fieldName' => 'fieldName', 'fieldLabel' => 'Field Name', 'type' => 'text', 'required' => true, 'description' => 'This field sets the name attribute for the input.'},
|
556
|
+
{'fieldName' => 'defaultValue', 'fieldLabel' => 'Default Value', 'type' => 'text', :preprocesser => lambda do |option_type, api_client, api_params|
|
557
|
+
input_type = option_type['fieldContext'] ? api_params[option_type['fieldContext']]['type'] : api_params['type']
|
558
|
+
if input_type == 'checkbox'
|
559
|
+
# option_type.merge!({'type' => 'select', 'selectOptions' => [{"name" => "On", "value" => "on"},{"name" => "Off", "value" => "off"}]})
|
560
|
+
option_type.merge!({'type' => 'checkbox'})
|
561
|
+
end
|
562
|
+
end
|
563
|
+
},
|
564
|
+
{'fieldName' => 'placeHolder', 'fieldLabel' => 'Placeholder', 'type' => 'text', 'description' => 'Text that is displayed when the field is empty'},
|
565
|
+
{'fieldName' => 'helpBlock', 'fieldLabel' => 'Help Block', 'type' => 'text', 'description' => 'This is the explaination of the input that shows typically underneath the option.'},
|
566
|
+
{'fieldName' => 'helpBlockFieldCode', 'fieldLabel' => 'Localized Help Block', 'type' => 'typeahead', 'optionSource' => 'messageCodes', 'description' => 'i18n code for the help block'},
|
567
|
+
{'fieldName' => 'required', 'fieldLabel' => 'Required', 'type' => 'checkbox', 'defaultValue' => false},
|
568
|
+
{'fieldName' => 'exportMeta', 'fieldLabel' => 'Export As Tag', 'type' => 'checkbox', 'defaultValue' => false, 'description' => 'Export as Tag'},
|
569
|
+
{'fieldName' => 'displayValueOnDetails', 'fieldLabel' => 'Display Value On Details', 'type' => 'checkbox', 'defaultValue' => false},
|
570
|
+
{'fieldName' => 'isLocked', 'fieldLabel' => 'Locked', 'type' => 'checkbox', 'defaultValue' => false},
|
571
|
+
{'fieldName' => 'isHidden', 'fieldLabel' => 'Hidden', 'type' => 'checkbox', 'defaultValue' => false},
|
572
|
+
{'fieldName' => 'excludeFromSearch', 'fieldLabel' => 'Exclude From Search', 'type' => 'checkbox', 'defaultValue' => false},
|
573
|
+
# Advanced
|
574
|
+
{'fieldName' => 'dependsOnCode', 'fieldLabel' => 'Dependent Field', 'type' => 'text', 'description' => 'A fieldName that will trigger reloading this input'},
|
575
|
+
{'fieldName' => 'visibleOnCode', 'fieldLabel' => 'Visibility Field', 'type' => 'text', 'description' => 'A fieldName that will trigger visibility of this input'},
|
576
|
+
{'fieldName' => 'verifyPattern', 'fieldLabel' => 'Verify Pattern', 'type' => 'text', 'dependsOnCode' => 'optionType.type:text', 'description' => 'A regexp string that validates the input, use (?i) to make the matcher case insensitive'},
|
577
|
+
{'fieldName' => 'requireOnCode', 'fieldLabel' => 'Require Field', 'type' => 'text', 'description' => 'A fieldName that will trigger required attribute of this input'},
|
578
|
+
]
|
579
|
+
end
|
580
|
+
|
581
|
+
def update_form_input_option_types()
|
582
|
+
list = new_option_type_form_option_types()
|
583
|
+
list.each {|it|
|
584
|
+
it.delete('required')
|
585
|
+
it.delete('defaultValue')
|
586
|
+
it.delete('skipSingleOption')
|
587
|
+
}
|
588
|
+
list
|
589
|
+
end
|
590
|
+
|
591
|
+
def get_available_form_input_types()
|
592
|
+
{
|
593
|
+
checkbox: "Checkbox",
|
594
|
+
hidden: "Hidden",
|
595
|
+
number: "Number",
|
596
|
+
password: "Password",
|
597
|
+
radio: "Radio",
|
598
|
+
select: "Select List",
|
599
|
+
text: "Text",
|
600
|
+
textarea: "Textarea",
|
601
|
+
byteSize: "Byte Size",
|
602
|
+
'code-editor': "Code Editor",
|
603
|
+
fileContent: "File Content",
|
604
|
+
logoSelector: "Icon Picker",
|
605
|
+
keyValue: "Key Value",
|
606
|
+
textArray: "Text Array",
|
607
|
+
typeahead: "Typeahead",
|
608
|
+
cloud: "Cloud",
|
609
|
+
diskManager: "Disks",
|
610
|
+
environment: "Environment",
|
611
|
+
ports: "Exposed Ports",
|
612
|
+
group: "Group",
|
613
|
+
'instances-input': "Instances",
|
614
|
+
layout: "Layout",
|
615
|
+
networkManager: "Networks",
|
616
|
+
plan: "Plan",
|
617
|
+
resourcePool: "Resource Pool",
|
618
|
+
secGroup: "Security Groups",
|
619
|
+
'servers-input': "Servers",
|
620
|
+
'virtual-image': "Virtual Image",
|
621
|
+
vmwFolders: "Vmw Folders",
|
622
|
+
httpHeader: "Headers",
|
623
|
+
}.collect {|k,v| {'name' => v.to_s, 'value' => k.to_s } }
|
624
|
+
end
|
625
|
+
end
|