morpheus-cli 5.5.0 → 5.5.1

Sign up to get free protection for your applications and to get access to all the features.
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)