foreman_remote_execution 4.1.0 → 4.3.0
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/.github/workflows/js_ci.yml +29 -0
- data/.github/workflows/{ci.yml → ruby_ci.yml} +22 -50
- data/.prettierrc +4 -0
- data/.rubocop.yml +13 -49
- data/.rubocop_todo.yml +326 -102
- data/Gemfile +1 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +21 -3
- data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_controller_extensions.rb +26 -0
- data/app/controllers/job_templates_controller.rb +1 -1
- data/app/controllers/ui_job_wizard_controller.rb +18 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +38 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +9 -1
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +38 -14
- data/app/models/foreign_input_set.rb +1 -1
- data/app/models/host_status/execution_status.rb +7 -0
- data/app/models/job_invocation.rb +2 -1
- data/app/models/job_invocation_composer.rb +1 -1
- data/app/models/remote_execution_feature.rb +5 -2
- data/app/models/remote_execution_provider.rb +6 -1
- data/app/services/remote_execution_proxy_selector.rb +3 -0
- data/app/views/api/v2/job_invocations/main.json.rabl +1 -1
- data/app/views/api/v2/registration/_form.html.erb +12 -0
- data/app/views/template_invocations/_output_line_set.html.erb +1 -1
- data/app/views/template_invocations/show.html.erb +30 -23
- data/app/views/templates/ssh/package_action.erb +1 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20200820122057_add_proxy_selector_override_to_remote_execution_feature.rb +5 -0
- data/foreman_remote_execution.gemspec +1 -2
- data/lib/foreman_remote_execution/engine.rb +21 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +6 -6
- data/test/functional/api/v2/job_invocations_controller_test.rb +42 -3
- data/test/functional/api/v2/registration_controller_test.rb +73 -0
- data/test/functional/ui_job_wizard_controller_test.rb +16 -0
- data/test/unit/actions/run_hosts_job_test.rb +1 -0
- data/webpack/JobWizard/JobWizard.js +32 -0
- data/webpack/JobWizard/index.js +32 -0
- data/webpack/Routes/routes.js +12 -0
- data/webpack/__mocks__/foremanReact/history.js +1 -0
- data/webpack/global_index.js +4 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +5 -1
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +6 -2
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -1
- metadata +24 -23
@@ -98,6 +98,7 @@ handle_zypp_res_codes () {
|
|
98
98
|
end
|
99
99
|
-%>
|
100
100
|
[ -x "$(command -v subscription-manager)" ] && subscription-manager refresh
|
101
|
+
export DEBIAN_FRONTEND=noninteractive
|
101
102
|
apt-get -y update
|
102
103
|
apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= action %> <%= input("package") %>
|
103
104
|
<% elsif package_manager == 'zypper' -%>
|
data/config/routes.rb
CHANGED
@@ -43,6 +43,9 @@ Rails.application.routes.draw do
|
|
43
43
|
get 'cockpit/host_ssh_params/:id', to: 'cockpit#host_ssh_params'
|
44
44
|
end
|
45
45
|
get 'cockpit/redirect', to: 'cockpit#redirect'
|
46
|
+
get 'ui_job_wizard/categories', to: 'ui_job_wizard#categories'
|
47
|
+
|
48
|
+
match '/experimental/job_wizard', to: 'react#index', :via => [:get]
|
46
49
|
|
47
50
|
namespace :api, :defaults => {:format => 'json'} do
|
48
51
|
scope '(:apiv)', :module => :v2, :defaults => {:apiv => 'v2'}, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2, :default => true) do
|
@@ -55,6 +58,8 @@ Rails.application.routes.draw do
|
|
55
58
|
post 'cancel'
|
56
59
|
post 'rerun'
|
57
60
|
get 'template_invocations', :to => 'template_invocations#template_invocations'
|
61
|
+
get 'outputs'
|
62
|
+
post 'outputs'
|
58
63
|
end
|
59
64
|
end
|
60
65
|
|
@@ -26,9 +26,8 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.add_dependency 'deface'
|
27
27
|
s.add_dependency 'dynflow', '>= 1.0.2', '< 2.0.0'
|
28
28
|
s.add_dependency 'foreman_remote_execution_core'
|
29
|
-
s.add_dependency 'foreman-tasks', '>= 0.
|
29
|
+
s.add_dependency 'foreman-tasks', '>= 4.0.0'
|
30
30
|
|
31
31
|
s.add_development_dependency 'factory_bot_rails', '~> 4.8.0'
|
32
|
-
s.add_development_dependency 'rubocop', '~> 0.80.0'
|
33
32
|
s.add_development_dependency 'rdoc'
|
34
33
|
end
|
@@ -53,6 +53,7 @@ module ForemanRemoteExecution
|
|
53
53
|
|
54
54
|
initializer 'foreman_remote_execution.register_plugin', before: :finisher_hook do |_app|
|
55
55
|
Foreman::Plugin.register :foreman_remote_execution do
|
56
|
+
register_global_js_file 'global'
|
56
57
|
requires_foreman '>= 2.2'
|
57
58
|
|
58
59
|
apipie_documented_controllers ["#{ForemanRemoteExecution::Engine.root}/app/controllers/api/v2/*.rb"]
|
@@ -68,7 +69,8 @@ module ForemanRemoteExecution
|
|
68
69
|
permission :view_job_templates, { :job_templates => [:index, :show, :revision, :auto_complete_search, :auto_complete_job_category, :preview, :export],
|
69
70
|
:'api/v2/job_templates' => [:index, :show, :revision, :export],
|
70
71
|
:'api/v2/template_inputs' => [:index, :show],
|
71
|
-
:'api/v2/foreign_input_sets' => [:index, :show]
|
72
|
+
:'api/v2/foreign_input_sets' => [:index, :show],
|
73
|
+
:ui_job_wizard => [:categories]}, :resource_type => 'JobTemplate'
|
72
74
|
permission :create_job_templates, { :job_templates => [:new, :create, :clone_template, :import],
|
73
75
|
:'api/v2/job_templates' => [:create, :clone, :import] }, :resource_type => 'JobTemplate'
|
74
76
|
permission :edit_job_templates, { :job_templates => [:edit, :update],
|
@@ -83,7 +85,7 @@ module ForemanRemoteExecution
|
|
83
85
|
permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh, :rerun, :preview_hosts],
|
84
86
|
'api/v2/job_invocations' => [:create, :rerun] }, :resource_type => 'JobInvocation'
|
85
87
|
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search], :template_invocations => [:show],
|
86
|
-
'api/v2/job_invocations' => [:index, :show, :output, :raw_output] }, :resource_type => 'JobInvocation'
|
88
|
+
'api/v2/job_invocations' => [:index, :show, :output, :raw_output, :outputs] }, :resource_type => 'JobInvocation'
|
87
89
|
permission :view_template_invocations, { :template_invocations => [:show],
|
88
90
|
'api/v2/template_invocations' => [:template_invocations] }, :resource_type => 'TemplateInvocation'
|
89
91
|
permission :create_template_invocations, {}, :resource_type => 'TemplateInvocation'
|
@@ -137,6 +139,13 @@ module ForemanRemoteExecution
|
|
137
139
|
parent: :monitor_menu,
|
138
140
|
after: :audits
|
139
141
|
|
142
|
+
menu :labs_menu, :job_wizard,
|
143
|
+
url_hash: { controller: 'job_wizard', action: :index },
|
144
|
+
caption: N_('Job wizard'),
|
145
|
+
parent: :lab_features_menu,
|
146
|
+
url: 'experimental/job_wizard',
|
147
|
+
after: :host_wizard
|
148
|
+
|
140
149
|
register_custom_status HostStatus::ExecutionStatus
|
141
150
|
# add dashboard widget
|
142
151
|
# widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1
|
@@ -151,6 +160,14 @@ module ForemanRemoteExecution
|
|
151
160
|
extend_rabl_template 'api/v2/subnets/show', 'api/v2/subnets/remote_execution_proxies'
|
152
161
|
parameter_filter ::Subnet, :remote_execution_proxy_ids
|
153
162
|
describe_host { overview_buttons_provider :host_overview_buttons }
|
163
|
+
|
164
|
+
# Extend Registration module
|
165
|
+
extend_allowed_registration_vars :remote_execution_interface
|
166
|
+
extend_page 'registration/_form' do |cx|
|
167
|
+
cx.add_pagelet :global_registration, name: N_('Remote Execution'), partial: 'api/v2/registration/form', priority: 100, id: 'remote_execution_interface'
|
168
|
+
end
|
169
|
+
ForemanTasks.dynflow.eager_load_actions!
|
170
|
+
extend_observable_events(::Dynflow::Action.descendants.select { |klass| klass <= ::Actions::ObservableAction }.map(&:namespaced_event_names))
|
154
171
|
end
|
155
172
|
end
|
156
173
|
|
@@ -207,6 +224,8 @@ module ForemanRemoteExecution
|
|
207
224
|
ForemanRemoteExecution.register_rex_feature
|
208
225
|
|
209
226
|
::Api::V2::SubnetsController.include ::ForemanRemoteExecution::Concerns::Api::V2::SubnetsControllerExtensions
|
227
|
+
::Api::V2::RegistrationController.prepend ::ForemanRemoteExecution::Concerns::Api::V2::RegistrationControllerExtensions
|
228
|
+
::Api::V2::RegistrationController.include ::ForemanRemoteExecution::Concerns::Api::V2::RegistrationControllerExtensions::ApipieExtensions
|
210
229
|
end
|
211
230
|
|
212
231
|
initializer 'foreman_remote_execution.register_gettext', after: :load_config_initializers do |_app|
|
data/package.json
CHANGED
@@ -21,16 +21,16 @@
|
|
21
21
|
},
|
22
22
|
"devDependencies": {
|
23
23
|
"@babel/core": "^7.7.0",
|
24
|
-
"@theforeman/builder": "^4.
|
25
|
-
"@theforeman/eslint-plugin-foreman": "^4.
|
26
|
-
"@theforeman/stories": "^4.
|
27
|
-
"@theforeman/test": "^4.
|
28
|
-
"@theforeman/vendor-dev": "^4.
|
24
|
+
"@theforeman/builder": "^4.14.0",
|
25
|
+
"@theforeman/eslint-plugin-foreman": "^4.14.0",
|
26
|
+
"@theforeman/stories": "^4.14.0",
|
27
|
+
"@theforeman/test": "^4.14.0",
|
28
|
+
"@theforeman/vendor-dev": "^4.14.0",
|
29
29
|
"babel-eslint": "^10.0.0",
|
30
30
|
"eslint": "^6.8.0",
|
31
31
|
"prettier": "^1.19.1"
|
32
32
|
},
|
33
33
|
"peerDependencies": {
|
34
|
-
"@theforeman/vendor": ">= 4.
|
34
|
+
"@theforeman/vendor": ">= 4.14.0"
|
35
35
|
}
|
36
36
|
}
|
@@ -137,6 +137,7 @@ module Api
|
|
137
137
|
|
138
138
|
test 'should provide empty output for host which does not have a task yet' do
|
139
139
|
JobInvocation.any_instance.expects(:sub_task_for_host).returns(nil)
|
140
|
+
JobInvocation.any_instance.expects(:finished?).returns(false)
|
140
141
|
get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
141
142
|
result = ActiveSupport::JSON.decode(@response.body)
|
142
143
|
assert_equal result['refresh'], true
|
@@ -144,6 +145,15 @@ module Api
|
|
144
145
|
assert_response :success
|
145
146
|
end
|
146
147
|
|
148
|
+
test 'should provide empty output marked as done for host which does not have a task when the job is finished' do
|
149
|
+
JobInvocation.any_instance.expects(:sub_task_for_host).returns(nil)
|
150
|
+
get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
151
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
152
|
+
assert_equal result['refresh'], false
|
153
|
+
assert_equal result['output'], []
|
154
|
+
assert_response :success
|
155
|
+
end
|
156
|
+
|
147
157
|
test 'should fail with 404 for non-existing job invocation' do
|
148
158
|
invocation_id = @invocation.id + 1
|
149
159
|
assert_empty JobInvocation.where(:id => invocation_id)
|
@@ -160,6 +170,35 @@ module Api
|
|
160
170
|
end
|
161
171
|
end
|
162
172
|
|
173
|
+
describe '#outputs' do
|
174
|
+
test 'should provide outputs for hosts in the job' do
|
175
|
+
get :outputs, params: { :id => @invocation.id }
|
176
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
177
|
+
host_output = result['outputs'].first
|
178
|
+
assert_equal host_output['host_id'], @invocation.targeting.host_ids.first
|
179
|
+
assert_equal host_output['refresh'], false
|
180
|
+
assert_equal host_output['output'], []
|
181
|
+
assert_response :success
|
182
|
+
end
|
183
|
+
|
184
|
+
test 'should provide outputs for selected hosts in the job' do
|
185
|
+
post :outputs, params: { :id => @invocation.id, :search_query => "id = #{@invocation.targeting.host_ids.first}" }, as: :json
|
186
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
187
|
+
host_output = result['outputs'].first
|
188
|
+
assert_equal host_output['host_id'], @invocation.targeting.host_ids.first
|
189
|
+
assert_equal host_output['refresh'], false
|
190
|
+
assert_equal host_output['output'], []
|
191
|
+
assert_response :success
|
192
|
+
end
|
193
|
+
|
194
|
+
test 'should provide outputs for hosts in the job matching a search query' do
|
195
|
+
get :outputs, params: { :id => @invocation.id, :search_query => "name = definitely_not_in_the_job" }
|
196
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
197
|
+
assert_equal result['outputs'], []
|
198
|
+
assert_response :success
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
163
202
|
describe 'raw output' do
|
164
203
|
let(:fake_output) do
|
165
204
|
(1..5).map do |i|
|
@@ -172,7 +211,7 @@ module Api
|
|
172
211
|
let(:host) { @invocation.targeting.hosts.first }
|
173
212
|
|
174
213
|
test 'should provide raw output for a host' do
|
175
|
-
JobInvocation.any_instance.expects(:task).returns(OpenStruct.new(:scheduled? => false))
|
214
|
+
JobInvocation.any_instance.expects(:task).times(3).returns(OpenStruct.new(:scheduled? => false, :pending? => false))
|
176
215
|
JobInvocation.any_instance.expects(:sub_task_for_host).returns(fake_task)
|
177
216
|
get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
178
217
|
result = ActiveSupport::JSON.decode(@response.body)
|
@@ -185,7 +224,7 @@ module Api
|
|
185
224
|
start_time = Time.now
|
186
225
|
JobInvocation.any_instance
|
187
226
|
.expects(:task).twice
|
188
|
-
.returns(OpenStruct.new(:scheduled? => true, :start_at => start_time))
|
227
|
+
.returns(OpenStruct.new(:scheduled? => true, :start_at => start_time, :pending? => true))
|
189
228
|
JobInvocation.any_instance.expects(:sub_task_for_host).never
|
190
229
|
get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
191
230
|
result = ActiveSupport::JSON.decode(@response.body)
|
@@ -197,7 +236,7 @@ module Api
|
|
197
236
|
end
|
198
237
|
|
199
238
|
test 'should provide raw output for host without task' do
|
200
|
-
JobInvocation.any_instance.expects(:task).returns(OpenStruct.new(:scheduled? => false))
|
239
|
+
JobInvocation.any_instance.expects(:task).times(3).returns(OpenStruct.new(:scheduled? => false, :pending? => true))
|
201
240
|
JobInvocation.any_instance.expects(:sub_task_for_host)
|
202
241
|
get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
|
203
242
|
result = ActiveSupport::JSON.decode(@response.body)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
module Api
|
4
|
+
module V2
|
5
|
+
# Tests for the extra methods to play roles on a Host
|
6
|
+
class RegistrationControllerTest < ActionController::TestCase
|
7
|
+
describe 'host registration' do
|
8
|
+
let(:organization) { FactoryBot.create(:organization) }
|
9
|
+
let(:tax_location) { FactoryBot.create(:location) }
|
10
|
+
let(:template) do
|
11
|
+
FactoryBot.create(
|
12
|
+
:provisioning_template,
|
13
|
+
template_kind: template_kinds(:host_init_config),
|
14
|
+
template: 'template content <%= @host.name %>',
|
15
|
+
locations: [tax_location],
|
16
|
+
organizations: [organization]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
let(:os) do
|
20
|
+
FactoryBot.create(
|
21
|
+
:operatingsystem,
|
22
|
+
:with_associations,
|
23
|
+
family: 'Redhat',
|
24
|
+
provisioning_templates: [
|
25
|
+
template,
|
26
|
+
]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:host_params) do
|
31
|
+
{ host: { name: 'centos-test.example.com',
|
32
|
+
managed: false, build: false,
|
33
|
+
organization_id: organization.id,
|
34
|
+
location_id: tax_location.id,
|
35
|
+
operatingsystem_id: os.id } }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'remote_execution_interface' do
|
39
|
+
setup do
|
40
|
+
Setting[:default_host_init_config_template] = template.name
|
41
|
+
@host = Host.create(host_params[:host])
|
42
|
+
@interface0 = FactoryBot.create(:nic_managed, host: @host, identifier: 'dummy0', execution: false)
|
43
|
+
end
|
44
|
+
|
45
|
+
test 'with existing interface' do
|
46
|
+
params = host_params.merge(remote_execution_interface: @interface0.identifier)
|
47
|
+
|
48
|
+
post :host, params: params, session: set_session_user
|
49
|
+
assert_response :success
|
50
|
+
assert @interface0.reload.execution
|
51
|
+
end
|
52
|
+
|
53
|
+
test 'with not-existing interface' do
|
54
|
+
params = host_params.merge(remote_execution_interface: 'dummy999')
|
55
|
+
|
56
|
+
post :host, params: params, session: set_session_user
|
57
|
+
assert_response :not_found
|
58
|
+
end
|
59
|
+
|
60
|
+
test 'with multiple interfaces' do
|
61
|
+
interface1 = FactoryBot.create(:nic_managed, host: @host, identifier: 'dummy1', execution: false)
|
62
|
+
params = host_params.merge(remote_execution_interface: interface1.identifier)
|
63
|
+
|
64
|
+
post :host, params: params, session: set_session_user
|
65
|
+
assert_response :success
|
66
|
+
refute @interface0.reload.execution
|
67
|
+
assert interface1.reload.execution
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class UiJobWizardControllerTest < ActionController::TestCase
|
4
|
+
def setup
|
5
|
+
FactoryBot.create(:job_template, :job_category => 'cat1')
|
6
|
+
FactoryBot.create(:job_template, :job_category => 'cat2')
|
7
|
+
FactoryBot.create(:job_template, :job_category => 'cat2')
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'should respond with categories' do
|
11
|
+
get :categories, :params => {}, :session => set_session_user
|
12
|
+
assert_response :success
|
13
|
+
res = JSON.parse @response.body
|
14
|
+
assert_equal ['cat1','cat2'], res['job_categories']
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Wizard } from '@patternfly/react-core';
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import history from 'foremanReact/history';
|
5
|
+
|
6
|
+
export const JobWizard = () => {
|
7
|
+
const steps = [
|
8
|
+
{
|
9
|
+
name: __('Category and template'),
|
10
|
+
component: <p>Category and template</p>,
|
11
|
+
},
|
12
|
+
{ name: __('Target hosts'), component: <p>TargetHosts </p> },
|
13
|
+
{ name: __('Advanced fields'), component: <p> AdvancedFields </p> },
|
14
|
+
{ name: __('Schedule'), component: <p>Schedule</p> },
|
15
|
+
{
|
16
|
+
name: __('Review details'),
|
17
|
+
component: <p>ReviewDetails</p>,
|
18
|
+
nextButtonText: 'Run',
|
19
|
+
},
|
20
|
+
];
|
21
|
+
const title = __('Run Job');
|
22
|
+
return (
|
23
|
+
<Wizard
|
24
|
+
onClose={() => history.goBack()}
|
25
|
+
navAriaLabel={`${title} steps`}
|
26
|
+
steps={steps}
|
27
|
+
height="70vh"
|
28
|
+
/>
|
29
|
+
);
|
30
|
+
};
|
31
|
+
|
32
|
+
export default JobWizard;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Title, Divider } from '@patternfly/react-core';
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
5
|
+
import { JobWizard } from './JobWizard';
|
6
|
+
|
7
|
+
const JobWizardPage = () => {
|
8
|
+
const title = __('Run job');
|
9
|
+
const breadcrumbOptions = {
|
10
|
+
breadcrumbItems: [
|
11
|
+
{ caption: __('Jobs'), url: `/jobs` },
|
12
|
+
{ caption: title },
|
13
|
+
],
|
14
|
+
};
|
15
|
+
return (
|
16
|
+
<PageLayout
|
17
|
+
header={title}
|
18
|
+
breadcrumbOptions={breadcrumbOptions}
|
19
|
+
searchable={false}
|
20
|
+
>
|
21
|
+
<React.Fragment>
|
22
|
+
<Title headingLevel="h2" size="2xl">
|
23
|
+
{title}
|
24
|
+
</Title>
|
25
|
+
<Divider component="div" />
|
26
|
+
<JobWizard />
|
27
|
+
</React.Fragment>
|
28
|
+
</PageLayout>
|
29
|
+
);
|
30
|
+
};
|
31
|
+
|
32
|
+
export default JobWizardPage;
|
@@ -0,0 +1 @@
|
|
1
|
+
export default { goBack: () => null };
|
@@ -51,8 +51,12 @@ const TargetingHosts = ({ apiStatus, items }) => {
|
|
51
51
|
};
|
52
52
|
|
53
53
|
TargetingHosts.propTypes = {
|
54
|
-
apiStatus: PropTypes.string
|
54
|
+
apiStatus: PropTypes.string,
|
55
55
|
items: PropTypes.array.isRequired,
|
56
56
|
};
|
57
57
|
|
58
|
+
TargetingHosts.defaultProps = {
|
59
|
+
apiStatus: null,
|
60
|
+
};
|
61
|
+
|
58
62
|
export default TargetingHosts;
|
@@ -39,7 +39,7 @@ const TargetingHostsPage = ({
|
|
39
39
|
<br />
|
40
40
|
<TargetingHosts apiStatus={apiStatus} items={items} />
|
41
41
|
<Pagination
|
42
|
-
viewType="
|
42
|
+
viewType="table"
|
43
43
|
itemCount={totalHosts}
|
44
44
|
pagination={pagination}
|
45
45
|
onChange={args => handlePagination(args)}
|
@@ -52,11 +52,15 @@ const TargetingHostsPage = ({
|
|
52
52
|
TargetingHostsPage.propTypes = {
|
53
53
|
handleSearch: PropTypes.func.isRequired,
|
54
54
|
searchQuery: PropTypes.string.isRequired,
|
55
|
-
apiStatus: PropTypes.string
|
55
|
+
apiStatus: PropTypes.string,
|
56
56
|
items: PropTypes.array.isRequired,
|
57
57
|
totalHosts: PropTypes.number.isRequired,
|
58
58
|
pagination: PropTypes.object.isRequired,
|
59
59
|
handlePagination: PropTypes.func.isRequired,
|
60
60
|
};
|
61
61
|
|
62
|
+
TargetingHostsPage.defaultProps = {
|
63
|
+
apiStatus: null,
|
64
|
+
};
|
65
|
+
|
62
66
|
export default TargetingHostsPage;
|