morpheus-cli 7.0.1 → 7.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8864c6fde23535cdf51e56b3bc94a8cb33c5a0d3fbe5f1a81e9ca9b16481b2b8
4
- data.tar.gz: 747c7097e26216cb0e088f289b789f9b3e2b8e71563457b831069aa27e66aa71
3
+ metadata.gz: b6cc9570fd3a41bf3c6ec4fb5b5ab5801171554a630eb269af90dcc6672fe141
4
+ data.tar.gz: 63150328a3155ed9ba99d57963be4e16eab3e54934eff64f94070b4fbf485c40
5
5
  SHA512:
6
- metadata.gz: 94cfd0a6625753fd42090fe83bdedc08826fb19486135c2d89bc2f449314bc53b053f3e86210d3ab6c5c35add845fc303d9dbfe4fb13a27b324dde927f6b05bb
7
- data.tar.gz: 492eed20dd75f0a10fc53275a6bd5b5044ea5286d26f8bd37c3736c4893a69488e1c4f6aaabfbc289dd5fd8a2229f3b5b947f1872dd0dc57c84063a001a20412
6
+ metadata.gz: 7d2375caf166551c16c460bf0b1cae7e2b672534c9a29a784408e2eac9e4fc336e01d93685ce5e0572933285a9e4766bc304314f8abfb2d62b700e09b9190f14
7
+ data.tar.gz: f30d57bcf10c3b3cb976329ec94a4ffff39f66da33ceffab25a7ab5246f921070f41547fe2b9c5f77de8e8814fc2ca8bf66855f93cf390044eac2fa76ba83ff3
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.7.5
2
2
 
3
- RUN gem install morpheus-cli -v 7.0.1
3
+ RUN gem install morpheus-cli -v 7.0.2
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -998,6 +998,11 @@ class Morpheus::APIClient
998
998
  Morpheus::NetworkFloatingIpsInterface.new(common_interface_options).setopts(@options)
999
999
  end
1000
1000
 
1001
+ def email_templates
1002
+ Morpheus::EmailTemplatesInterface.new(common_interface_options).setopts(@options)
1003
+ end
1004
+
1005
+
1001
1006
  def rest(endpoint)
1002
1007
  Morpheus::RestInterface.new(common_interface_options).setopts(@options.merge({base_path: "#{@base_url}/api/#{endpoint}"}))
1003
1008
  end
@@ -0,0 +1,53 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::EmailTemplatesInterface < Morpheus::APIClient
4
+
5
+ def list(params={})
6
+ url = "#{@base_url}/api/email-templates"
7
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
8
+ opts = {method: :get, url: url, headers: headers}
9
+ execute(opts)
10
+ end
11
+
12
+ def get(id, params={})
13
+ url = "#{@base_url}/api/email-templates/#{id}"
14
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
15
+ if params.is_a?(Hash)
16
+ headers[:params].merge!(params)
17
+ elsif params.is_a?(Numeric)
18
+ url = "#{base_path}/#{params}"
19
+ elsif params.is_a?(String)
20
+ headers[:params]['name'] = params
21
+ end
22
+ execute(method: :get, url: url, headers: headers)
23
+ end
24
+
25
+
26
+ def update(id, payload)
27
+ url = "#{@base_url}/api/email-templates/#{id}"
28
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
29
+ execute(method: :put, url: url, headers: headers, payload: payload.to_json)
30
+ end
31
+
32
+
33
+ def create(options)
34
+ url = "#{@base_url}/api/email-templates"
35
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
36
+ payload = options
37
+ execute(method: :post, url: url, headers: headers, payload: payload.to_json)
38
+ end
39
+
40
+ def template_types(params={})
41
+ url = "#{@base_url}/api/email-templates/types"
42
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
43
+ opts = {method: :get, url: url, headers: headers}
44
+ execute(opts)
45
+ end
46
+
47
+ def destroy(id, params={})
48
+ url = "#{@base_url}/api/email-templates/#{id}"
49
+ headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
50
+ execute(method: :delete, url: url, headers: headers)
51
+ end
52
+
53
+ end
@@ -132,6 +132,7 @@ class Morpheus::Cli::BudgetsCommand
132
132
  "Scope" => lambda {|it| format_budget_scope(it) },
133
133
  "Period" => lambda {|it| it['year'] },
134
134
  "Interval" => lambda {|it| it['interval'].to_s.capitalize },
135
+ "Forecast Model" => lambda {|it| it['forecastType'] ? it['forecastType']['name'] : '' },
135
136
  # the UI doesn't consider timezone, so uhh do it this hacky way for now.
136
137
  "Start Date" => lambda {|it|
137
138
  if it['timezone'] == 'UTC'
@@ -173,6 +174,7 @@ class Morpheus::Cli::BudgetsCommand
173
174
  }
174
175
  budget_row = {label:"Budget"}
175
176
  actual_row = {label:"Actual"}
177
+ forecast_row = {label:"Forecast"}
176
178
  multi_year = false
177
179
  if budget['startDate'] && budget['endDate'] && parse_time(budget['startDate']).year != parse_time(budget['endDate']).year
178
180
  multi_year = true
@@ -192,8 +194,18 @@ class Morpheus::Cli::BudgetsCommand
192
194
  budget_row[interval_key] = "#{cyan}#{format_money(budget_cost, currency)}#{cyan}"
193
195
  actual_row[interval_key] = "#{cyan}#{format_money(actual_cost, currency)}#{cyan}"
194
196
  end
197
+ forecast_cost = it["forecast"] ? it["forecast"].to_f : 0
198
+ forecast_over_budget = forecast_cost > 0 && forecast_cost > budget_cost
199
+ if forecast_over_budget
200
+ forecast_row[interval_key] = "#{red}#{format_money(forecast_cost, currency)}#{cyan}"
201
+ else
202
+ forecast_row[interval_key] = "#{cyan}#{format_money(forecast_cost, currency)}#{cyan}"
203
+ end
195
204
  end
196
205
  chart_data = [budget_row, actual_row]
206
+ if budget['stats']['intervals'].find { |it| it['forecast'] && it['forecast'] != 0 }
207
+ chart_data << forecast_row
208
+ end
197
209
  print as_pretty_table(chart_data, budget_summary_columns, options)
198
210
  print reset,"\n"
199
211
  rescue => ex
@@ -575,26 +587,27 @@ EOT
575
587
 
576
588
  def add_budget_option_types
577
589
  [
578
- {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
579
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 1},
590
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
591
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text'},
580
592
  # {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true},
581
- {'fieldName' => 'scope', 'fieldLabel' => 'Scope', 'code' => 'budget.scope', 'type' => 'select', 'selectOptions' => [{'name'=>'Account','value'=>'account'},{'name'=>'Tenant','value'=>'tenant'},{'name'=>'Cloud','value'=>'cloud'},{'name'=>'Group','value'=>'group'},{'name'=>'User','value'=>'user'}], 'defaultValue' => 'account', 'required' => true, 'displayOrder' => 3},
593
+ {'fieldName' => 'scope', 'fieldLabel' => 'Scope', 'code' => 'budget.scope', 'type' => 'select', 'selectOptions' => [{'name'=>'Account','value'=>'account'},{'name'=>'Tenant','value'=>'tenant'},{'name'=>'Cloud','value'=>'cloud'},{'name'=>'Group','value'=>'group'},{'name'=>'User','value'=>'user'}], 'defaultValue' => 'account', 'required' => true},
582
594
  {'fieldName' => 'tenant', 'fieldLabel' => 'Tenant', 'type' => 'select', 'optionSource' => lambda {|api_client, api_params|
583
595
  @options_interface.options_for_source("tenants", {})['data']
584
- }, 'required' => true, 'dependsOnCode' => 'budget.scope:tenant', 'displayOrder' => 4},
596
+ }, 'required' => true, 'dependsOnCode' => 'budget.scope:tenant'},
585
597
  {'fieldName' => 'user', 'fieldLabel' => 'User', 'type' => 'select', 'optionSource' => lambda {|api_client, api_params|
586
598
  @options_interface.options_for_source("users", {})['data']
587
- }, 'required' => true, 'dependsOnCode' => 'budget.scope:user', 'displayOrder' => 5},
599
+ }, 'required' => true, 'dependsOnCode' => 'budget.scope:user'},
588
600
  {'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'optionSource' => lambda {|api_client, api_params|
589
601
  @options_interface.options_for_source("groups", {})['data']
590
- }, 'required' => true, 'dependsOnCode' => 'budget.scope:group', 'displayOrder' => 6},
602
+ }, 'required' => true, 'dependsOnCode' => 'budget.scope:group'},
591
603
  {'fieldName' => 'cloud', 'fieldLabel' => 'Cloud', 'type' => 'select', 'optionSource' => lambda {|api_client, api_params|
592
604
  @options_interface.options_for_source("clouds", {})['data']
593
- }, 'required' => true, 'dependsOnCode' => 'budget.scope:cloud', 'displayOrder' => 7},
594
- {'fieldName' => 'year', 'fieldLabel' => 'Period', 'code' => 'budget.year', 'type' => 'text', 'required' => true, 'defaultValue' => Time.now.year, 'description' => "The period (year) the budget applies, YYYY or 'custom' to enter Start Date and End Date manually", 'displayOrder' => 8},
595
- {'fieldName' => 'startDate', 'fieldLabel' => 'Start Date', 'type' => 'text', 'required' => true, 'description' => 'The Start Date for custom period budget eg. 2021-01-01', 'dependsOnCode' => 'budget.year:custom', 'displayOrder' => 9},
596
- {'fieldName' => 'endDate', 'fieldLabel' => 'End Date', 'type' => 'text', 'required' => true, 'description' => 'The End Date for custom period budget eg. 2023-12-31 (must be 1, 2 or 3 years from Start Date)', 'dependsOnCode' => 'budget.year:custom', 'displayOrder' => 10},
597
- {'fieldName' => 'interval', 'fieldLabel' => 'Interval', 'type' => 'select', 'selectOptions' => [{'name'=>'Year','value'=>'year'},{'name'=>'Quarter','value'=>'quarter'},{'name'=>'Month','value'=>'month'}], 'defaultValue' => 'year', 'required' => true, 'description' => 'The budget interval, determines cost amounts: "year", "quarter" or "month"', 'displayOrder' => 11}
605
+ }, 'required' => true, 'dependsOnCode' => 'budget.scope:cloud'},
606
+ {'fieldName' => 'year', 'fieldLabel' => 'Period', 'code' => 'budget.year', 'type' => 'text', 'required' => true, 'defaultValue' => Time.now.year, 'description' => "The period (year) the budget applies, YYYY or 'custom' to enter Start Date and End Date manually"},
607
+ {'fieldName' => 'startDate', 'fieldLabel' => 'Start Date', 'type' => 'text', 'required' => true, 'description' => 'The Start Date for custom period budget eg. 2021-01-01', 'dependsOnCode' => 'budget.year:custom'},
608
+ {'fieldName' => 'endDate', 'fieldLabel' => 'End Date', 'type' => 'text', 'required' => true, 'description' => 'The End Date for custom period budget eg. 2023-12-31 (must be 1, 2 or 3 years from Start Date)', 'dependsOnCode' => 'budget.year:custom'},
609
+ {'fieldName' => 'interval', 'fieldLabel' => 'Interval', 'type' => 'select', 'selectOptions' => [{'name'=>'Year','value'=>'year'},{'name'=>'Quarter','value'=>'quarter'},{'name'=>'Month','value'=>'month'}], 'defaultValue' => 'year', 'required' => true, 'description' => 'The budget interval, determines cost amounts: "year", "quarter" or "month"'},
610
+ {'fieldName' => 'forecastType.id', 'fieldLabel' => 'Forecast Model', 'type' => 'select', 'optionSource' => 'forecastTypes', 'description' => 'The budget forcecast model type, determines projected cost calculations.'},
598
611
  ]
599
612
  end
600
613
 
@@ -4277,26 +4277,24 @@ class Morpheus::Cli::Clusters
4277
4277
 
4278
4278
  if provision_type && provision_type['hasZonePools']
4279
4279
  # Resource pool
4280
- if !['resourcePoolId', 'resourceGroup', 'vpc'].find { |it| cloud['config'][it] && cloud['config'][it].length > 0 }
4281
- resource_pool_id = nil
4282
- resource_pool = options[:resourcePool] ? find_cloud_resource_pool_by_name_or_id(cloud['id'], options[:resourcePool]) : nil
4280
+ resource_pool_id = nil
4281
+ resource_pool = options[:resourcePool] ? find_cloud_resource_pool_by_name_or_id(cloud['id'], options[:resourcePool]) : nil
4283
4282
 
4284
- if !resource_pool
4285
- resource_pool_options = @options_interface.options_for_source('zonePools', {groupId: group['id'], zoneId: cloud['id']}.merge(service_plan ? {planId: service_plan['id']} : {}))['data'].reject { |it| it['id'].nil? && it['name'].nil? }
4283
+ if !resource_pool
4284
+ resource_pool_options = @options_interface.options_for_source('zonePools', {groupId: group['id'], zoneId: cloud['id']}.merge(service_plan ? {planId: service_plan['id']} : {}))['data'].reject { |it| it['id'].nil? && it['name'].nil? }
4286
4285
 
4287
- if resource_pool_options.empty?
4288
- print_red_alert "Cloud #{cloud['name']} has no available resource pools"
4289
- exit 1
4290
- elsif resource_pool_options.count > 1 && !options[:no_prompt]
4291
- resource_pool_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resourcePool', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.'}],options[:options],api_client, {})['resourcePool']
4292
- else
4293
- resource_pool_id = resource_pool_options.first['id']
4294
- end
4295
- if resource_pool_id.to_s["poolGroup-"]
4296
- resource_pool = @resource_pool_groups_interface.get(resource_pool_id)['resourcePoolGroup']
4297
- else
4298
- resource_pool = @cloud_resource_pools_interface.get(cloud['id'], resource_pool_id)['resourcePool']
4299
- end
4286
+ if resource_pool_options.empty?
4287
+ print_red_alert "Cloud #{cloud['name']} has no available resource pools"
4288
+ exit 1
4289
+ elsif resource_pool_options.count > 1 && !options[:no_prompt]
4290
+ resource_pool_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resourcePool', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.'}],options[:options],api_client, {})['resourcePool']
4291
+ else
4292
+ resource_pool_id = resource_pool_options.first['id']
4293
+ end
4294
+ if resource_pool_id.to_s["poolGroup-"]
4295
+ resource_pool = @resource_pool_groups_interface.get(resource_pool_id)['resourcePoolGroup']
4296
+ else
4297
+ resource_pool = @cloud_resource_pools_interface.get(cloud['id'], resource_pool_id)['resourcePool']
4300
4298
  end
4301
4299
  end
4302
4300
  end
@@ -0,0 +1,373 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::EmailTemplates
4
+ include Morpheus::Cli::CliCommand
5
+
6
+ register_subcommands :list, :get, :add, :update, :remove, :execute
7
+
8
+ def connect(opts)
9
+ @api_client = establish_remote_appliance_connection(opts)
10
+ @email_templates_interface = @api_client.email_templates
11
+ end
12
+
13
+ def handle(args)
14
+ handle_subcommand(args)
15
+ end
16
+
17
+ def list(args)
18
+ params = {}
19
+ options = {}
20
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
21
+ opts.banner = subcommand_usage("[search]")
22
+
23
+ build_standard_list_options(opts, options)
24
+ opts.footer = "List email templates."
25
+ end
26
+ optparse.parse!(args)
27
+ connect(options)
28
+ if args.count > 0
29
+ options[:phrase] = args.join(" ")
30
+ end
31
+ params.merge!(parse_list_options(options))
32
+ @email_templates_interface.setopts(options)
33
+ if options[:dry_run]
34
+ print_dry_run @email_templates.dry.list(params)
35
+ return
36
+ end
37
+
38
+ json_response = @email_templates_interface.list(params)
39
+ templates = json_response['emailTemplates']
40
+ render_response(json_response, options, 'templates') do
41
+ title = "Morpheus Email Templates"
42
+ subtitles = []
43
+ subtitles += parse_list_subtitles(options)
44
+ print_h1 title, subtitles
45
+ if templates.empty?
46
+ print cyan,"No templates found.",reset,"\n"
47
+ else
48
+ print cyan
49
+ print_templates_table(templates, options)
50
+ print_results_pagination(json_response)
51
+ end
52
+ print reset,"\n"
53
+ end
54
+ if templates.empty?
55
+ return 1, "no templates found"
56
+ else
57
+ return 0, nil
58
+ end
59
+ end
60
+
61
+ def get(args)
62
+ params = {}
63
+ options = {}
64
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
65
+ opts.banner = subcommand_usage("[emailTemplate]")
66
+ build_standard_get_options(opts, options)
67
+ opts.footer = <<-EOT
68
+ Get details about a specific email template.
69
+ [emailTemplate] is required. This is the name or id of an emailTemplate.
70
+ EOT
71
+ end
72
+ optparse.parse!(args)
73
+ verify_args!(args:args, optparse:optparse, min:1)
74
+ connect(options)
75
+ parse_options(options, params)
76
+ id_list = parse_id_list(args)
77
+ return run_command_for_each_arg(id_list) do |arg|
78
+ _get(arg, params, options)
79
+ end
80
+ end
81
+
82
+ def _get(id, params, options)
83
+ if id.to_s !~ /\A\d{1,}\Z/
84
+ record = find_by_name_or_id('emailTemplate', id)
85
+ if record.nil?
86
+ return 1, "EmailTemplate not found for '#{id}'"
87
+ end
88
+ id = record['id']
89
+ end
90
+ options[:params] = params # parse_options(options, params)
91
+ options.delete(:payload)
92
+ execute_api(@email_templates_interface, :get, [id], options, 'emailTemplate') do |json_response|
93
+ email_template = json_response['emailTemplate']
94
+ print_h1 "EmailTemplate Details", [], options
95
+ print cyan
96
+ columns = email_template_column_definitions
97
+ print_description_list(columns, email_template, options)
98
+ print reset,"\n"
99
+ end
100
+ end
101
+
102
+ def add(args)
103
+ options = {}
104
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
105
+ opts.banner = subcommand_usage( "[name]")
106
+ opts.on("--type [TEXT]", String, "Type") do |val|
107
+ options[:type] = val.to_s
108
+ end
109
+ opts.on( '--template [TEXT]', "Template" ) do |val|
110
+ options[:template] = val.to_s
111
+ end
112
+ opts.on('--accounts LIST', Array, "Tenant accounts, comma separated list of account IDs") do |list|
113
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
114
+ options[:accounts] = []
115
+ else
116
+ options[:accounts] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
117
+ end
118
+ end
119
+
120
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
121
+ opts.footer = "Create an email template.\n" +
122
+ "[name] is required. This is the name of the new template."
123
+ end
124
+
125
+ optparse.parse!(args)
126
+ if args.count > 1
127
+ raise_command_error "wrong number of arguments, expected 0-2 and got (#{args.count}) #{args}\n#{optparse}"
128
+ end
129
+ connect(options)
130
+
131
+ begin
132
+ payload = nil
133
+ if options[:payload]
134
+ payload = options[:payload]
135
+ # support -O OPTION switch on top of --payload
136
+ payload['emailTemplate'] ||= {}
137
+ if options[:options]
138
+ payload['emailTemplate'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
139
+ end
140
+
141
+ if options[:type]
142
+ payload['emailTemplate']['type'] = options[:type]
143
+ end
144
+
145
+ if options[:template]
146
+ payload['emailTemplate']['template'] = options[:template]
147
+ end
148
+
149
+ if options[:accounts]
150
+ payload['emailTemplate']['accounts'] = options[:accounts]
151
+ end
152
+
153
+ else
154
+ payload = {'emailTemplate' => {}}
155
+
156
+ # Template Type
157
+ template_type_id = nil
158
+ template_type = options[:type] ? find_template_type_by_name_or_id(options[:type]) : nil
159
+
160
+ if template_type
161
+ template_type_id = template_type['id']
162
+ else
163
+ available_template_types = template_types_for_dropdown
164
+
165
+ if available_template_types.empty?
166
+ print_red_alert "A template type is required"
167
+ exit 1
168
+ elsif available_template_types.count > 1 && !options[:no_prompt]
169
+ template_type_code = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'templateType', 'type' => 'select', 'fieldLabel' => 'Template Type', 'selectOptions' => template_types_for_dropdown, 'required' => true, 'description' => 'Select Template Type.'}],options[:options],@api_client,{})
170
+ template_type_code = template_type_code["templateType"]
171
+ else
172
+ template_type_code = available_template_types.first['code']
173
+ end
174
+ #template_type = get_template_types.find { |ct| ct['code'] == template_type_code }
175
+ end
176
+
177
+ payload['emailTemplate']['code'] = template_type_code
178
+ payload['emailTemplate']['template'] = Morpheus::Cli::OptionTypes.file_content_prompt({'fieldName' => 'source', 'fieldLabel' => 'File Content', 'type' => 'file-content', 'required' => true}, {'source' => {'source' => 'local'}}, nil, {})['content']
179
+ # Tenants
180
+ if options[:accounts]
181
+ payload['emailTemplate']['accounts'] = options[:accounts]
182
+ else
183
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'accounts', 'fieldLabel' => 'Tenants', 'type' => 'text', 'required' => false, 'description' => 'Tenant accounts, comma separated list of account IDs'}], options)
184
+ payload['emailTemplate']['accounts'] = v_prompt['accounts']
185
+ end
186
+
187
+ end
188
+ @email_templates_interface.setopts(options)
189
+ if options[:dry_run]
190
+ print_dry_run @email_templates_interface.dry.create(payload)
191
+ return
192
+ end
193
+ json_response = @email_templates_interface.create(payload)
194
+ if options[:json]
195
+ print JSON.pretty_generate(json_response)
196
+ print "\n"
197
+ elsif json_response['success']
198
+ get_args = [json_response["emailTemplate"]["id"]] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
199
+ get(get_args)
200
+ else
201
+ print_rest_errors(json_response, options)
202
+ end
203
+ rescue RestClient::Exception => e
204
+ print_rest_exception(e, options)
205
+ exit 1
206
+ end
207
+ end
208
+
209
+ def update(args)
210
+ options = {}
211
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
212
+ opts.banner = subcommand_usage( "[emailTemplate] --template")
213
+ opts.on("--template TEMPLATE", String, "Updates Email Template") do |val|
214
+ options[:template] = val.to_s
215
+ end
216
+ opts.on('--accounts LIST', Array, "Tenant accounts, comma separated list of account IDs") do |list|
217
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
218
+ options[:accounts] = []
219
+ else
220
+ options[:accounts] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
221
+ end
222
+ end
223
+
224
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
225
+ opts.footer = "Update an Email Template.\n" +
226
+ "[emailTemplate] is required. This is the name or id of an existing email template."
227
+ end
228
+
229
+ optparse.parse!(args)
230
+ if args.count != 1
231
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
232
+ end
233
+ connect(options)
234
+
235
+ begin
236
+ payload = nil
237
+ email_template = nil
238
+
239
+ if options[:payload]
240
+ payload = options[:payload]
241
+ # support -O OPTION switch on top of --payload
242
+ if options[:options]
243
+ payload['emailTemplate'] ||= {}
244
+ payload['emailTemplate'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
245
+ end
246
+
247
+ if !payload['emailTemplate'].empty?
248
+ email_template = find_by_name_or_id('emailTemplate', payload['emailTemplate']['id'] || payload['emailTemplate']['name'])
249
+ end
250
+ else
251
+ email_template = find_by_name_or_id('emailTemplate', args[0])
252
+ template_payload = {}
253
+ if options[:template]
254
+ template_payload['template'] = options[:template]
255
+ end
256
+ if options[:accounts]
257
+ template_payload['accounts'] = options[:accounts]
258
+ end
259
+
260
+ payload = {"emailTemplate" => template_payload}
261
+ end
262
+
263
+ if !email_template
264
+ print_red_alert "No templates available for update"
265
+ exit 1
266
+ end
267
+
268
+ if payload.empty?
269
+ print_green_success "Nothing to update"
270
+ exit 1
271
+ end
272
+
273
+ @email_templates_interface.setopts(options)
274
+ if options[:dry_run]
275
+ print_dry_run @email_templates_interface.dry.update(email_template['id'], payload)
276
+ return
277
+ end
278
+ json_response = @email_templates_interface.update(email_template['id'], payload)
279
+ if options[:json]
280
+ print JSON.pretty_generate(json_response)
281
+ print "\n"
282
+ elsif json_response['success']
283
+ get_args = [json_response["emailTemplate"]["id"]] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
284
+ get(get_args)
285
+ else
286
+ print_rest_errors(json_response, options)
287
+ end
288
+ end
289
+ end
290
+
291
+ def remove(args)
292
+ options = {}
293
+ query_params = {}
294
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
295
+ opts.banner = subcommand_usage("[emailTemplate]")
296
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
297
+ opts.footer = "Delete an email template.\n" +
298
+ "[emailTemplate] is required. This is the id of an existing email template.\n" +
299
+ "Note: You cannot remove System Templates, only those that you own/created."
300
+ end
301
+ optparse.parse!(args)
302
+ if args.count != 1
303
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
304
+ end
305
+ connect(options)
306
+
307
+ begin
308
+ email_template = find_by_name_or_id('emailTemplate', args[0])
309
+
310
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the email template '#{email_template['name']}'?", options)
311
+ return 9, "aborted command"
312
+ end
313
+ @email_templates_interface.setopts(options)
314
+ if options[:dry_run]
315
+ print_dry_run @email_templates_interface.dry.destroy(email_template['id'], query_params)
316
+ return
317
+ end
318
+ json_response = @email_templates_interface.destroy(email_template['id'], query_params)
319
+ if options[:json]
320
+ print JSON.pretty_generate(json_response)
321
+ print "\n"
322
+ elsif !options[:quiet]
323
+ msg = "Email Template #{email_template['name']} is being removed..."
324
+ if json_response['msg'] != nil && json_response['msg'] != ''
325
+ msg = json_response['msg']
326
+ end
327
+ print_green_success msg
328
+
329
+ end
330
+ rescue RestClient::Exception => e
331
+ print_rest_exception(e, options)
332
+ exit 1
333
+ end
334
+ end
335
+
336
+
337
+
338
+ def print_templates_table(templates, opts={})
339
+ columns = [
340
+ {"ID" => lambda {|it| it['id'] } },
341
+ {"Name" => lambda {|it| it['name'] } },
342
+ {"Owner" => lambda {|it| it['owner']['name'] || 'System'} },
343
+ {"Tenants" => lambda {|it| it['accounts'].collect {|a| a['name'] }.join(', ') rescue it['owner'] ? 'Global' : ''} }
344
+
345
+ # {"UPDATED" => lambda {|it| format_local_dt(it['lastUpdated']) } },
346
+ ]
347
+ if opts[:include_fields]
348
+ columns = opts[:include_fields]
349
+ end
350
+ print as_pretty_table(templates, columns, opts)
351
+ end
352
+
353
+ def email_template_column_definitions()
354
+ {
355
+ "ID" => 'id',
356
+ "Name" => 'name',
357
+ "Owner" => lambda {|it| it['owner']['name'] || 'System' },
358
+ "Template" => lambda {|it| it['template'] rescue '' },
359
+ "Tenants" => lambda {|it| it['accounts'].collect {|a| a['name'] }.join(', ') rescue it['owner'] ? 'Global' : ''}
360
+ }
361
+ end
362
+
363
+ def template_types_for_dropdown
364
+ get_template_types.collect {|it| {'name' => it['name'], 'code' => it['value'], 'value' => it['value']} }
365
+ end
366
+
367
+ def get_template_types(refresh=false)
368
+ if !@template_types || refresh
369
+ @template_types = @email_templates_interface.template_types()['types']
370
+ end
371
+ @template_types
372
+ end
373
+ end
@@ -362,7 +362,7 @@ module Morpheus::Cli::PrintHelper
362
362
  end
363
363
  end
364
364
  end
365
- if payload && !payload.empty?
365
+ if payload #&& !payload.empty?
366
366
  out << + ' \\' + "\n"
367
367
  if headers && headers['Content-Type'] == 'application/json'
368
368
  if payload.is_a?(String)
@@ -386,7 +386,7 @@ module Morpheus::Cli::PrintHelper
386
386
  # pretty_size = Filesize.from("#{payload.size} B").pretty.strip
387
387
  pretty_size = "#{payload.size} B"
388
388
  # print "File: #{payload.path} (#{payload.size} bytes)"
389
- out << " -d @#{payload.path}"
389
+ out << " --data-binary @#{payload.path}"
390
390
  elsif payload.is_a?(String)
391
391
  out << " -d '#{payload}'"
392
392
  elsif payload.respond_to?(:map)
@@ -1256,6 +1256,7 @@ module Morpheus
1256
1256
  file_params = passed_file_params
1257
1257
  end
1258
1258
  is_required = option_type['required']
1259
+
1259
1260
  if file_params['source']
1260
1261
  file_params['sourceType'] = file_params.delete('source')
1261
1262
  end
@@ -1482,9 +1483,13 @@ module Morpheus
1482
1483
  end
1483
1484
 
1484
1485
  def self.sort_option_types(option_types)
1485
- option_types.select {|it| (it['fieldGroup'] || 'default').casecmp?('default')}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i} +
1486
- option_types.reject {|it| ['default', 'advanced'].include?((it['fieldGroup'] || 'default').downcase)}.sort{|a,b| a['displayOrder'] <=> b['displayOrder']}.group_by{|it| it['fieldGroup']}.values.collect { |it| it.sort{|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}}.flatten +
1487
- option_types.select {|it| 'advanced'.casecmp?(it['fieldGroup'])}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}
1486
+ if option_types.find {|it| it['fieldGroup'] || it['displayOrder'] }
1487
+ option_types.select {|it| (it['fieldGroup'] || 'default').casecmp?('default')}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i} +
1488
+ option_types.reject {|it| ['default', 'advanced'].include?((it['fieldGroup'] || 'default').downcase)}.sort{|a,b| a['displayOrder'] <=> b['displayOrder']}.group_by{|it| it['fieldGroup']}.values.collect { |it| it.sort{|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}}.flatten +
1489
+ option_types.select {|it| 'advanced'.casecmp?(it['fieldGroup'])}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}
1490
+ else
1491
+ option_types
1492
+ end
1488
1493
  end
1489
1494
 
1490
1495
  def self.display_select_options(opt, select_options = [], paging = nil)
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "7.0.1"
4
+ VERSION = "7.0.2"
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morpheus-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.1
4
+ version: 7.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2024-04-17 00:00:00.000000000 Z
14
+ date: 2024-05-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -229,6 +229,7 @@ files:
229
229
  - lib/morpheus/api/datastores_interface.rb
230
230
  - lib/morpheus/api/deploy_interface.rb
231
231
  - lib/morpheus/api/deployments_interface.rb
232
+ - lib/morpheus/api/email_templates_interface.rb
232
233
  - lib/morpheus/api/environments_interface.rb
233
234
  - lib/morpheus/api/execute_schedules_interface.rb
234
235
  - lib/morpheus/api/execution_request_interface.rb
@@ -414,6 +415,7 @@ files:
414
415
  - lib/morpheus/cli/commands/echo_command.rb
415
416
  - lib/morpheus/cli/commands/edit_profile_command.rb
416
417
  - lib/morpheus/cli/commands/edit_rc_command.rb
418
+ - lib/morpheus/cli/commands/email_templates_command.rb
417
419
  - lib/morpheus/cli/commands/environments_command.rb
418
420
  - lib/morpheus/cli/commands/execute_schedules_command.rb
419
421
  - lib/morpheus/cli/commands/execution_request_command.rb