morpheus-cli 3.6.31 → 3.6.32
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/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/apps_interface.rb +15 -0
- data/lib/morpheus/api/clouds_interface.rb +15 -0
- data/lib/morpheus/api/environments_interface.rb +54 -0
- data/lib/morpheus/api/groups_interface.rb +15 -0
- data/lib/morpheus/api/instances_interface.rb +15 -0
- data/lib/morpheus/api/servers_interface.rb +14 -0
- data/lib/morpheus/api/wiki_interface.rb +54 -0
- data/lib/morpheus/cli.rb +2 -0
- data/lib/morpheus/cli/apps.rb +209 -21
- data/lib/morpheus/cli/clouds.rb +209 -1
- data/lib/morpheus/cli/environments_command.rb +406 -0
- data/lib/morpheus/cli/groups.rb +207 -0
- data/lib/morpheus/cli/hosts.rb +208 -0
- data/lib/morpheus/cli/instances.rb +350 -36
- data/lib/morpheus/cli/key_pairs.rb +3 -3
- data/lib/morpheus/cli/library_container_types_command.rb +10 -5
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +28 -3
- data/lib/morpheus/cli/networks_command.rb +12 -0
- data/lib/morpheus/cli/recent_activity_command.rb +9 -3
- data/lib/morpheus/cli/remote.rb +1 -1
- data/lib/morpheus/cli/reports_command.rb +60 -11
- data/lib/morpheus/cli/tenants_command.rb +43 -46
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/wiki_command.rb +461 -0
- metadata +6 -2
@@ -94,10 +94,10 @@ class Morpheus::Cli::KeyPairs
|
|
94
94
|
|
95
95
|
@key_pairs_interface.setopts(options)
|
96
96
|
if options[:dry_run]
|
97
|
-
if
|
98
|
-
print_dry_run @key_pairs_interface.dry.get(account_id,
|
97
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
98
|
+
print_dry_run @key_pairs_interface.dry.get(account_id, args[0])
|
99
99
|
else
|
100
|
-
print_dry_run @key_pairs_interface.dry.list(account_id, {name:
|
100
|
+
print_dry_run @key_pairs_interface.dry.list(account_id, {name: args[0]})
|
101
101
|
end
|
102
102
|
return 0
|
103
103
|
end
|
@@ -380,6 +380,10 @@ class Morpheus::Cli::LibraryContainerTypesCommand
|
|
380
380
|
end
|
381
381
|
|
382
382
|
end
|
383
|
+
# avoid API bug in 3.6.3
|
384
|
+
if payload['containerType']
|
385
|
+
payload['containerType']['config'] ||= {}
|
386
|
+
end
|
383
387
|
@library_container_types_interface.setopts(options)
|
384
388
|
if options[:dry_run]
|
385
389
|
print_dry_run @library_container_types_interface.dry.create(layout_id, payload)
|
@@ -445,14 +449,16 @@ class Morpheus::Cli::LibraryContainerTypesCommand
|
|
445
449
|
return 1
|
446
450
|
end
|
447
451
|
# construct payload
|
452
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
448
453
|
payload = nil
|
449
454
|
if options[:payload]
|
450
455
|
payload = options[:payload]
|
456
|
+
payload.deep_merge!({'containerType' => passed_options}) unless passed_options.empty?
|
451
457
|
else
|
452
|
-
payload = {}
|
458
|
+
payload = {'containerType' => {} }
|
453
459
|
# option_types = update_layout_option_types(instance_type)
|
454
460
|
# params = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
|
455
|
-
payload.deep_merge!(
|
461
|
+
payload.deep_merge!({'containerType' => passed_options}) unless passed_options.empty?
|
456
462
|
|
457
463
|
# ENVIRONMENT VARIABLES
|
458
464
|
if evars
|
@@ -475,9 +481,8 @@ class Morpheus::Cli::LibraryContainerTypesCommand
|
|
475
481
|
# prompt
|
476
482
|
end
|
477
483
|
|
478
|
-
if params.empty?
|
479
|
-
|
480
|
-
exit 1
|
484
|
+
if params.empty? && passed_options.empty?
|
485
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
481
486
|
end
|
482
487
|
|
483
488
|
# payload = {'containerType' => params}
|
@@ -293,6 +293,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
293
293
|
arbitrary_options.delete('layout')
|
294
294
|
arbitrary_options.delete('servicePlan')
|
295
295
|
arbitrary_options.delete('description')
|
296
|
+
arbitrary_options.delete('environment')
|
296
297
|
arbitrary_options.delete('instanceContext')
|
297
298
|
arbitrary_options.delete('tags')
|
298
299
|
payload.deep_merge!(arbitrary_options)
|
@@ -306,7 +307,12 @@ module Morpheus::Cli::ProvisioningHelper
|
|
306
307
|
payload['instance']['description'] = v_prompt['description'] if !v_prompt['description'].empty?
|
307
308
|
|
308
309
|
# Environment
|
309
|
-
|
310
|
+
if options[:environment]
|
311
|
+
options[:options]['environment'] = options[:environment]
|
312
|
+
elsif options[:options]['instanceContext'] && !options[:options]['environment']
|
313
|
+
options[:options]['environment'] = options[:options]['instanceContext']
|
314
|
+
end
|
315
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => get_available_environments()}], options[:options])
|
310
316
|
payload['instance']['instanceContext'] = v_prompt['instanceContext'] if !v_prompt['instanceContext'].empty?
|
311
317
|
|
312
318
|
# Tags
|
@@ -384,7 +390,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
384
390
|
# prompt for resource pool
|
385
391
|
has_zone_pools = layout["provisionType"] && layout["provisionType"]["id"] && layout["provisionType"]["hasZonePools"]
|
386
392
|
if has_zone_pools
|
387
|
-
resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'optionSource' => 'zonePools', 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.'}],options[:options],api_client,{groupId: group_id, zoneId: cloud_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], planId: service_plan["id"]})
|
393
|
+
resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'optionSource' => 'zonePools', 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.'}],options[:options],api_client,{groupId: group_id, siteId: group_id, zoneId: cloud_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], planId: service_plan["id"], layoutId: layout["id"]})
|
388
394
|
if resource_pool_prompt['config'] && resource_pool_prompt['config']['resourcePoolId']
|
389
395
|
payload['config'] ||= {}
|
390
396
|
payload['config']['resourcePoolId'] = resource_pool_prompt['config']['resourcePoolId']
|
@@ -1185,7 +1191,26 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1185
1191
|
}
|
1186
1192
|
end
|
1187
1193
|
|
1188
|
-
def
|
1194
|
+
def get_available_environments(refresh=false)
|
1195
|
+
if !@available_environments || refresh
|
1196
|
+
begin
|
1197
|
+
option_results = options_interface.options_for_source('environments',{})
|
1198
|
+
@available_environments = option_results['data'].collect {|it|
|
1199
|
+
{"code" => (it["code"] || it["value"]), "name" => it["name"], "value" => it["value"]}
|
1200
|
+
}
|
1201
|
+
rescue RestClient::Exception => e
|
1202
|
+
# if e.response && e.response.code == 404
|
1203
|
+
Morpheus::Logging::DarkPrinter.puts "Unable to determine available environments, using default options" if Morpheus::Logging.debug?
|
1204
|
+
@available_environments = get_static_environments()
|
1205
|
+
# else
|
1206
|
+
# raise e
|
1207
|
+
# end
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
return @available_environments
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
def get_static_environments
|
1189
1214
|
[{'name' => 'Dev', 'value' => 'dev'}, {'name' => 'Test', 'value' => 'qa'}, {'name' => 'Staging', 'value' => 'staging'}, {'name' => 'Production', 'value' => 'production'}]
|
1190
1215
|
end
|
1191
1216
|
|
@@ -36,6 +36,9 @@ class Morpheus::Cli::NetworksCommand
|
|
36
36
|
params = {}
|
37
37
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
38
38
|
opts.banner = subcommand_usage()
|
39
|
+
opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
|
40
|
+
options[:cloud] = val
|
41
|
+
end
|
39
42
|
opts.on('--cidr VALUE', String, "Filter by cidr, matches beginning of value.") do |val|
|
40
43
|
params['cidr'] = val
|
41
44
|
end
|
@@ -46,6 +49,12 @@ class Morpheus::Cli::NetworksCommand
|
|
46
49
|
connect(options)
|
47
50
|
begin
|
48
51
|
params.merge!(parse_list_options(options))
|
52
|
+
cloud = nil
|
53
|
+
if options[:cloud]
|
54
|
+
cloud = find_cloud_by_name_or_id(options[:cloud])
|
55
|
+
return 1 if cloud.nil?
|
56
|
+
params['zoneId'] = cloud['id']
|
57
|
+
end
|
49
58
|
@networks_interface.setopts(options)
|
50
59
|
if options[:dry_run]
|
51
60
|
print_dry_run @networks_interface.dry.list(params)
|
@@ -65,6 +74,9 @@ class Morpheus::Cli::NetworksCommand
|
|
65
74
|
end
|
66
75
|
title = "Morpheus Networks"
|
67
76
|
subtitles = []
|
77
|
+
if cloud
|
78
|
+
subtitles << "Cloud: #{cloud['id']}"
|
79
|
+
end
|
68
80
|
subtitles += parse_list_subtitles(options)
|
69
81
|
print_h1 title, subtitles
|
70
82
|
if networks.empty?
|
@@ -31,12 +31,12 @@ class Morpheus::Cli::RecentActivityCommand
|
|
31
31
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
32
32
|
opts.banner = usage
|
33
33
|
opts.on('--start TIMESTAMP','--start TIMESTAMP', "Start timestamp. Default is 30 days ago.") do |val|
|
34
|
-
options[:start] = parse_time(val).iso8601
|
34
|
+
options[:start] = parse_time(val).utc.iso8601
|
35
35
|
end
|
36
36
|
opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
|
37
|
-
options[:end] = parse_time(val).iso8601
|
37
|
+
options[:end] = parse_time(val).utc.iso8601
|
38
38
|
end
|
39
|
-
build_common_options(opts, options, [:account, :list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
39
|
+
build_common_options(opts, options, [:account, :query, :list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
40
40
|
end
|
41
41
|
optparse.parse!(args)
|
42
42
|
connect(options)
|
@@ -45,6 +45,12 @@ class Morpheus::Cli::RecentActivityCommand
|
|
45
45
|
account_id = account ? account['id'] : nil
|
46
46
|
params = {}
|
47
47
|
params.merge!(parse_list_options(options))
|
48
|
+
if options[:start]
|
49
|
+
params['start'] = options[:start]
|
50
|
+
end
|
51
|
+
if options[:end]
|
52
|
+
params['end'] = options[:end]
|
53
|
+
end
|
48
54
|
@dashboard_interface.setopts(options)
|
49
55
|
if options[:dry_run]
|
50
56
|
print_dry_run @dashboard_interface.dry.recent_activity(account_id, params)
|
data/lib/morpheus/cli/remote.rb
CHANGED
@@ -74,7 +74,7 @@ EOT
|
|
74
74
|
columns = [
|
75
75
|
{:active => {:display_name => "", :display_method => lambda {|it| it[:active] ? "=>" : "" } } },
|
76
76
|
# {:name => {display_method: lambda {|it| it[:active] ? "#{green}#{it[:name]}#{reset}#{cyan}" : it[:name] }, :width => 16 } },
|
77
|
-
{:name => {display_method: lambda {|it| it[:name] }
|
77
|
+
{:name => {display_method: lambda {|it| it[:name] } } },
|
78
78
|
{:url => {display_method: lambda {|it| it[:host] || it[:url] }, :width => 40 } },
|
79
79
|
{:version => lambda {|it| it[:build_version] } },
|
80
80
|
{:status => lambda {|it| format_appliance_status(it, cyan) } },
|
@@ -104,6 +104,9 @@ class Morpheus::Cli::ReportsCommand
|
|
104
104
|
opts.on('--rows', '--rows', "Print Report Data rows too.") do
|
105
105
|
options[:show_data_rows] = true
|
106
106
|
end
|
107
|
+
opts.on('--view', '--view', "View report result in web browser too.") do
|
108
|
+
options[:view_report] = true
|
109
|
+
end
|
107
110
|
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :outfile, :dry_run, :remote])
|
108
111
|
opts.footer = "Get details about a report result." + "\n"
|
109
112
|
+ "[id] is required. This is the id of the report result."
|
@@ -166,11 +169,11 @@ class Morpheus::Cli::ReportsCommand
|
|
166
169
|
if data_width < 0
|
167
170
|
data_wdith = 10
|
168
171
|
end
|
169
|
-
puts as_pretty_table(report_result['rows'], {
|
172
|
+
puts as_pretty_table(report_result['rows'], options[:include_fields] || {
|
170
173
|
"ID" => lambda {|it| it['id'] },
|
171
174
|
"SECTION" => lambda {|it| it['section'] },
|
172
175
|
"DATA" => lambda {|it| truncate_string(it['data'], data_width) }
|
173
|
-
})
|
176
|
+
}, options.merge({:responsive_table => false}))
|
174
177
|
|
175
178
|
else
|
176
179
|
print yellow, "No report data found.", reset, "\n"
|
@@ -193,6 +196,9 @@ class Morpheus::Cli::ReportsCommand
|
|
193
196
|
get(original_args)
|
194
197
|
end
|
195
198
|
end
|
199
|
+
if options[:view_report]
|
200
|
+
view([report_result['id']])
|
201
|
+
end
|
196
202
|
return 0
|
197
203
|
rescue RestClient::Exception => e
|
198
204
|
print_rest_exception(e, options)
|
@@ -218,6 +224,9 @@ class Morpheus::Cli::ReportsCommand
|
|
218
224
|
opts.on('--rows', '--rows', "Print Report Data rows too.") do
|
219
225
|
options[:show_data_rows] = true
|
220
226
|
end
|
227
|
+
opts.on('--view', '--view', "View report result in web browser when it is finished.") do
|
228
|
+
options[:view_report] = true
|
229
|
+
end
|
221
230
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
222
231
|
opts.footer = "Run a report to generate a new result." + "\n" +
|
223
232
|
"[type] is required. This is code of the report type."
|
@@ -257,16 +266,27 @@ class Morpheus::Cli::ReportsCommand
|
|
257
266
|
return 1 if report_type.nil?
|
258
267
|
payload['report']['type'] = report_type['code']
|
259
268
|
|
260
|
-
# Report Types
|
269
|
+
# Report Types tell us what the available filters are...
|
270
|
+
report_option_types = report_type['optionTypes'] || []
|
271
|
+
# pluck out optionTypes like the UI does..
|
272
|
+
metadata_option_type = nil
|
273
|
+
if report_option_types.find {|it| it['fieldName'] == 'metadata' }
|
274
|
+
metadata_option_type = report_option_types.delete_if {|it| it['fieldName'] == 'metadata' }
|
275
|
+
end
|
261
276
|
|
262
|
-
|
263
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'startDate', 'fieldLabel' => 'Start Date', 'type' => 'text', 'required' => false}], options[:options])
|
264
|
-
payload['report']['startDate'] = v_prompt['startDate'] unless v_prompt['startDate'].to_s.empty?
|
277
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(report_option_types, options[:options], @api_client)
|
265
278
|
|
266
|
-
|
267
|
-
|
268
|
-
|
279
|
+
if metadata_option_type
|
280
|
+
if !options[:options]['metadata']
|
281
|
+
metadata_filter = prompt_metadata(options)
|
282
|
+
if metadata_filter && !metadata_filter.empty?
|
283
|
+
payload['report']['metadata'] = metadata_filter
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
269
287
|
|
288
|
+
# payload.deep_merge!({'report' => v_prompt}) unless v_prompt.empty?
|
289
|
+
payload.deep_merge!(v_prompt) unless v_prompt.empty?
|
270
290
|
end
|
271
291
|
|
272
292
|
@reports_interface.setopts(options)
|
@@ -284,6 +304,7 @@ class Morpheus::Cli::ReportsCommand
|
|
284
304
|
print_args = [json_response['reportResult']['id']]
|
285
305
|
print_args << "--refresh" if do_refresh
|
286
306
|
print_args << "--rows" if options[:show_data_rows]
|
307
|
+
print_args << "--view" if options[:view_report]
|
287
308
|
get(print_args)
|
288
309
|
return 0
|
289
310
|
rescue RestClient::Exception => e
|
@@ -296,9 +317,9 @@ class Morpheus::Cli::ReportsCommand
|
|
296
317
|
options = {}
|
297
318
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
298
319
|
opts.banner = subcommand_usage("[id]")
|
299
|
-
build_common_options(opts, options, [:
|
320
|
+
build_common_options(opts, options, [:dry_run, :remote])
|
300
321
|
opts.footer = "View a report result in a web browser" + "\n" +
|
301
|
-
"[id] is required. This is id of the report result."
|
322
|
+
"[id] is required. This is the id of the report result."
|
302
323
|
end
|
303
324
|
optparse.parse!(args)
|
304
325
|
if args.count != 1
|
@@ -590,5 +611,33 @@ class Morpheus::Cli::ReportsCommand
|
|
590
611
|
out
|
591
612
|
end
|
592
613
|
|
614
|
+
# Prompts user for metadata for report filter
|
615
|
+
# returns array of metadata objects {id: null, name: "MYTAG", value: "myvalue"}
|
616
|
+
def prompt_metadata(options={})
|
617
|
+
#puts "Configure Environment Variables:"
|
618
|
+
no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
|
619
|
+
metadata_filter = {}
|
620
|
+
metadata_index = 0
|
621
|
+
has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
|
622
|
+
add_another_metadata = has_another_metadata || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add a metadata tag filter?", {default: false}))
|
623
|
+
while add_another_metadata do
|
624
|
+
field_context = "metadata#{metadata_index}"
|
625
|
+
metadata = {}
|
626
|
+
metadata['id'] = nil
|
627
|
+
metadata_label = metadata_index == 0 ? "Metadata Tag" : "Metadata Tag [#{metadata_index+1}]"
|
628
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Name", 'required' => true, 'description' => 'Metadata Tag Name.', 'defaultValue' => metadata['name']}], options[:options])
|
629
|
+
# todo: metadata.type ?
|
630
|
+
metadata['name'] = v_prompt[field_context]['name'].to_s
|
631
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'value', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Value", 'required' => true, 'description' => 'Metadata Tag Value', 'defaultValue' => metadata['value']}], options[:options])
|
632
|
+
metadata['value'] = v_prompt[field_context]['value'].to_s
|
633
|
+
metadata_filter[metadata['name']] = metadata['value']
|
634
|
+
metadata_index += 1
|
635
|
+
has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
|
636
|
+
add_another_metadata = has_another_metadata || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another metadata tag filter?", {default: false}))
|
637
|
+
end
|
638
|
+
|
639
|
+
return metadata_filter
|
640
|
+
end
|
641
|
+
|
593
642
|
end
|
594
643
|
|
@@ -42,7 +42,7 @@ class Morpheus::Cli::TenantsCommand
|
|
42
42
|
options = {}
|
43
43
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
44
44
|
opts.banner = subcommand_usage()
|
45
|
-
build_common_options(opts, options, [:list, :query, :json, :
|
45
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
46
46
|
opts.footer = "List tenants (accounts)."
|
47
47
|
end
|
48
48
|
optparse.parse!(args)
|
@@ -56,23 +56,21 @@ class Morpheus::Cli::TenantsCommand
|
|
56
56
|
return
|
57
57
|
end
|
58
58
|
json_response = @accounts_interface.list(params)
|
59
|
+
render_result = render_with_format(json_response, options, 'accounts')
|
60
|
+
return 0 if render_result
|
59
61
|
accounts = json_response['accounts']
|
60
|
-
|
61
|
-
|
62
|
-
|
62
|
+
title = "Morpheus Tenants"
|
63
|
+
subtitles = []
|
64
|
+
subtitles += parse_list_subtitles(options)
|
65
|
+
print_h1 title, subtitles
|
66
|
+
if accounts.empty?
|
67
|
+
puts yellow,"No tenants found.",reset
|
63
68
|
else
|
64
|
-
|
65
|
-
|
66
|
-
subtitles += parse_list_subtitles(options)
|
67
|
-
print_h1 title, subtitles
|
68
|
-
if accounts.empty?
|
69
|
-
puts yellow,"No tenants found.",reset
|
70
|
-
else
|
71
|
-
print_accounts_table(accounts)
|
72
|
-
print_results_pagination(json_response)
|
73
|
-
end
|
74
|
-
print reset,"\n"
|
69
|
+
print_accounts_table(accounts)
|
70
|
+
print_results_pagination(json_response)
|
75
71
|
end
|
72
|
+
print reset,"\n"
|
73
|
+
return 0
|
76
74
|
rescue RestClient::Exception => e
|
77
75
|
print_rest_exception(e, options)
|
78
76
|
exit 1
|
@@ -113,7 +111,7 @@ class Morpheus::Cli::TenantsCommand
|
|
113
111
|
options = {}
|
114
112
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
115
113
|
opts.banner = subcommand_usage("[name]")
|
116
|
-
build_common_options(opts, options, [:json, :
|
114
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :outfile, :dry_run, :remote])
|
117
115
|
end
|
118
116
|
optparse.parse!(args)
|
119
117
|
if args.count != 1
|
@@ -132,36 +130,35 @@ class Morpheus::Cli::TenantsCommand
|
|
132
130
|
end
|
133
131
|
account = find_account_by_name_or_id(args[0])
|
134
132
|
exit 1 if account.nil?
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
end
|
133
|
+
|
134
|
+
json_response = {'account' => account}
|
135
|
+
render_result = render_with_format(json_response, options, 'account')
|
136
|
+
return 0 if render_result
|
137
|
+
|
138
|
+
print_h1 "Tenant Details", [], options
|
139
|
+
|
140
|
+
description_cols = {
|
141
|
+
"ID" => 'id',
|
142
|
+
"Name" => 'name',
|
143
|
+
"Description" => 'description',
|
144
|
+
"Subdomain" => 'subdomain',
|
145
|
+
"Currency" => 'currency',
|
146
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
147
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
148
|
+
"Status" => lambda {|it|
|
149
|
+
status_state = nil
|
150
|
+
if account['active']
|
151
|
+
status_state = "#{green}ACTIVE#{cyan}"
|
152
|
+
else
|
153
|
+
status_state = "#{red}INACTIVE#{cyan}"
|
154
|
+
end
|
155
|
+
status_state
|
156
|
+
},
|
157
|
+
}
|
158
|
+
print_description_list(description_cols, account)
|
159
|
+
|
160
|
+
print reset,"\n"
|
161
|
+
return 0
|
165
162
|
rescue RestClient::Exception => e
|
166
163
|
print_rest_exception(e, options)
|
167
164
|
exit 1
|
data/lib/morpheus/cli/version.rb
CHANGED
@@ -0,0 +1,461 @@
|
|
1
|
+
require 'io/console'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'optparse'
|
4
|
+
require 'morpheus/cli/cli_command'
|
5
|
+
require 'morpheus/cli/option_types'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class Morpheus::Cli::WikiCommand
|
9
|
+
include Morpheus::Cli::CliCommand
|
10
|
+
set_command_name :wiki
|
11
|
+
register_subcommands :list, :get, :view, :add, :update, :remove, :'categories'
|
12
|
+
|
13
|
+
def initialize()
|
14
|
+
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect(opts)
|
18
|
+
@api_client = establish_remote_appliance_connection(opts)
|
19
|
+
@wiki_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).wiki
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle(args)
|
23
|
+
handle_subcommand(args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def list(args)
|
27
|
+
options = {}
|
28
|
+
params = {}
|
29
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
30
|
+
opts.banner = subcommand_usage()
|
31
|
+
opts.on('--category VALUE', String, "Category") do |val|
|
32
|
+
params['category'] = val
|
33
|
+
end
|
34
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
35
|
+
end
|
36
|
+
optparse.parse!(args)
|
37
|
+
if args.count != 0
|
38
|
+
raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
|
39
|
+
end
|
40
|
+
connect(options)
|
41
|
+
begin
|
42
|
+
params.merge!(parse_list_options(options))
|
43
|
+
@wiki_interface.setopts(options)
|
44
|
+
if options[:dry_run]
|
45
|
+
print_dry_run @wiki_interface.dry.list(params)
|
46
|
+
return 0
|
47
|
+
end
|
48
|
+
json_response = @wiki_interface.list(params)
|
49
|
+
render_result = render_with_format(json_response, options, 'pages')
|
50
|
+
return 0 if render_result
|
51
|
+
pages = json_response['pages']
|
52
|
+
unless options[:quiet]
|
53
|
+
title = "Morpheus Wiki Pages"
|
54
|
+
subtitles = []
|
55
|
+
if params['category']
|
56
|
+
subtitles << "Category: #{params['category']}"
|
57
|
+
end
|
58
|
+
subtitles += parse_list_subtitles(options)
|
59
|
+
print_h1 title, subtitles
|
60
|
+
if pages.empty?
|
61
|
+
print yellow,"No wiki pages found.",reset
|
62
|
+
else
|
63
|
+
columns = [
|
64
|
+
{"ID" => lambda {|page| page['id'] } },
|
65
|
+
{"NAME" => lambda {|page| page['name'] } },
|
66
|
+
{"CATEGORY" => lambda {|page| page['category'] } },
|
67
|
+
{"AUTHOR" => lambda {|page| page['updatedBy'] ? page['updatedBy']['username'] : '' } },
|
68
|
+
{"CREATED" => lambda {|page| format_local_dt(page['dateCreated']) } },
|
69
|
+
{"UPDATED" => lambda {|page| format_local_dt(page['lastUpdated']) } },
|
70
|
+
]
|
71
|
+
if options[:include_fields]
|
72
|
+
columns = options[:include_fields]
|
73
|
+
end
|
74
|
+
print as_pretty_table(pages, columns, options)
|
75
|
+
print_results_pagination(json_response)
|
76
|
+
end
|
77
|
+
print reset,"\n"
|
78
|
+
end
|
79
|
+
return 0
|
80
|
+
rescue RestClient::Exception => e
|
81
|
+
print_rest_exception(e, options)
|
82
|
+
exit 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def get(args)
|
87
|
+
options = {}
|
88
|
+
params = {}
|
89
|
+
open_wiki_link = false
|
90
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
91
|
+
opts.banner = subcommand_usage("[name]")
|
92
|
+
opts.on('--view', '--view', "View wiki page in web browser too.") do
|
93
|
+
open_wiki_link = true
|
94
|
+
end
|
95
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
96
|
+
end
|
97
|
+
optparse.parse!(args)
|
98
|
+
|
99
|
+
if args.count != 1
|
100
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
101
|
+
end
|
102
|
+
|
103
|
+
connect(options)
|
104
|
+
begin
|
105
|
+
@wiki_interface.setopts(options)
|
106
|
+
if options[:dry_run]
|
107
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
108
|
+
print_dry_run @wiki_interface.dry.get(args[0])
|
109
|
+
else
|
110
|
+
print_dry_run @wiki_interface.dry.list({name: args[0].to_s})
|
111
|
+
end
|
112
|
+
return 0
|
113
|
+
end
|
114
|
+
page = find_wiki_page_by_name_or_id(args[0])
|
115
|
+
return 1 if page.nil?
|
116
|
+
json_response = {'page' => page}
|
117
|
+
render_result = render_with_format(json_response, options, 'page')
|
118
|
+
return 0 if render_result
|
119
|
+
|
120
|
+
unless options[:quiet]
|
121
|
+
print_h1 "Wiki Page Details"
|
122
|
+
print cyan
|
123
|
+
|
124
|
+
print_description_list({
|
125
|
+
"ID" => 'id',
|
126
|
+
"Name" => 'name',
|
127
|
+
"Category" => 'category',
|
128
|
+
"Ref Type" => 'refType',
|
129
|
+
"Ref ID" => 'refId',
|
130
|
+
#"Owner" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
131
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
132
|
+
"Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
|
133
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
134
|
+
"Updated By" => lambda {|it| it['updatedBy'] ? it['updatedBy']['username'] : '' }
|
135
|
+
}, page)
|
136
|
+
print reset,"\n"
|
137
|
+
|
138
|
+
print_h2 "Page Content"
|
139
|
+
print cyan, page['content'], reset, "\n"
|
140
|
+
|
141
|
+
end
|
142
|
+
print reset,"\n"
|
143
|
+
if open_wiki_link
|
144
|
+
return view([page['id']])
|
145
|
+
end
|
146
|
+
return 0
|
147
|
+
rescue RestClient::Exception => e
|
148
|
+
print_rest_exception(e, options)
|
149
|
+
exit 1
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def view(args)
|
154
|
+
options = {}
|
155
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
156
|
+
opts.banner = subcommand_usage("[id]")
|
157
|
+
build_common_options(opts, options, [:dry_run, :remote])
|
158
|
+
opts.footer = "View a wiki page in a web browser" + "\n" +
|
159
|
+
"[id] is required. This is name or id of the wiki page."
|
160
|
+
end
|
161
|
+
optparse.parse!(args)
|
162
|
+
if args.count != 1
|
163
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
164
|
+
end
|
165
|
+
connect(options)
|
166
|
+
begin
|
167
|
+
page = find_wiki_page_by_name_or_id(args[0])
|
168
|
+
return 1 if page.nil?
|
169
|
+
|
170
|
+
link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/operations/wiki/#{page['urlName']}"
|
171
|
+
|
172
|
+
open_command = nil
|
173
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
174
|
+
open_command = "start #{link}"
|
175
|
+
elsif RbConfig::CONFIG['host_os'] =~ /darwin/
|
176
|
+
open_command = "open #{link}"
|
177
|
+
elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
|
178
|
+
open_command = "xdg-open #{link}"
|
179
|
+
end
|
180
|
+
|
181
|
+
if options[:dry_run]
|
182
|
+
puts "system: #{open_command}"
|
183
|
+
return 0
|
184
|
+
end
|
185
|
+
|
186
|
+
system(open_command)
|
187
|
+
|
188
|
+
return 0
|
189
|
+
rescue RestClient::Exception => e
|
190
|
+
print_rest_exception(e, options)
|
191
|
+
exit 1
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def add(args)
|
196
|
+
options = {}
|
197
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
198
|
+
opts.banner = subcommand_usage("[name] [options]")
|
199
|
+
build_option_type_options(opts, options, add_wiki_page_option_types)
|
200
|
+
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
201
|
+
end
|
202
|
+
optparse.parse!(args)
|
203
|
+
if args.count > 1
|
204
|
+
raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args}\n#{optparse}"
|
205
|
+
end
|
206
|
+
if args[0]
|
207
|
+
options[:options] ||= {}
|
208
|
+
options[:options]['name'] ||= args[0]
|
209
|
+
end
|
210
|
+
connect(options)
|
211
|
+
begin
|
212
|
+
# construct payload
|
213
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
214
|
+
payload = nil
|
215
|
+
if options[:payload]
|
216
|
+
payload = options[:payload]
|
217
|
+
payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
|
218
|
+
else
|
219
|
+
payload = {
|
220
|
+
'page' => {
|
221
|
+
}
|
222
|
+
}
|
223
|
+
# allow arbitrary -O options
|
224
|
+
payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
|
225
|
+
# prompt for options
|
226
|
+
params = Morpheus::Cli::OptionTypes.prompt(add_wiki_page_option_types, options[:options], @api_client, options[:params])
|
227
|
+
payload.deep_merge!({'page' => params}) unless params.empty?
|
228
|
+
end
|
229
|
+
|
230
|
+
@wiki_interface.setopts(options)
|
231
|
+
if options[:dry_run]
|
232
|
+
print_dry_run @wiki_interface.dry.create(payload)
|
233
|
+
return
|
234
|
+
end
|
235
|
+
json_response = @wiki_interface.create(payload)
|
236
|
+
if options[:json]
|
237
|
+
print JSON.pretty_generate(json_response)
|
238
|
+
print "\n"
|
239
|
+
else
|
240
|
+
display_name = json_response['page'] ? json_response['page']['name'] : ''
|
241
|
+
print_green_success "Wiki page #{display_name} added"
|
242
|
+
get([json_response['page']['id']])
|
243
|
+
end
|
244
|
+
return 0
|
245
|
+
rescue RestClient::Exception => e
|
246
|
+
print_rest_exception(e, options)
|
247
|
+
exit 1
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def update(args)
|
252
|
+
options = {}
|
253
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
254
|
+
opts.banner = subcommand_usage("[name] [options]")
|
255
|
+
build_option_type_options(opts, options, update_wiki_page_option_types)
|
256
|
+
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
257
|
+
end
|
258
|
+
optparse.parse!(args)
|
259
|
+
|
260
|
+
if args.count != 1
|
261
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
262
|
+
end
|
263
|
+
|
264
|
+
connect(options)
|
265
|
+
begin
|
266
|
+
|
267
|
+
page = find_wiki_page_by_name_or_id(args[0])
|
268
|
+
return 1 if page.nil?
|
269
|
+
|
270
|
+
# construct payload
|
271
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
272
|
+
payload = nil
|
273
|
+
if options[:payload]
|
274
|
+
payload = options[:payload]
|
275
|
+
payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
|
276
|
+
else
|
277
|
+
payload = {
|
278
|
+
'page' => {
|
279
|
+
}
|
280
|
+
}
|
281
|
+
# allow arbitrary -O options
|
282
|
+
payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
|
283
|
+
# prompt for options
|
284
|
+
#params = Morpheus::Cli::OptionTypes.prompt(update_wiki_page_option_types, options[:options], @api_client, options[:params])
|
285
|
+
params = passed_options
|
286
|
+
|
287
|
+
if params.empty?
|
288
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
289
|
+
end
|
290
|
+
if params["category"] && (params["category"].strip == "" || params["category"].strip == "null")
|
291
|
+
params["category"] = ""
|
292
|
+
end
|
293
|
+
payload.deep_merge!({'page' => params}) unless params.empty?
|
294
|
+
end
|
295
|
+
@wiki_interface.setopts(options)
|
296
|
+
if options[:dry_run]
|
297
|
+
print_dry_run @wiki_interface.dry.update(page['id'], payload)
|
298
|
+
return
|
299
|
+
end
|
300
|
+
json_response = @wiki_interface.update(page['id'], payload)
|
301
|
+
if options[:json]
|
302
|
+
print JSON.pretty_generate(json_response)
|
303
|
+
print "\n"
|
304
|
+
else
|
305
|
+
display_name = json_response['page'] ? json_response['page']['name'] : ''
|
306
|
+
print_green_success "Wiki page #{display_name} updated"
|
307
|
+
get([json_response['page']['id']])
|
308
|
+
end
|
309
|
+
return 0
|
310
|
+
rescue RestClient::Exception => e
|
311
|
+
print_rest_exception(e, options)
|
312
|
+
exit 1
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def remove(args)
|
317
|
+
options = {}
|
318
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
319
|
+
opts.banner = subcommand_usage("[name]")
|
320
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
321
|
+
end
|
322
|
+
optparse.parse!(args)
|
323
|
+
|
324
|
+
if args.count != 1
|
325
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
326
|
+
end
|
327
|
+
|
328
|
+
connect(options)
|
329
|
+
begin
|
330
|
+
page = find_wiki_page_by_name_or_id(args[0])
|
331
|
+
return 1 if page.nil?
|
332
|
+
|
333
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the page #{page['name']}?")
|
334
|
+
return 9, "aborted command"
|
335
|
+
end
|
336
|
+
@wiki_interface.setopts(options)
|
337
|
+
if options[:dry_run]
|
338
|
+
print_dry_run @wiki_interface.dry.destroy(page['id'])
|
339
|
+
return
|
340
|
+
end
|
341
|
+
json_response = @wiki_interface.destroy(page['id'])
|
342
|
+
if options[:json]
|
343
|
+
print JSON.pretty_generate(json_response)
|
344
|
+
print "\n"
|
345
|
+
else
|
346
|
+
print_green_success "Wiki page #{page['name']} removed"
|
347
|
+
# list([])
|
348
|
+
end
|
349
|
+
return 0
|
350
|
+
rescue RestClient::Exception => e
|
351
|
+
print_rest_exception(e, options)
|
352
|
+
exit 1
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def categories(args)
|
357
|
+
options = {}
|
358
|
+
params = {}
|
359
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
360
|
+
opts.banner = subcommand_usage()
|
361
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
362
|
+
end
|
363
|
+
optparse.parse!(args)
|
364
|
+
if args.count != 0
|
365
|
+
raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
|
366
|
+
end
|
367
|
+
connect(options)
|
368
|
+
begin
|
369
|
+
params.merge!(parse_list_options(options))
|
370
|
+
@wiki_interface.setopts(options)
|
371
|
+
if options[:dry_run]
|
372
|
+
print_dry_run @wiki_interface.dry.categories(params)
|
373
|
+
return 0
|
374
|
+
end
|
375
|
+
json_response = @wiki_interface.categories(params)
|
376
|
+
render_result = render_with_format(json_response, options, 'categories')
|
377
|
+
return 0 if render_result
|
378
|
+
categories = json_response['categories']
|
379
|
+
unless options[:quiet]
|
380
|
+
title = "Morpheus Wiki Categories"
|
381
|
+
subtitles = []
|
382
|
+
subtitles += parse_list_subtitles(options)
|
383
|
+
print_h1 title, subtitles
|
384
|
+
if categories.empty?
|
385
|
+
print yellow,"No wiki categories found.",reset,"\n"
|
386
|
+
else
|
387
|
+
columns = [
|
388
|
+
{"CATEGORY" => lambda {|page| page['name'] } },
|
389
|
+
{"# PAGES" => lambda {|it| it['pageCount'] } }
|
390
|
+
]
|
391
|
+
if options[:include_fields]
|
392
|
+
columns = options[:include_fields]
|
393
|
+
end
|
394
|
+
print as_pretty_table(categories, columns, options)
|
395
|
+
#print_results_pagination(json_response)
|
396
|
+
end
|
397
|
+
print reset,"\n"
|
398
|
+
end
|
399
|
+
return 0
|
400
|
+
rescue RestClient::Exception => e
|
401
|
+
print_rest_exception(e, options)
|
402
|
+
exit 1
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
private
|
407
|
+
def find_wiki_page_by_name_or_id(val)
|
408
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
409
|
+
return find_wiki_page_by_id(val)
|
410
|
+
else
|
411
|
+
return find_wiki_page_by_name(val)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def find_wiki_page_by_id(id)
|
416
|
+
raise "#{self.class} has not defined @wiki_interface" if @wiki_interface.nil?
|
417
|
+
begin
|
418
|
+
json_response = @wiki_interface.get(id)
|
419
|
+
return json_response['page']
|
420
|
+
rescue RestClient::Exception => e
|
421
|
+
if e.response && e.response.code == 404
|
422
|
+
print_red_alert "Wiki page not found by id #{id}"
|
423
|
+
else
|
424
|
+
raise e
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def find_wiki_page_by_name(name)
|
430
|
+
raise "#{self.class} has not defined @wiki_interface" if @wiki_interface.nil?
|
431
|
+
pages = @wiki_interface.list({name: name.to_s})['pages']
|
432
|
+
if pages.empty?
|
433
|
+
print_red_alert "Wiki page not found by name #{name}"
|
434
|
+
return nil
|
435
|
+
elsif pages.size > 1
|
436
|
+
print_red_alert "#{pages.size} wiki pages found by name #{name}"
|
437
|
+
print as_pretty_table(pages, [:id,:name], {color:red})
|
438
|
+
print reset,"\n"
|
439
|
+
return nil
|
440
|
+
else
|
441
|
+
return pages[0]
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def add_wiki_page_option_types
|
446
|
+
[
|
447
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
448
|
+
{'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
449
|
+
{'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'textarea', 'required' => true, 'displayOrder' => 3}
|
450
|
+
]
|
451
|
+
end
|
452
|
+
|
453
|
+
def update_wiki_page_option_types
|
454
|
+
[
|
455
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1},
|
456
|
+
{'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
457
|
+
{'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'textarea', 'required' => false, 'displayOrder' => 3}
|
458
|
+
]
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|