morpheus-cli 5.5.0 → 5.5.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +4 -0
  4. data/lib/morpheus/api/clusters_interface.rb +12 -0
  5. data/lib/morpheus/api/network_pool_servers_interface.rb +7 -0
  6. data/lib/morpheus/api/scale_thresholds_interface.rb +9 -0
  7. data/lib/morpheus/cli/cli_command.rb +39 -20
  8. data/lib/morpheus/cli/commands/apps.rb +1 -1
  9. data/lib/morpheus/cli/commands/cloud_resource_pools_command.rb +33 -2
  10. data/lib/morpheus/cli/commands/clouds.rb +12 -6
  11. data/lib/morpheus/cli/commands/clusters.rb +66 -5
  12. data/lib/morpheus/cli/commands/hosts.rb +5 -1
  13. data/lib/morpheus/cli/commands/instances.rb +1 -1
  14. data/lib/morpheus/cli/commands/integrations_command.rb +1 -1
  15. data/lib/morpheus/cli/commands/invoices_command.rb +8 -1
  16. data/lib/morpheus/cli/commands/jobs_command.rb +45 -225
  17. data/lib/morpheus/cli/commands/library_container_types_command.rb +52 -3
  18. data/lib/morpheus/cli/commands/library_option_types_command.rb +56 -62
  19. data/lib/morpheus/cli/commands/load_balancers.rb +11 -19
  20. data/lib/morpheus/cli/commands/network_pool_servers_command.rb +5 -2
  21. data/lib/morpheus/cli/commands/roles.rb +475 -70
  22. data/lib/morpheus/cli/commands/scale_thresholds.rb +103 -0
  23. data/lib/morpheus/cli/commands/tasks.rb +19 -12
  24. data/lib/morpheus/cli/commands/user_sources_command.rb +107 -39
  25. data/lib/morpheus/cli/commands/users.rb +10 -10
  26. data/lib/morpheus/cli/commands/view.rb +1 -0
  27. data/lib/morpheus/cli/commands/workflows.rb +21 -14
  28. data/lib/morpheus/cli/error_handler.rb +13 -4
  29. data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
  30. data/lib/morpheus/cli/mixins/execution_request_helper.rb +1 -1
  31. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +3 -3
  32. data/lib/morpheus/cli/mixins/jobs_helper.rb +173 -0
  33. data/lib/morpheus/cli/mixins/print_helper.rb +120 -38
  34. data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -3
  35. data/lib/morpheus/cli/mixins/rest_command.rb +41 -14
  36. data/lib/morpheus/cli/option_types.rb +68 -37
  37. data/lib/morpheus/cli/version.rb +1 -1
  38. data/lib/morpheus/logging.rb +6 -8
  39. metadata +6 -4
@@ -0,0 +1,173 @@
1
+ require 'morpheus/cli/mixins/print_helper'
2
+ # Mixin for Morpheus::Cli command classes
3
+ # Provides refreshing job execution records by id
4
+ module Morpheus::Cli::JobsHelper
5
+
6
+ def self.included(klass)
7
+ klass.send :include, Morpheus::Cli::PrintHelper
8
+ # klass.send :include, Morpheus::Cli::ProcessesHelper
9
+ end
10
+
11
+ def api_client
12
+ raise "#{self.class} has not defined @api_client" if @api_client.nil?
13
+ @api_client
14
+ end
15
+
16
+ def jobs_interface
17
+ # get_interface('jobs')
18
+ api_client.jobs
19
+ end
20
+
21
+ def get_process_event_data(process_or_event)
22
+ {
23
+ id: process_or_event['id'],
24
+ description: process_or_event['description'] || (process_or_event['refType'] == 'instance' ? process_or_event['displayName'] : (process_or_event['processTypeName'] || '').capitalize),
25
+ start_date: format_local_dt(process_or_event['startDate']),
26
+ created_by: process_or_event['createdBy'] ? process_or_event['createdBy']['displayName'] : '',
27
+ duration: format_human_duration((process_or_event['duration'] || process_or_event['statusEta'] || 0) / 1000.0),
28
+ status: format_job_status(process_or_event['status']),
29
+ error: process_or_event['message'] || process_or_event['error'],
30
+ output: process_or_event['output'],
31
+ }
32
+ end
33
+
34
+ # both process and process events
35
+ def print_process_events(events, options={})
36
+ # event_columns = [:id, :description, :start_date, :created_by, :duration, :status, :error, :output]
37
+ event_columns = {
38
+ "ID" => lambda {|it| it[:id]},
39
+ "Description" => lambda {|it| it[:description]},
40
+ "Start Date" => lambda {|it| it[:start_date]},
41
+ "Created By" => lambda {|it| it[:created_by]},
42
+ "Duration" => lambda {|it| it[:duration]},
43
+ "Status" => lambda {|it| it[:status]},
44
+ "Error" => lambda {|it| options[:details] ? it[:error] : truncate_string(it[:error], 32) },
45
+ "Output" => lambda {|it| options[:details] ? it[:output] : truncate_string(it[:output], 32) }
46
+ }
47
+ print as_pretty_table(events.collect {|it| get_process_event_data(it)}, event_columns.upcase_keys!, options)
48
+ end
49
+
50
+ def print_job_execution(job_execution, options)
51
+ process = job_execution['process']
52
+ print cyan
53
+ description_cols = {
54
+ "ID" => lambda {|it| it['id'] },
55
+ "Job" => lambda {|it| it['job'] ? it['job']['name'] : ''},
56
+ "Job Type" => lambda {|it| it['job'] && it['job']['type'] ? (it['job']['type']['code'] == 'morpheus.workflow' ? 'Workflow' : 'Task') : ''},
57
+ # "Description" => lambda {|it| it['description'] || (it['job'] ? it['job']['description'] : '') },
58
+ "Start Date" => lambda {|it| format_local_dt(it['startDate'])},
59
+ "ETA/Time" => lambda {|it| it['duration'] ? format_human_duration(it['duration'] / 1000.0) : ''},
60
+ "Status" => lambda {|it| format_job_status(it['status'])},
61
+ #"Output" => lambda {|it| it['process'] && (it['process']['output']) ?(it['process']['output']).to_s.strip : ''},
62
+ #"Error" => lambda {|it| it['process'] && (it['process']['message'] || it['process']['error']) ? red + (it['process']['message'] || it['process']['error']).to_s.strip + cyan : ''},
63
+ "Created By" => lambda {|it| it['createdBy'].nil? ? '' : it['createdBy']['displayName'] || it['createdBy']['username']}
64
+ }
65
+ print_description_list(description_cols, job_execution, options)
66
+
67
+ if process
68
+ process_data = get_process_event_data(process)
69
+ print_h2 "Process Details"
70
+ process_description_cols = {
71
+ "Process ID" => lambda {|it| it[:id]},
72
+ "Description" => lambda {|it| it[:description]},
73
+ "Start Date" => lambda {|it| it[:start_date]},
74
+ "Created By" => lambda {|it| it[:created_by]},
75
+ "Duration" => lambda {|it| it[:duration]},
76
+ "Status" => lambda {|it| it[:status]}
77
+ }
78
+ print_description_list(process_description_cols, process_data, options)
79
+
80
+ if process_data[:output] && process_data[:output].strip.length > 0
81
+ print_h2 "Output"
82
+ print process['output']
83
+ end
84
+ if process_data[:error] && process_data[:error].strip.length > 0
85
+ print_h2 "Error"
86
+ print process['message'] || process['error']
87
+ print reset,"\n"
88
+ end
89
+
90
+
91
+ if process['events'] && !process['events'].empty?
92
+ print_h2 "Process Events", options
93
+ print_process_events(process['events'], options)
94
+ end
95
+ else
96
+ print reset,"\n"
97
+ end
98
+ return 0, nil
99
+ end
100
+
101
+ def print_job_executions(execs, options={})
102
+ if execs.empty?
103
+ print cyan,"No job executions found.",reset,"\n"
104
+ else
105
+ rows = execs.collect do |ex|
106
+ {
107
+ id: ex['id'],
108
+ job: ex['job'] ? ex['job']['name'] : '',
109
+ description: ex['description'] || ex['job'] ? ex['job']['description'] : '',
110
+ type: ex['job'] && ex['job']['type'] ? (ex['job']['type']['code'] == 'morpheus.workflow' ? 'Workflow' : 'Task') : '',
111
+ start: format_local_dt(ex['startDate']),
112
+ duration: ex['duration'] ? format_human_duration(ex['duration'] / 1000.0) : '',
113
+ status: format_job_status(ex['status']),
114
+ error: truncate_string(ex['process'] && (ex['process']['message'] || ex['process']['error']) ? ex['process']['message'] || ex['process']['error'] : '', options[:details] ? nil : 32),
115
+ output: truncate_string(ex['process'] && ex['process']['output'] ? ex['process']['output'] : '', options[:details] ? nil : 32),
116
+ }
117
+ end
118
+
119
+ columns = [
120
+ :id, :job, :type, {'START DATE' => :start}, {'ETA/TIME' => :duration}, :status, :error, :output
121
+ ]
122
+ print as_pretty_table(rows, columns, options)
123
+ end
124
+ end
125
+
126
+ def format_job_status(status_string, return_color=cyan)
127
+ out = ""
128
+ if status_string
129
+ if ['complete','success', 'successful', 'ok'].include?(status_string)
130
+ out << "#{green}#{status_string.upcase}"
131
+ elsif ['error', 'offline', 'failed', 'failure'].include?(status_string)
132
+ out << "#{red}#{status_string.upcase}"
133
+ else
134
+ out << "#{yellow}#{status_string.upcase}"
135
+ end
136
+ end
137
+ out + return_color
138
+ end
139
+
140
+ # this is built into CliCommand now...
141
+ # def find_by_name_or_id(type, val)
142
+ # interface = instance_variable_get "@#{type}s_interface"
143
+ # typeCamelCase = type.gsub(/(?:^|_)([a-z])/) do $1.upcase end
144
+ # typeCamelCase = typeCamelCase[0, 1].downcase + typeCamelCase[1..-1]
145
+ # (val.to_s =~ /\A\d{1,}\Z/) ? interface.get(val.to_i)[typeCamelCase] : interface.list({'name' => val})["#{typeCamelCase}s"].first
146
+ # end
147
+
148
+ # refresh execution request until it is finished
149
+ # returns json response data of the last execution request when status reached 'completed' or 'failed'
150
+ def wait_for_job_execution(job_execution_id, options={}, print_output = true)
151
+ refresh_interval = 10
152
+ if options[:refresh_interval].to_i > 0
153
+ refresh_interval = options[:refresh_interval]
154
+ end
155
+ refresh_display_seconds = refresh_interval % 1.0 == 0 ? refresh_interval.to_i : refresh_interval
156
+ unless options[:quiet]
157
+ print cyan, "Refreshing every #{refresh_display_seconds} seconds until execution is complete...", "\n", reset
158
+ end
159
+ job_execution = jobs_interface.get_execution(job_execution_id)['jobExecution']
160
+ while ['new','queued','pending','running'].include?(job_execution['status']) do
161
+ sleep(refresh_interval)
162
+ job_execution = jobs_interface.get_execution(job_execution_id)['jobExecution']
163
+ end
164
+ if print_output && options[:quiet] != true
165
+ print_h1 "Morpheus Job Execution", [], options
166
+ print_job_execution(job_execution, options)
167
+ end
168
+ return job_execution
169
+ end
170
+
171
+
172
+
173
+ end
@@ -608,6 +608,9 @@ module Morpheus::Cli::PrintHelper
608
608
  # @param suffix [String] the character to pad right side with. Default is '...'
609
609
  def truncate_string(value, width, suffix="...")
610
610
  value = value.to_s
611
+ if !width
612
+ return value
613
+ end
611
614
  # JD: hack alerty.. this sux, but it's a best effort to preserve values containing ascii coloring codes
612
615
  # it stops working when there are words separated by ascii codes, eg. two diff colors
613
616
  # plus this is probably pretty slow...
@@ -714,7 +717,11 @@ module Morpheus::Cli::PrintHelper
714
717
 
715
718
  # support --fields x,y,z and --all-fields or --fields all
716
719
  all_fields = data.first ? data.first.keys : []
717
-
720
+ #todo: support --raw-fields meh, not really needed..
721
+ # if options[:include_fields] && options[:raw_fields]
722
+ # data = transform_data_for_field_options(data, options)
723
+ # data = data[options[:include_fields_context]] if options[:include_fields_context]
724
+ # elsif options[:include_fields]
718
725
  if options[:include_fields]
719
726
  if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
720
727
  columns = all_fields
@@ -1093,16 +1100,78 @@ module Morpheus::Cli::PrintHelper
1093
1100
  '"' + v.to_s.gsub('"', '""') + '"'
1094
1101
  end
1095
1102
 
1096
- def as_csv(data, columns, options={})
1103
+ def as_csv(data, default_columns=nil, options={}, object_key=nil)
1097
1104
  out = ""
1098
1105
  delim = options[:csv_delim] || options[:delim] || ","
1099
1106
  newline = options[:csv_newline] || options[:newline] || "\n"
1100
1107
  include_header = options[:csv_no_header] ? false : true
1101
1108
  do_quotes = options[:csv_quotes] || options[:quotes]
1102
1109
 
1110
+ if options[:include_fields]
1111
+ data = transform_data_for_field_options(data, options, object_key)
1112
+ if data.is_a?(Hash)
1113
+ if options[:raw_fields]
1114
+ if options[:include_fields_context]
1115
+ data = data[options[:include_fields_context]]
1116
+ else
1117
+ # todo: could use a dynamic object_key, first Array or Hash in the data, can probably always do this...
1118
+ # if object_key.nil?
1119
+ # object_key = data.keys.find {|k| data[k].is_a?(Array) || data[k].is_a?(Hash) }
1120
+ # end
1121
+ # if object_key && data[object_key]
1122
+ # data = data[object_key]
1123
+ # end
1124
+ end
1125
+ else
1126
+ if object_key
1127
+ data = data[object_key]
1128
+ end
1129
+ end
1130
+ end
1131
+ else
1132
+ # need array of records, so always select the object/array here
1133
+ if object_key
1134
+ if data.is_a?(Hash)
1135
+ data = data[object_key]
1136
+ else
1137
+ Morpheus::Logging::DarkPrinter.puts "as_csv() expects data as an to fetch object key '#{object_key}' from, #{records.class}." if Morpheus::Logging.debug?
1138
+ end
1139
+ end
1140
+ end
1141
+
1142
+
1143
+ records = data
1144
+
1145
+ # allow records as Array and Hash only..
1146
+ if records.is_a?(Array)
1147
+ # records = records
1148
+ elsif records.is_a?(Hash)
1149
+ records = [records]
1150
+ else
1151
+ #raise "records_as_csv expects records as an Array of objects to render"
1152
+ Morpheus::Logging::DarkPrinter.puts "as_csv() expects data as an Array of objects to render, got a #{records.class} instead" if Morpheus::Logging.debug?
1153
+ # return ""
1154
+ return out
1155
+ end
1156
+
1157
+ # build column definitions, by default use all properties for the first record (Hash) in the array
1158
+ columns = []
1159
+ all_fields = records.first.is_a?(Hash) ? records.first.keys : []
1160
+ if options[:include_fields]
1161
+ if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
1162
+ columns = all_fields
1163
+ else
1164
+ columns = options[:include_fields]
1165
+ end
1166
+ elsif options[:all_fields]
1167
+ columns = all_fields
1168
+ elsif default_columns
1169
+ columns = default_columns
1170
+ else
1171
+ columns = all_fields
1172
+ end
1173
+
1103
1174
  column_defs = build_column_definitions(columns)
1104
- #columns = columns.flatten.compact
1105
- data_array = [data].flatten.compact
1106
1175
 
1107
1176
  if include_header
1108
1177
  headers = column_defs.collect {|column_def| column_def.label }
@@ -1113,7 +1182,7 @@ module Morpheus::Cli::PrintHelper
1113
1182
  out << newline
1114
1183
  end
1115
1184
  lines = []
1116
- data_array.each do |obj|
1185
+ records.each do |obj|
1117
1186
  if obj
1118
1187
  cells = []
1119
1188
  column_defs.each do |column_def|
@@ -1137,29 +1206,9 @@ module Morpheus::Cli::PrintHelper
1137
1206
 
1138
1207
  end
1139
1208
 
1209
+ # deprecated, replaced by as_csv(records, columns, options, object_key)
1140
1210
  def records_as_csv(records, options={}, default_columns=nil)
1141
- out = ""
1142
- if !records
1143
- #raise "records_as_csv expects records as an Array of objects to render"
1144
- return out
1145
- end
1146
- cols = []
1147
- all_fields = records.first ? records.first.keys : []
1148
- if options[:include_fields]
1149
- if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
1150
- cols = all_fields
1151
- else
1152
- cols = options[:include_fields]
1153
- end
1154
- elsif options[:all_fields]
1155
- cols = all_fields
1156
- elsif default_columns
1157
- cols = default_columns
1158
- else
1159
- cols = all_fields
1160
- end
1161
- out << as_csv(records, cols, options)
1162
- out
1211
+ as_csv(records, default_columns, options)
1163
1212
  end
1164
1213
 
1165
1214
  def as_json(data, options={}, object_key=nil)
@@ -1169,12 +1218,7 @@ module Morpheus::Cli::PrintHelper
1169
1218
  end
1170
1219
 
1171
1220
  if options[:include_fields]
1172
- if object_key
1173
- # data[object_key] = filter_data(data[object_key], options[:include_fields])
1174
- data = {(object_key) => filter_data(data[object_key], options[:include_fields]) }
1175
- else
1176
- data = filter_data(data, options[:include_fields])
1177
- end
1221
+ data = transform_data_for_field_options(data, options, object_key)
1178
1222
  end
1179
1223
 
1180
1224
  do_pretty = options.key?(:pretty_json) ? options[:pretty_json] : true
@@ -1193,11 +1237,7 @@ module Morpheus::Cli::PrintHelper
1193
1237
  return "null" # "No data"
1194
1238
  end
1195
1239
  if options[:include_fields]
1196
- if object_key
1197
- data[object_key] = filter_data(data[object_key], options[:include_fields])
1198
- else
1199
- data = filter_data(data, options[:include_fields])
1200
- end
1240
+ data = transform_data_for_field_options(data, options, object_key)
1201
1241
  end
1202
1242
  begin
1203
1243
  out << data.to_yaml
@@ -1209,6 +1249,48 @@ module Morpheus::Cli::PrintHelper
1209
1249
  out
1210
1250
  end
1211
1251
 
1252
+
1253
+ # transform data for options --fields id,authority and --select id,authority
1254
+ # support traversing records with --raw-fields and the list command. Example: roles list --raw-fields roles.id,roles.authority
1255
+ def transform_data_for_field_options(data, options, object_key=nil)
1256
+ if options[:include_fields] && data.is_a?(Hash)
1257
+ if options[:raw_fields]
1258
+ row = (object_key && !options[:raw_fields]) ? data[object_key] : data
1259
+ records = [row].flatten()
1260
+ # look for an array in the first field only now...
1261
+ field_parts = options[:include_fields][0].to_s.split(".")
1262
+ field_context = field_parts[0]
1263
+ context_data = data[field_context]
1264
+ if field_parts.size > 1 && context_data.is_a?(Array)
1265
+ # inject all the root level properties to be selectable too..
1266
+ context_data = data.delete(field_context)
1267
+ # records = context_data
1268
+ records = context_data.collect {|it| it.is_a?(Hash) ? data.merge(it) : data }
1269
+ # hacky modifying options in place
1270
+ options[:include_fields_context] = field_context
1271
+ options[:include_fields] = options[:include_fields].collect {|it| it.sub(field_context+'.', '')}
1272
+ # data = filter_data(records, options[:include_fields])
1273
+ # data[field_context] = filter_data(records, options[:include_fields])
1274
+ data = {(field_context) => filter_data(records, options[:include_fields])}
1275
+ else
1276
+ data = filter_data(data, options[:include_fields])
1277
+ end
1278
+ else
1279
+ # By default, fields are relative to the object_key, so you can use -F id instead of requiring -F instance.id
1280
+ # So ironically it is the 'raw' options (:raw_fields == true) that has to do all this funny stuff to filter intuitively.
1281
+ if object_key
1282
+ # this removes everything but the object, makes sense when using --fields
1283
+ data = {(object_key) => filter_data(data[object_key], options[:include_fields])}
1284
+ # this preserves other fields eg. meta...
1285
+ # data[object_key] = filter_data(data[object_key], options[:include_fields])
1286
+ else
1287
+ data = filter_data(data, options[:include_fields])
1288
+ end
1289
+ end
1290
+ end
1291
+ return data
1292
+ end
1293
+
1212
1294
  def sleep_with_dots(sleep_seconds, dots=3, dot_chr=".")
1213
1295
  dot_interval = (sleep_seconds.to_f / dots.to_i)
1214
1296
  dots.to_i.times do |dot_index|
@@ -2082,7 +2082,6 @@ module Morpheus::Cli::ProvisioningHelper
2082
2082
  if !visibility && !options[:no_prompt]
2083
2083
  visibility = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Tenant Permissions Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility']
2084
2084
  end
2085
-
2086
2085
  permissions['resourcePool'] = {'visibility' => visibility} if visibility
2087
2086
  end
2088
2087
 
@@ -2114,8 +2113,7 @@ module Morpheus::Cli::ProvisioningHelper
2114
2113
  def prompt_permissions_v2(options, excludes = [])
2115
2114
  perms = prompt_permissions(options, excludes)
2116
2115
  rtn = {}
2117
-
2118
- rtn['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil?
2116
+ rtn['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !excludes.include?('visibility')
2119
2117
  rtn['tenants'] = ((perms['tenantPermissions'] || {})['accounts'] || []).collect {|it| {'id' => it}}
2120
2118
  rtn
2121
2119
  end
@@ -148,6 +148,17 @@ module Morpheus::Cli::RestCommand
148
148
 
149
149
  alias :set_rest_interface_name :rest_interface_name=
150
150
 
151
+ # rest_perms_config enables and configures permissions prompt
152
+ def rest_perms_config
153
+ @rest_perms_config || {}
154
+ end
155
+
156
+ def rest_perms_config=(v)
157
+ @rest_perms_config = v
158
+ end
159
+
160
+ alias :set_rest_perms_config :rest_perms_config=
161
+
151
162
  # rest_has_type indicates a resource has a type. default is false
152
163
  def rest_has_type
153
164
  @rest_has_type == true
@@ -329,6 +340,10 @@ module Morpheus::Cli::RestCommand
329
340
  self.class.rest_interface_name
330
341
  end
331
342
 
343
+ def rest_perms_config
344
+ self.class.rest_perms_config
345
+ end
346
+
332
347
  # returns the default rest interface, allows using rest_interface_name = "your"
333
348
  # or override this method to return @your_interface if needed
334
349
  def rest_interface
@@ -600,25 +615,25 @@ EOT
600
615
  verify_args!(args:args, optparse:optparse, count: 0)
601
616
  end
602
617
  connect(options)
603
- # load or prompt for type
604
- if rest_has_type && type_option_type.nil?
605
- if record_type_id.nil?
606
- #raise_command_error "#{rest_type_label} is required.\n#{optparse}"
607
- type_list = rest_type_interface.list({max:10000, creatable:true})[rest_type_list_key]
608
- type_dropdown_options = respond_to?("#{rest_key}_type_list_to_options", true) ? send("#{rest_key}_type_list_to_options", type_list) : type_list.collect {|it| {'name' => it['name'], 'value' => it['code']} }
609
- record_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => rest_type_label, 'type' => 'select', 'selectOptions' => type_dropdown_options, 'required' => true}], options[:options], @api_client)['type']
610
- end
611
- record_type = rest_type_find_by_name_or_id(record_type_id)
612
- if record_type.nil?
613
- return 1, "#{rest_type_label} not found for '#{record_type_id}"
614
- end
615
- end
616
618
  passed_options = parse_passed_options(options)
617
619
  payload = {}
618
620
  if options[:payload]
619
621
  payload = options[:payload]
620
622
  payload.deep_merge!({rest_object_key => passed_options})
621
623
  else
624
+ # load or prompt for type
625
+ if rest_has_type && type_option_type.nil?
626
+ if record_type_id.nil?
627
+ #raise_command_error "#{rest_type_label} is required.\n#{optparse}"
628
+ type_list = rest_type_interface.list({max:10000, creatable:true})[rest_type_list_key]
629
+ type_dropdown_options = respond_to?("#{rest_key}_type_list_to_options", true) ? send("#{rest_key}_type_list_to_options", type_list) : type_list.collect {|it| {'name' => it['name'], 'value' => it['code']} }
630
+ record_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => rest_type_label, 'type' => 'select', 'selectOptions' => type_dropdown_options, 'required' => true}], options[:options], @api_client)['type']
631
+ end
632
+ record_type = rest_type_find_by_name_or_id(record_type_id)
633
+ if record_type.nil?
634
+ return 1, "#{rest_type_label} not found for '#{record_type_id}"
635
+ end
636
+ end
622
637
  record_payload = {}
623
638
  if record_name
624
639
  record_payload['name'] = record_name
@@ -667,7 +682,7 @@ EOT
667
682
  end
668
683
  end
669
684
  api_params = (options[:params] || {}).merge(record_payload)
670
- v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, api_params)
685
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, api_params, false, true)
671
686
  v_prompt.deep_compact!
672
687
  v_prompt.booleanize! # 'on' => true
673
688
  record_payload.deep_merge!(v_prompt)
@@ -679,6 +694,18 @@ EOT
679
694
  v_prompt.booleanize! # 'on' => true
680
695
  record_payload.deep_merge!(v_prompt)
681
696
  end
697
+ # permissions
698
+ if rest_perms_config[:enabled]
699
+ if rest_perms_config[:version] == 2
700
+ perms = prompt_permissions_v2(options.deep_merge(rest_perms_config[:options] || {}), rest_perms_config[:excludes] || [])
701
+ else
702
+ perms = prompt_permissions(options.deep_merge(rest_perms_config[:options] || {}), rest_perms_config[:excludes] || [])
703
+ end
704
+ if !rest_perms_config[:name].nil?
705
+ perms.transform_keys! {|k| k == 'resourcePermissions' ? rest_perms_config[:name] : k}
706
+ end
707
+ record_payload.merge!(perms)
708
+ end
682
709
  payload[rest_object_key] = record_payload
683
710
  end
684
711
  rest_interface.setopts(options)