foreman_rh_cloud 14.1.0 → 14.1.2
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/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +4 -8
- data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -7
- data/app/services/foreman_rh_cloud/url_remediations_retriever.rb +19 -5
- data/lib/foreman_inventory_upload/async/destroy_organization_hbi_hosts_job.rb +49 -0
- data/lib/foreman_inventory_upload.rb +4 -0
- data/lib/foreman_rh_cloud/engine.rb +1 -0
- data/lib/foreman_rh_cloud/organization_destroy_extensions.rb +10 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +16 -2
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +16 -2
- data/test/jobs/destroy_organization_hbi_hosts_job_test.rb +63 -0
- data/test/unit/organization_destroy_extensions_test.rb +50 -0
- data/test/unit/services/foreman_rh_cloud/url_remediations_retriever_test.rb +82 -5
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +24 -2
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +73 -10
- data/webpack/ForemanRhCloudHelpers.js +22 -0
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +27 -1
- data/webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js +134 -22
- metadata +7 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8561ef219578d2be3678f2576fba36e1cb3f2a91aab82b5438369aa08ddd0a05
|
|
4
|
+
data.tar.gz: 6abcf201d049e27b6dc90fef6f5094a0f6f72d337f49aa6cdf328d8aa92e3516
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf0448da7b88a0ddca0e2726805b9c0fb6e5356651e64ac09d0a18cd16d05a393dab07f4a19c445948a39fd1a4111e17c275c8ffaf33df974cc80a9af1693ba0
|
|
7
|
+
data.tar.gz: 7e780f06f64dc1f5042a10ec48f3c397d5b767bc5b6ac2c08ac7d9cc059518df4415ee442b37216777a471a0286d59a73a56142a6b80cb10b04213d477a9c4d7
|
|
@@ -32,14 +32,10 @@ module InsightsCloud::Api
|
|
|
32
32
|
rescue RestClient::ExceptionWithResponse => e
|
|
33
33
|
response_obj = e.response.presence || e.exception
|
|
34
34
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:error => response_obj.to_s,
|
|
40
|
-
:headers => {},
|
|
41
|
-
:response => response_obj,
|
|
42
|
-
}, status: code
|
|
35
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
36
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
37
|
+
|
|
38
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
43
39
|
rescue StandardError => e
|
|
44
40
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
45
41
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -36,14 +36,10 @@ module InsightsCloud
|
|
|
36
36
|
rescue RestClient::ExceptionWithResponse => e
|
|
37
37
|
response_obj = e.response.presence || e.exception
|
|
38
38
|
code = response_obj.try(:code) || response_obj.try(:http_code) || 500
|
|
39
|
-
|
|
39
|
+
upstream_content_type = response_obj.try(:headers)&.[](:content_type)
|
|
40
|
+
content_type = upstream_content_type&.match?(/json/) ? upstream_content_type : 'application/json'
|
|
40
41
|
|
|
41
|
-
return render
|
|
42
|
-
:message => message,
|
|
43
|
-
:error => response_obj.to_s,
|
|
44
|
-
:headers => {},
|
|
45
|
-
:response => response_obj,
|
|
46
|
-
}, status: code
|
|
42
|
+
return render body: response_obj.to_s, status: code, content_type: content_type
|
|
47
43
|
rescue StandardError => e
|
|
48
44
|
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
|
49
45
|
logger.warn("Cloud request failed with exception: #{e}")
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
module ForemanRhCloud
|
|
2
2
|
class URLRemediationsRetriever < RemediationsRetriever
|
|
3
|
-
attr_reader :url, :
|
|
3
|
+
attr_reader :url, :headers
|
|
4
4
|
|
|
5
5
|
def initialize(url:, organization_id:, payload: '', headers: {}, logger: Logger.new(IO::NULL))
|
|
6
6
|
super(logger: logger)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
parsed_url = URI.parse(url)
|
|
9
|
+
query_params = parsed_url.query ? CGI.parse(parsed_url.query) : {}
|
|
10
|
+
hosts_param = query_params.delete('hosts')
|
|
11
|
+
|
|
12
|
+
if hosts_param.present?
|
|
13
|
+
@host_uuids = hosts_param.flat_map { |v| v.split(',') }.map(&:strip).reject(&:blank?)
|
|
14
|
+
@host_uuids = nil if @host_uuids.empty?
|
|
15
|
+
parsed_url.query = query_params.any? ? URI.encode_www_form(query_params) : nil
|
|
16
|
+
@url = parsed_url.to_s
|
|
17
|
+
else
|
|
18
|
+
@host_uuids = nil
|
|
19
|
+
@url = url
|
|
20
|
+
end
|
|
21
|
+
|
|
9
22
|
@payload = payload
|
|
10
23
|
@headers = headers
|
|
11
24
|
@organization_id = organization_id
|
|
@@ -14,7 +27,7 @@ module ForemanRhCloud
|
|
|
14
27
|
private
|
|
15
28
|
|
|
16
29
|
def query_playbook
|
|
17
|
-
logger.debug("Querying playbook at: #{url} with payload: #{payload} and headers: #{headers}")
|
|
30
|
+
logger.debug("Querying playbook via #{method.to_s.upcase} at: #{url} with payload: #{payload} and headers: #{headers}")
|
|
18
31
|
|
|
19
32
|
super
|
|
20
33
|
end
|
|
@@ -28,11 +41,12 @@ module ForemanRhCloud
|
|
|
28
41
|
end
|
|
29
42
|
|
|
30
43
|
def payload
|
|
31
|
-
|
|
44
|
+
return @host_uuids.to_json if @host_uuids.present?
|
|
45
|
+
@payload.present? ? @payload.to_json : @payload
|
|
32
46
|
end
|
|
33
47
|
|
|
34
48
|
def method
|
|
35
|
-
:get
|
|
49
|
+
@host_uuids.present? ? :post : :get
|
|
36
50
|
end
|
|
37
51
|
|
|
38
52
|
def organization
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module ForemanInventoryUpload
|
|
2
|
+
module Async
|
|
3
|
+
class DestroyOrganizationHbiHostsJob < ::Actions::EntryAction
|
|
4
|
+
include ForemanRhCloud::CertAuth
|
|
5
|
+
|
|
6
|
+
def plan(organization_id)
|
|
7
|
+
plan_self(organization_id: organization_id)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
org = Organization.find_by(id: input[:organization_id])
|
|
12
|
+
unless org
|
|
13
|
+
output[:result] = _("Organization not found")
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
logger.info("Destroying all HBI hosts for organization #{org.label} (id: #{org.id})")
|
|
18
|
+
|
|
19
|
+
execute_cloud_request(
|
|
20
|
+
organization: org,
|
|
21
|
+
method: :delete,
|
|
22
|
+
url: ForemanInventoryUpload.hosts_delete_all_url,
|
|
23
|
+
headers: {
|
|
24
|
+
content_type: :json,
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
output[:result] = format(_("Successfully deleted all HBI hosts for organization %s"), org.label)
|
|
29
|
+
rescue RestClient::NotFound
|
|
30
|
+
output[:result] = format(_("No HBI hosts found for organization %s"), org&.label)
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
logger.error(format(_("Failed to destroy HBI hosts for organization %s: %s"), org&.label, e.message))
|
|
33
|
+
raise
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def logger
|
|
37
|
+
Foreman::Logging.logger('background')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def rescue_strategy
|
|
41
|
+
Dynflow::Action::Rescue::Skip
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def humanized_name
|
|
45
|
+
_("Destroy HBI hosts for organization")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -103,6 +103,10 @@ module ForemanInventoryUpload
|
|
|
103
103
|
"#{inventory_base_url}/#{host_uuid}"
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
+
def self.hosts_delete_all_url
|
|
107
|
+
"#{inventory_base_url}/all?confirm_delete_all=true"
|
|
108
|
+
end
|
|
109
|
+
|
|
106
110
|
def self.hosts_by_ids_url(host_uuids)
|
|
107
111
|
host_ids_string = host_uuids.join(',')
|
|
108
112
|
"#{inventory_base_url}/#{host_ids_string}"
|
|
@@ -42,6 +42,7 @@ module ForemanRhCloud
|
|
|
42
42
|
::Katello::Api::Rhsm::CandlepinDynflowProxyController.include InsightsCloud::PackageProfileUploadExtensions
|
|
43
43
|
::Katello::Api::Rhsm::CandlepinProxiesController.include InsightsCloud::CandlepinProxiesExtensions
|
|
44
44
|
::Katello::RegistrationManager.singleton_class.prepend ::ForemanRhCloud::RegistrationManagerExtensions
|
|
45
|
+
::Actions::Katello::Organization::Destroy.prepend ::ForemanRhCloud::OrganizationDestroyExtensions
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module ForemanRhCloud
|
|
2
|
+
module OrganizationDestroyExtensions
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
def remove_consumers(organization)
|
|
6
|
+
plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, organization.id) if ForemanRhCloud.with_iop_smart_proxy?
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
data/package.json
CHANGED
|
@@ -154,8 +154,22 @@ module InsightsCloud::Api
|
|
|
154
154
|
|
|
155
155
|
get :forward_request, params: { "path" => "platform/module-update-router/v1/channel" }
|
|
156
156
|
assert_equal 500, @response.status
|
|
157
|
-
assert_equal
|
|
158
|
-
|
|
157
|
+
assert_equal @body, @response.body
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
test "should forward JSON error responses without double-escaping" do
|
|
161
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
162
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
163
|
+
net_http_resp['content-type'] = 'application/json'
|
|
164
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
165
|
+
::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
166
|
+
|
|
167
|
+
get :forward_request, params: { "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }
|
|
168
|
+
assert_equal 404, @response.status
|
|
169
|
+
assert_includes @response.content_type, 'application/json'
|
|
170
|
+
assert_equal json_error, @response.body
|
|
171
|
+
parsed = JSON.parse(@response.body)
|
|
172
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
159
173
|
end
|
|
160
174
|
|
|
161
175
|
test "should create insights facet" do
|
|
@@ -177,8 +177,22 @@ module InsightsCloud
|
|
|
177
177
|
|
|
178
178
|
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
|
179
179
|
assert_equal 500, @response.status
|
|
180
|
-
assert_equal
|
|
181
|
-
|
|
180
|
+
assert_equal @body, @response.body
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
test "should forward JSON error responses without double-escaping" do
|
|
184
|
+
json_error = { errors: [{ detail: 'inventory_id must exist', status: '404' }] }.to_json
|
|
185
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 404, "Not Found")
|
|
186
|
+
net_http_resp['content-type'] = 'application/json'
|
|
187
|
+
res = RestClient::Response.create(json_error, net_http_resp, @http_req)
|
|
188
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotFound.new(res))
|
|
189
|
+
|
|
190
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/systems/00000000-0000-0000-0000-000000000000" }, session: set_session
|
|
191
|
+
assert_equal 404, @response.status
|
|
192
|
+
assert_includes @response.content_type, 'application/json'
|
|
193
|
+
assert_equal json_error, @response.body
|
|
194
|
+
parsed = JSON.parse(@response.body)
|
|
195
|
+
assert_equal 'inventory_id must exist', parsed['errors'][0]['detail']
|
|
182
196
|
end
|
|
183
197
|
|
|
184
198
|
test "should allow forward_request with nil location (Any location)" do
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
require 'foreman_tasks/test_helpers'
|
|
3
|
+
|
|
4
|
+
class DestroyOrganizationHbiHostsJobTest < ActiveSupport::TestCase
|
|
5
|
+
include Dynflow::Testing::Factories
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
+
|
|
10
|
+
Organization.any_instance.stubs(:manifest_expired?).returns(false)
|
|
11
|
+
@org = FactoryBot.create(:organization)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test 'Deletes all HBI hosts for organization' do
|
|
15
|
+
expected_url = ForemanInventoryUpload.hosts_delete_all_url
|
|
16
|
+
|
|
17
|
+
ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob.any_instance.expects(:execute_cloud_request).with do |params|
|
|
18
|
+
params[:organization] == @org &&
|
|
19
|
+
params[:method] == :delete &&
|
|
20
|
+
params[:url] == expected_url &&
|
|
21
|
+
params[:headers][:content_type] == :json
|
|
22
|
+
end.returns(mock_response)
|
|
23
|
+
|
|
24
|
+
action = create_and_plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, @org.id)
|
|
25
|
+
action = run_action(action)
|
|
26
|
+
|
|
27
|
+
assert_match(/Successfully deleted/, action.output[:result])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
test 'Handles RestClient::NotFound gracefully' do
|
|
31
|
+
ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob.any_instance.expects(:execute_cloud_request).raises(RestClient::NotFound)
|
|
32
|
+
|
|
33
|
+
action = create_and_plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, @org.id)
|
|
34
|
+
action = run_action(action)
|
|
35
|
+
|
|
36
|
+
assert_match(/No HBI hosts found/, action.output[:result])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test 'Raises on other errors' do
|
|
40
|
+
ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob.any_instance.expects(:execute_cloud_request).raises(
|
|
41
|
+
RestClient::InternalServerError.new
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
action = create_and_plan_action(ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, @org.id)
|
|
45
|
+
|
|
46
|
+
assert_raises(RestClient::InternalServerError) do
|
|
47
|
+
run_action(action)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'hosts_delete_all_url returns correct format' do
|
|
52
|
+
url = ForemanInventoryUpload.hosts_delete_all_url
|
|
53
|
+
|
|
54
|
+
assert_match %r{/hosts/all\?confirm_delete_all=true$}, url
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def mock_response(code: 200, body: '')
|
|
58
|
+
response = mock('response')
|
|
59
|
+
response.stubs(:code).returns(code)
|
|
60
|
+
response.stubs(:body).returns(body)
|
|
61
|
+
response
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
require 'foreman_tasks/test_helpers'
|
|
3
|
+
|
|
4
|
+
class OrganizationDestroyExtensionsTest < ActiveSupport::TestCase
|
|
5
|
+
include Dynflow::Testing
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
User.current = User.find_by(login: 'secret_admin')
|
|
9
|
+
|
|
10
|
+
@organization = stub
|
|
11
|
+
@organization.stubs(:label).returns('test_org')
|
|
12
|
+
@organization.stubs(:id).returns(1)
|
|
13
|
+
@organization.stubs(:validate_destroy).returns([])
|
|
14
|
+
@organization.stubs(:products).returns([])
|
|
15
|
+
@organization.stubs(:activation_keys).returns([])
|
|
16
|
+
@organization.stubs(:content_views).returns(stub(:non_default => []))
|
|
17
|
+
@organization.stubs(:default_content_view).returns(stub(:content_view_environments => []))
|
|
18
|
+
@organization.stubs(:promotion_paths).returns([])
|
|
19
|
+
@organization.stubs(:providers).returns([])
|
|
20
|
+
@organization.stubs(:library).returns(stub(:destroy! => true))
|
|
21
|
+
|
|
22
|
+
where_clause = mock
|
|
23
|
+
where_clause.stubs(:where).returns([])
|
|
24
|
+
::Host.stubs(:unscoped).returns(where_clause)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
teardown do
|
|
28
|
+
ForemanRhCloud.unstub(:with_iop_smart_proxy?)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test 'plans DestroyOrganizationHbiHostsJob when in IoP mode' do
|
|
32
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
|
|
33
|
+
|
|
34
|
+
action = create_action(::Actions::Katello::Organization::Destroy)
|
|
35
|
+
action.stubs(:action_subject).with(@organization)
|
|
36
|
+
plan_action(action, @organization)
|
|
37
|
+
|
|
38
|
+
assert_action_planned_with(action, ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob, @organization.id)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
test 'does not plan DestroyOrganizationHbiHostsJob when not in IoP mode' do
|
|
42
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
43
|
+
|
|
44
|
+
action = create_action(::Actions::Katello::Organization::Destroy)
|
|
45
|
+
action.stubs(:action_subject).with(@organization)
|
|
46
|
+
plan_action(action, @organization)
|
|
47
|
+
|
|
48
|
+
refute_action_planned(action, ForemanInventoryUpload::Async::DestroyOrganizationHbiHostsJob)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'test_plugin_helper'
|
|
2
2
|
|
|
3
3
|
class URLRemediationsRetrieverTest < ActiveSupport::TestCase
|
|
4
|
-
test 'Calls the given url' do
|
|
5
|
-
|
|
4
|
+
test 'Calls the given url with GET when no hosts in URL' do
|
|
5
|
+
retriever = ForemanRhCloud::URLRemediationsRetriever.new(
|
|
6
6
|
organization_id: FactoryBot.create(:organization).id,
|
|
7
7
|
url: 'http://test.example.com',
|
|
8
8
|
payload: 'TEST_PAYLOAD',
|
|
@@ -11,19 +11,96 @@ class URLRemediationsRetrieverTest < ActiveSupport::TestCase
|
|
|
11
11
|
}
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
retriever.stubs(:cert_auth_available?).returns(true)
|
|
15
15
|
|
|
16
16
|
response = mock('response')
|
|
17
17
|
response.stubs(:body).returns('TEST_RESPONSE')
|
|
18
|
-
|
|
18
|
+
retriever.expects(:execute_cloud_request).with do |params|
|
|
19
19
|
params[:method] == :get &&
|
|
20
20
|
params[:url] == 'http://test.example.com' &&
|
|
21
21
|
params[:headers][:custom1] == 'TEST_HEADER' &&
|
|
22
22
|
params[:payload] == "\"TEST_PAYLOAD\""
|
|
23
23
|
end.returns(response)
|
|
24
24
|
|
|
25
|
-
actual =
|
|
25
|
+
actual = retriever.create_playbook
|
|
26
26
|
|
|
27
27
|
assert_equal 'TEST_RESPONSE', actual
|
|
28
28
|
end
|
|
29
|
+
|
|
30
|
+
test 'Uses POST with hosts in body when URL contains hosts query param' do
|
|
31
|
+
retriever = ForemanRhCloud::URLRemediationsRetriever.new(
|
|
32
|
+
organization_id: FactoryBot.create(:organization).id,
|
|
33
|
+
url: 'http://test.example.com/api/remediations/1234/playbook?hosts=uuid-1,uuid-2,uuid-3'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
retriever.stubs(:cert_auth_available?).returns(true)
|
|
37
|
+
|
|
38
|
+
response = mock('response')
|
|
39
|
+
response.stubs(:body).returns('TEST_PLAYBOOK')
|
|
40
|
+
retriever.expects(:execute_cloud_request).with do |params|
|
|
41
|
+
params[:method] == :post &&
|
|
42
|
+
params[:url] == 'http://test.example.com/api/remediations/1234/playbook' &&
|
|
43
|
+
JSON.parse(params[:payload]) == ['uuid-1', 'uuid-2', 'uuid-3']
|
|
44
|
+
end.returns(response)
|
|
45
|
+
|
|
46
|
+
actual = retriever.create_playbook
|
|
47
|
+
|
|
48
|
+
assert_equal 'TEST_PLAYBOOK', actual
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'Preserves other query params when extracting hosts' do
|
|
52
|
+
retriever = ForemanRhCloud::URLRemediationsRetriever.new(
|
|
53
|
+
organization_id: FactoryBot.create(:organization).id,
|
|
54
|
+
url: 'http://test.example.com/api/remediations/1234/playbook?hosts=uuid-1,uuid-2&localhost=false'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
retriever.stubs(:cert_auth_available?).returns(true)
|
|
58
|
+
|
|
59
|
+
response = mock('response')
|
|
60
|
+
response.stubs(:body).returns('TEST_PLAYBOOK')
|
|
61
|
+
retriever.expects(:execute_cloud_request).with do |params|
|
|
62
|
+
params[:method] == :post &&
|
|
63
|
+
params[:url].include?('localhost=false') &&
|
|
64
|
+
!params[:url].include?('hosts=') &&
|
|
65
|
+
JSON.parse(params[:payload]) == ['uuid-1', 'uuid-2']
|
|
66
|
+
end.returns(response)
|
|
67
|
+
|
|
68
|
+
retriever.create_playbook
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
test 'Merges multiple hosts query params into a single array' do
|
|
72
|
+
retriever = ForemanRhCloud::URLRemediationsRetriever.new(
|
|
73
|
+
organization_id: FactoryBot.create(:organization).id,
|
|
74
|
+
url: 'http://test.example.com/api/remediations/1234/playbook?hosts=uuid-1&hosts=uuid-2'
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
retriever.stubs(:cert_auth_available?).returns(true)
|
|
78
|
+
|
|
79
|
+
response = mock('response')
|
|
80
|
+
response.stubs(:body).returns('TEST_PLAYBOOK')
|
|
81
|
+
retriever.expects(:execute_cloud_request).with do |params|
|
|
82
|
+
params[:method] == :post &&
|
|
83
|
+
params[:url] == 'http://test.example.com/api/remediations/1234/playbook' &&
|
|
84
|
+
JSON.parse(params[:payload]) == ['uuid-1', 'uuid-2']
|
|
85
|
+
end.returns(response)
|
|
86
|
+
|
|
87
|
+
retriever.create_playbook
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
test 'Falls back to GET when hosts query param is empty' do
|
|
91
|
+
retriever = ForemanRhCloud::URLRemediationsRetriever.new(
|
|
92
|
+
organization_id: FactoryBot.create(:organization).id,
|
|
93
|
+
url: 'http://test.example.com/api/remediations/1234/playbook?hosts='
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
retriever.stubs(:cert_auth_available?).returns(true)
|
|
97
|
+
|
|
98
|
+
response = mock('response')
|
|
99
|
+
response.stubs(:body).returns('TEST_PLAYBOOK')
|
|
100
|
+
retriever.expects(:execute_cloud_request).with do |params|
|
|
101
|
+
params[:method] == :get
|
|
102
|
+
end.returns(response)
|
|
103
|
+
|
|
104
|
+
retriever.create_playbook
|
|
105
|
+
end
|
|
29
106
|
end
|
|
@@ -3,6 +3,10 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import { ScalprumComponent, ScalprumProvider } from '@scalprum/react-core';
|
|
4
4
|
import { createProviderOptions } from '../common/ScalprumModule/ScalprumContext';
|
|
5
5
|
import { useInsightsPermissions } from '../common/Hooks/PermissionsHooks';
|
|
6
|
+
import {
|
|
7
|
+
vulnerabilityDisabled,
|
|
8
|
+
useTabRedirect,
|
|
9
|
+
} from '../ForemanRhCloudHelpers';
|
|
6
10
|
import './CVEsHostDetailsTab.scss';
|
|
7
11
|
|
|
8
12
|
const CVEsHostDetailsTab = ({ systemId }) => {
|
|
@@ -26,6 +30,15 @@ CVEsHostDetailsTab.propTypes = {
|
|
|
26
30
|
|
|
27
31
|
const CVEsHostDetailsTabWrapper = ({ response }) => {
|
|
28
32
|
const permissions = useInsightsPermissions();
|
|
33
|
+
const isHostDataLoaded = Boolean(response?.id);
|
|
34
|
+
const shouldHideTab = useTabRedirect(
|
|
35
|
+
isHostDataLoaded && vulnerabilityDisabled({ hostDetails: response })
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (shouldHideTab) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
return (
|
|
30
43
|
<ScalprumProvider {...createProviderOptions(permissions)}>
|
|
31
44
|
<CVEsHostDetailsTab
|
|
@@ -38,10 +51,19 @@ const CVEsHostDetailsTabWrapper = ({ response }) => {
|
|
|
38
51
|
|
|
39
52
|
CVEsHostDetailsTabWrapper.propTypes = {
|
|
40
53
|
response: PropTypes.shape({
|
|
54
|
+
id: PropTypes.number,
|
|
55
|
+
operatingsystem_name: PropTypes.string,
|
|
56
|
+
vulnerability: PropTypes.shape({
|
|
57
|
+
enabled: PropTypes.bool,
|
|
58
|
+
}),
|
|
41
59
|
subscription_facet_attributes: PropTypes.shape({
|
|
42
|
-
uuid: PropTypes.string
|
|
60
|
+
uuid: PropTypes.string,
|
|
43
61
|
}),
|
|
44
|
-
})
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
CVEsHostDetailsTabWrapper.defaultProps = {
|
|
66
|
+
response: {},
|
|
45
67
|
};
|
|
46
68
|
|
|
47
69
|
export default CVEsHostDetailsTabWrapper;
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
3
4
|
import CVEsHostDetailsTabWrapper from '../CVEsHostDetailsTab';
|
|
5
|
+
import { OVERVIEW_TAB_PATH } from '../../ForemanRhCloudHelpers';
|
|
6
|
+
|
|
7
|
+
const mockHistoryReplace = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock('react-router-dom', () => ({
|
|
10
|
+
...jest.requireActual('react-router-dom'),
|
|
11
|
+
useHistory: () => ({
|
|
12
|
+
replace: mockHistoryReplace,
|
|
13
|
+
}),
|
|
14
|
+
}));
|
|
4
15
|
|
|
5
16
|
jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
6
17
|
useForemanContext: () => ({
|
|
@@ -25,31 +36,44 @@ jest.mock('@scalprum/react-core', () => {
|
|
|
25
36
|
};
|
|
26
37
|
});
|
|
27
38
|
|
|
39
|
+
const defaultResponse = {
|
|
40
|
+
id: 1,
|
|
41
|
+
operatingsystem_name: 'Red Hat Enterprise Linux 8',
|
|
42
|
+
vulnerability: { enabled: true },
|
|
43
|
+
subscription_facet_attributes: { uuid: '1-2-3' },
|
|
44
|
+
};
|
|
45
|
+
|
|
28
46
|
describe('CVEsHostDetailsTabWrapper', () => {
|
|
29
47
|
beforeEach(() => {
|
|
30
48
|
jest.clearAllMocks();
|
|
31
49
|
});
|
|
32
50
|
|
|
33
|
-
it('renders without crashing', () => {
|
|
51
|
+
it('renders without crashing and does not redirect for valid host', () => {
|
|
34
52
|
const { container } = render(
|
|
35
|
-
<
|
|
36
|
-
response={
|
|
37
|
-
|
|
53
|
+
<MemoryRouter>
|
|
54
|
+
<CVEsHostDetailsTabWrapper response={defaultResponse} />
|
|
55
|
+
</MemoryRouter>
|
|
38
56
|
);
|
|
39
57
|
expect(
|
|
40
58
|
container.querySelector(
|
|
41
59
|
'.rh-cloud-insights-vulnerability-host-details-component'
|
|
42
60
|
)
|
|
43
61
|
).toBeTruthy();
|
|
62
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
44
63
|
});
|
|
45
64
|
|
|
46
65
|
it('remounts ScalprumComponent when systemId changes', () => {
|
|
47
66
|
const { ScalprumComponent } = require('@scalprum/react-core');
|
|
48
67
|
|
|
68
|
+
const responseHostA = {
|
|
69
|
+
...defaultResponse,
|
|
70
|
+
subscription_facet_attributes: { uuid: 'uuid-host-A' },
|
|
71
|
+
};
|
|
72
|
+
|
|
49
73
|
const { rerender } = render(
|
|
50
|
-
<
|
|
51
|
-
response={
|
|
52
|
-
|
|
74
|
+
<MemoryRouter>
|
|
75
|
+
<CVEsHostDetailsTabWrapper response={responseHostA} />
|
|
76
|
+
</MemoryRouter>
|
|
53
77
|
);
|
|
54
78
|
|
|
55
79
|
expect(mockUnmountTracker).not.toHaveBeenCalled();
|
|
@@ -58,10 +82,15 @@ describe('CVEsHostDetailsTabWrapper', () => {
|
|
|
58
82
|
expect.anything()
|
|
59
83
|
);
|
|
60
84
|
|
|
85
|
+
const responseHostB = {
|
|
86
|
+
...defaultResponse,
|
|
87
|
+
subscription_facet_attributes: { uuid: 'uuid-host-B' },
|
|
88
|
+
};
|
|
89
|
+
|
|
61
90
|
rerender(
|
|
62
|
-
<
|
|
63
|
-
response={
|
|
64
|
-
|
|
91
|
+
<MemoryRouter>
|
|
92
|
+
<CVEsHostDetailsTabWrapper response={responseHostB} />
|
|
93
|
+
</MemoryRouter>
|
|
65
94
|
);
|
|
66
95
|
|
|
67
96
|
expect(mockUnmountTracker).toHaveBeenCalledTimes(1);
|
|
@@ -70,4 +99,38 @@ describe('CVEsHostDetailsTabWrapper', () => {
|
|
|
70
99
|
expect.anything()
|
|
71
100
|
);
|
|
72
101
|
});
|
|
102
|
+
|
|
103
|
+
it('redirects to Overview when tab should be hidden', () => {
|
|
104
|
+
const nonRhelResponse = {
|
|
105
|
+
id: 2,
|
|
106
|
+
operatingsystem_name: 'Ubuntu 20.04',
|
|
107
|
+
vulnerability: { enabled: false },
|
|
108
|
+
subscription_facet_attributes: { uuid: '1-2-3' },
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const { container } = render(
|
|
112
|
+
<MemoryRouter>
|
|
113
|
+
<CVEsHostDetailsTabWrapper response={nonRhelResponse} />
|
|
114
|
+
</MemoryRouter>
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(mockHistoryReplace).toHaveBeenCalledWith(OVERVIEW_TAB_PATH);
|
|
118
|
+
expect(
|
|
119
|
+
container.querySelector(
|
|
120
|
+
'.rh-cloud-insights-vulnerability-host-details-component'
|
|
121
|
+
)
|
|
122
|
+
).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('does not redirect when host data is not yet loaded', () => {
|
|
126
|
+
const emptyResponse = { subscription_facet_attributes: { uuid: '1-2-3' } };
|
|
127
|
+
|
|
128
|
+
render(
|
|
129
|
+
<MemoryRouter>
|
|
130
|
+
<CVEsHostDetailsTabWrapper response={emptyResponse} />
|
|
131
|
+
</MemoryRouter>
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
135
|
+
});
|
|
73
136
|
});
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useHistory } from 'react-router-dom';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* copied from core, since it's not in the ReactApp folder,
|
|
3
6
|
* it's complicated to import it and mock it in tests.
|
|
@@ -5,6 +8,25 @@
|
|
|
5
8
|
*/
|
|
6
9
|
export const foremanUrl = path => `${window.URL_PREFIX}${path}`;
|
|
7
10
|
|
|
11
|
+
export const OVERVIEW_TAB_PATH = '/Overview';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Redirects to Overview tab when the current tab should be hidden
|
|
15
|
+
* @param {boolean} shouldRedirect - Whether to redirect (e.g., host loaded AND tab should hide)
|
|
16
|
+
* @returns {boolean} - Returns shouldRedirect for convenience
|
|
17
|
+
*/
|
|
18
|
+
export const useTabRedirect = shouldRedirect => {
|
|
19
|
+
const history = useHistory();
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (shouldRedirect && history) {
|
|
23
|
+
history.replace(OVERVIEW_TAB_PATH);
|
|
24
|
+
}
|
|
25
|
+
}, [shouldRedirect, history]);
|
|
26
|
+
|
|
27
|
+
return shouldRedirect;
|
|
28
|
+
};
|
|
29
|
+
|
|
8
30
|
export const isNotRhelHost = ({ hostDetails }) =>
|
|
9
31
|
// This regex tries matches sane variations of "RedHat", "RHEL" and "RHCOS"
|
|
10
32
|
!new RegExp('red[\\s\\-]?hat|rh[\\s\\-]?el|rhc[\\s\\-]?os', 'i').test(
|
|
@@ -25,6 +25,11 @@ import { useIopConfig } from '../common/Hooks/ConfigHooks';
|
|
|
25
25
|
import { generateRuleUrl } from '../InsightsCloudSync/InsightsCloudSync';
|
|
26
26
|
import { createProviderOptions } from '../common/ScalprumModule/ScalprumContext';
|
|
27
27
|
import { useInsightsPermissions } from '../common/Hooks/PermissionsHooks';
|
|
28
|
+
import {
|
|
29
|
+
isNotRhelHost,
|
|
30
|
+
hasNoInsightsFacet,
|
|
31
|
+
useTabRedirect,
|
|
32
|
+
} from '../ForemanRhCloudHelpers';
|
|
28
33
|
|
|
29
34
|
// Hosted Insights advisor
|
|
30
35
|
const NewHostDetailsTab = ({ hostName, router }) => {
|
|
@@ -165,7 +170,18 @@ const IopInsightsTabWrapped = props => {
|
|
|
165
170
|
};
|
|
166
171
|
|
|
167
172
|
const InsightsTab = props => {
|
|
173
|
+
const { response } = props;
|
|
168
174
|
const isIop = useIopConfig();
|
|
175
|
+
const isHostDataLoaded = Boolean(response?.id);
|
|
176
|
+
const shouldHideTab = useTabRedirect(
|
|
177
|
+
isHostDataLoaded &&
|
|
178
|
+
(isNotRhelHost({ hostDetails: response }) ||
|
|
179
|
+
hasNoInsightsFacet({ response, hostDetails: response }))
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (shouldHideTab) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
169
185
|
|
|
170
186
|
return isIop ? (
|
|
171
187
|
<IopInsightsTabWrapped {...props} />
|
|
@@ -174,6 +190,16 @@ const InsightsTab = props => {
|
|
|
174
190
|
);
|
|
175
191
|
};
|
|
176
192
|
|
|
177
|
-
InsightsTab.
|
|
193
|
+
InsightsTab.propTypes = {
|
|
194
|
+
response: PropTypes.shape({
|
|
195
|
+
id: PropTypes.number,
|
|
196
|
+
operatingsystem_name: PropTypes.string,
|
|
197
|
+
insights_attributes: PropTypes.object,
|
|
198
|
+
}),
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
InsightsTab.defaultProps = {
|
|
202
|
+
response: {},
|
|
203
|
+
};
|
|
178
204
|
|
|
179
205
|
export default InsightsTab;
|
|
@@ -2,9 +2,20 @@ import React from 'react';
|
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import '@testing-library/jest-dom';
|
|
4
4
|
import { Provider } from 'react-redux';
|
|
5
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
6
|
import configureMockStore from 'redux-mock-store';
|
|
6
7
|
import thunk from 'redux-thunk';
|
|
7
8
|
import NewHostDetailsTab from '../NewHostDetailsTab';
|
|
9
|
+
import { OVERVIEW_TAB_PATH } from '../../ForemanRhCloudHelpers';
|
|
10
|
+
|
|
11
|
+
const mockHistoryReplace = jest.fn();
|
|
12
|
+
|
|
13
|
+
jest.mock('react-router-dom', () => ({
|
|
14
|
+
...jest.requireActual('react-router-dom'),
|
|
15
|
+
useHistory: () => ({
|
|
16
|
+
replace: mockHistoryReplace,
|
|
17
|
+
}),
|
|
18
|
+
}));
|
|
8
19
|
|
|
9
20
|
jest.mock('../../common/Hooks/ConfigHooks', () => ({
|
|
10
21
|
useIopConfig: jest.fn(() => false),
|
|
@@ -14,8 +25,33 @@ jest.mock('foremanReact/common/I18n', () => ({
|
|
|
14
25
|
translate: jest.fn(str => str),
|
|
15
26
|
}));
|
|
16
27
|
|
|
28
|
+
jest.mock(
|
|
29
|
+
'foremanReact/components/SearchBar',
|
|
30
|
+
() => () => <div>SearchBar</div>,
|
|
31
|
+
{ virtual: true }
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
jest.mock('../../InsightsCloudSync/Components/InsightsTable', () => () => (
|
|
35
|
+
<div>InsightsTable</div>
|
|
36
|
+
));
|
|
37
|
+
|
|
38
|
+
jest.mock('../../InsightsCloudSync/Components/RemediationModal', () => () => (
|
|
39
|
+
<div>RemediationModal</div>
|
|
40
|
+
));
|
|
41
|
+
|
|
42
|
+
jest.mock(
|
|
43
|
+
'../../InsightsCloudSync/Components/InsightsTable/Pagination',
|
|
44
|
+
() => () => <div>Pagination</div>
|
|
45
|
+
);
|
|
46
|
+
|
|
17
47
|
const mockStore = configureMockStore([thunk]);
|
|
18
48
|
|
|
49
|
+
const defaultResponse = {
|
|
50
|
+
id: 1,
|
|
51
|
+
operatingsystem_name: 'Red Hat Enterprise Linux 8',
|
|
52
|
+
insights_attributes: { uuid: 'test-uuid' },
|
|
53
|
+
};
|
|
54
|
+
|
|
19
55
|
describe('NewHostDetailsTab', () => {
|
|
20
56
|
let store;
|
|
21
57
|
let mockRouter;
|
|
@@ -68,17 +104,18 @@ describe('NewHostDetailsTab', () => {
|
|
|
68
104
|
it('should preserve hash when clearing search params on unmount', () => {
|
|
69
105
|
const { unmount } = render(
|
|
70
106
|
<Provider store={store}>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
<MemoryRouter>
|
|
108
|
+
<NewHostDetailsTab
|
|
109
|
+
hostName="test-host.example.com"
|
|
110
|
+
router={mockRouter}
|
|
111
|
+
response={defaultResponse}
|
|
112
|
+
/>
|
|
113
|
+
</MemoryRouter>
|
|
75
114
|
</Provider>
|
|
76
115
|
);
|
|
77
116
|
|
|
78
|
-
// Unmount the component to trigger cleanup
|
|
79
117
|
unmount();
|
|
80
118
|
|
|
81
|
-
// Verify router.replace was called with both search: null AND the existing hash
|
|
82
119
|
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
83
120
|
search: null,
|
|
84
121
|
hash: '#/Insights',
|
|
@@ -90,16 +127,18 @@ describe('NewHostDetailsTab', () => {
|
|
|
90
127
|
|
|
91
128
|
const { unmount } = render(
|
|
92
129
|
<Provider store={store}>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
130
|
+
<MemoryRouter>
|
|
131
|
+
<NewHostDetailsTab
|
|
132
|
+
hostName="test-host.example.com"
|
|
133
|
+
router={mockRouter}
|
|
134
|
+
response={defaultResponse}
|
|
135
|
+
/>
|
|
136
|
+
</MemoryRouter>
|
|
97
137
|
</Provider>
|
|
98
138
|
);
|
|
99
139
|
|
|
100
140
|
unmount();
|
|
101
141
|
|
|
102
|
-
// When there's no hash, should only pass search: null
|
|
103
142
|
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
104
143
|
search: null,
|
|
105
144
|
});
|
|
@@ -114,16 +153,18 @@ describe('NewHostDetailsTab', () => {
|
|
|
114
153
|
|
|
115
154
|
const { unmount } = render(
|
|
116
155
|
<Provider store={store}>
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
156
|
+
<MemoryRouter>
|
|
157
|
+
<NewHostDetailsTab
|
|
158
|
+
hostName="test-host.example.com"
|
|
159
|
+
router={routerWithoutLocation}
|
|
160
|
+
response={defaultResponse}
|
|
161
|
+
/>
|
|
162
|
+
</MemoryRouter>
|
|
121
163
|
</Provider>
|
|
122
164
|
);
|
|
123
165
|
|
|
124
166
|
unmount();
|
|
125
167
|
|
|
126
|
-
// Should still call replace with search: null even if location is undefined
|
|
127
168
|
expect(routerWithoutLocation.replace).toHaveBeenCalledWith({
|
|
128
169
|
search: null,
|
|
129
170
|
});
|
|
@@ -132,23 +173,94 @@ describe('NewHostDetailsTab', () => {
|
|
|
132
173
|
it('should use the latest hash value at unmount time, not a stale captured value', () => {
|
|
133
174
|
const { unmount } = render(
|
|
134
175
|
<Provider store={store}>
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
176
|
+
<MemoryRouter>
|
|
177
|
+
<NewHostDetailsTab
|
|
178
|
+
hostName="test-host.example.com"
|
|
179
|
+
router={mockRouter}
|
|
180
|
+
response={defaultResponse}
|
|
181
|
+
/>
|
|
182
|
+
</MemoryRouter>
|
|
139
183
|
</Provider>
|
|
140
184
|
);
|
|
141
185
|
|
|
142
|
-
// Change the hash after mount, before unmount
|
|
143
186
|
mockRouter.location.hash = '#/Overview';
|
|
144
187
|
|
|
145
188
|
unmount();
|
|
146
189
|
|
|
147
|
-
// Verify router.replace was called with the UPDATED hash, not the initial '#/Insights'
|
|
148
190
|
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
149
191
|
search: null,
|
|
150
192
|
hash: '#/Overview',
|
|
151
193
|
});
|
|
152
194
|
});
|
|
153
195
|
});
|
|
196
|
+
|
|
197
|
+
describe('tab visibility', () => {
|
|
198
|
+
it('should redirect to Overview when host is not RHEL', () => {
|
|
199
|
+
const nonRhelResponse = {
|
|
200
|
+
id: 2,
|
|
201
|
+
operatingsystem_name: 'Ubuntu 20.04',
|
|
202
|
+
insights_attributes: { uuid: 'test-uuid' },
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
render(
|
|
206
|
+
<Provider store={store}>
|
|
207
|
+
<MemoryRouter>
|
|
208
|
+
<NewHostDetailsTab
|
|
209
|
+
hostName="test-host.example.com"
|
|
210
|
+
response={nonRhelResponse}
|
|
211
|
+
/>
|
|
212
|
+
</MemoryRouter>
|
|
213
|
+
</Provider>
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expect(mockHistoryReplace).toHaveBeenCalledWith(OVERVIEW_TAB_PATH);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should redirect to Overview when insights facet is missing', () => {
|
|
220
|
+
const responseWithoutInsights = {
|
|
221
|
+
id: 3,
|
|
222
|
+
operatingsystem_name: 'Red Hat Enterprise Linux 8',
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
render(
|
|
226
|
+
<Provider store={store}>
|
|
227
|
+
<MemoryRouter>
|
|
228
|
+
<NewHostDetailsTab
|
|
229
|
+
hostName="test-host.example.com"
|
|
230
|
+
response={responseWithoutInsights}
|
|
231
|
+
/>
|
|
232
|
+
</MemoryRouter>
|
|
233
|
+
</Provider>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
expect(mockHistoryReplace).toHaveBeenCalledWith(OVERVIEW_TAB_PATH);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should not redirect when host is valid RHEL with insights facet', () => {
|
|
240
|
+
render(
|
|
241
|
+
<Provider store={store}>
|
|
242
|
+
<MemoryRouter>
|
|
243
|
+
<NewHostDetailsTab
|
|
244
|
+
hostName="test-host.example.com"
|
|
245
|
+
response={defaultResponse}
|
|
246
|
+
/>
|
|
247
|
+
</MemoryRouter>
|
|
248
|
+
</Provider>
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should not redirect when host data is not yet loaded', () => {
|
|
255
|
+
render(
|
|
256
|
+
<Provider store={store}>
|
|
257
|
+
<MemoryRouter>
|
|
258
|
+
<NewHostDetailsTab hostName="test-host.example.com" response={{}} />
|
|
259
|
+
</MemoryRouter>
|
|
260
|
+
</Provider>
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
264
|
+
});
|
|
265
|
+
});
|
|
154
266
|
});
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_rh_cloud
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 14.1.
|
|
4
|
+
version: 14.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Foreman Red Hat Cloud team
|
|
@@ -51,34 +51,6 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '4.18'
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: rdoc
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - ">="
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0'
|
|
61
|
-
type: :development
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - ">="
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0'
|
|
68
|
-
- !ruby/object:Gem::Dependency
|
|
69
|
-
name: theforeman-rubocop
|
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - "~>"
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: 0.1.0
|
|
75
|
-
type: :development
|
|
76
|
-
prerelease: false
|
|
77
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
-
requirements:
|
|
79
|
-
- - "~>"
|
|
80
|
-
- !ruby/object:Gem::Version
|
|
81
|
-
version: 0.1.0
|
|
82
54
|
description: Foreman plugin that process & upload data to Red Hat Cloud
|
|
83
55
|
email:
|
|
84
56
|
- rlavi@redhat.com, sshtein@redhat.com
|
|
@@ -188,6 +160,7 @@ files:
|
|
|
188
160
|
- lib/foreman_inventory_upload/async/async_helpers.rb
|
|
189
161
|
- lib/foreman_inventory_upload/async/create_missing_insights_facets.rb
|
|
190
162
|
- lib/foreman_inventory_upload/async/delayed_start.rb
|
|
163
|
+
- lib/foreman_inventory_upload/async/destroy_organization_hbi_hosts_job.rb
|
|
191
164
|
- lib/foreman_inventory_upload/async/generate_all_reports_job.rb
|
|
192
165
|
- lib/foreman_inventory_upload/async/generate_host_report.rb
|
|
193
166
|
- lib/foreman_inventory_upload/async/host_inventory_report_job.rb
|
|
@@ -206,6 +179,7 @@ files:
|
|
|
206
179
|
- lib/foreman_rh_cloud.rb
|
|
207
180
|
- lib/foreman_rh_cloud/async/exponential_backoff.rb
|
|
208
181
|
- lib/foreman_rh_cloud/engine.rb
|
|
182
|
+
- lib/foreman_rh_cloud/organization_destroy_extensions.rb
|
|
209
183
|
- lib/foreman_rh_cloud/plugin.rb
|
|
210
184
|
- lib/foreman_rh_cloud/version.rb
|
|
211
185
|
- lib/insights_cloud.rb
|
|
@@ -260,6 +234,7 @@ files:
|
|
|
260
234
|
- test/jobs/cloud_connector_announce_task_test.rb
|
|
261
235
|
- test/jobs/connector_playbook_execution_reporter_task_test.rb
|
|
262
236
|
- test/jobs/create_missing_insights_facets_test.rb
|
|
237
|
+
- test/jobs/destroy_organization_hbi_hosts_job_test.rb
|
|
263
238
|
- test/jobs/exponential_backoff_test.rb
|
|
264
239
|
- test/jobs/generate_host_report_test.rb
|
|
265
240
|
- test/jobs/host_inventory_report_job_test.rb
|
|
@@ -286,6 +261,7 @@ files:
|
|
|
286
261
|
- test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb
|
|
287
262
|
- test/unit/lib/insights_cloud/async/vmaas_reposcan_sync_test.rb
|
|
288
263
|
- test/unit/metadata_generator_test.rb
|
|
264
|
+
- test/unit/organization_destroy_extensions_test.rb
|
|
289
265
|
- test/unit/playbook_progress_generator_test.rb
|
|
290
266
|
- test/unit/rh_cloud_host_test.rb
|
|
291
267
|
- test/unit/rh_cloud_http_proxy_test.rb
|
|
@@ -692,6 +668,7 @@ test_files:
|
|
|
692
668
|
- test/jobs/cloud_connector_announce_task_test.rb
|
|
693
669
|
- test/jobs/connector_playbook_execution_reporter_task_test.rb
|
|
694
670
|
- test/jobs/create_missing_insights_facets_test.rb
|
|
671
|
+
- test/jobs/destroy_organization_hbi_hosts_job_test.rb
|
|
695
672
|
- test/jobs/exponential_backoff_test.rb
|
|
696
673
|
- test/jobs/generate_host_report_test.rb
|
|
697
674
|
- test/jobs/host_inventory_report_job_test.rb
|
|
@@ -718,6 +695,7 @@ test_files:
|
|
|
718
695
|
- test/unit/lib/foreman_rh_cloud/registration_manager_extensions_test.rb
|
|
719
696
|
- test/unit/lib/insights_cloud/async/vmaas_reposcan_sync_test.rb
|
|
720
697
|
- test/unit/metadata_generator_test.rb
|
|
698
|
+
- test/unit/organization_destroy_extensions_test.rb
|
|
721
699
|
- test/unit/playbook_progress_generator_test.rb
|
|
722
700
|
- test/unit/rh_cloud_host_test.rb
|
|
723
701
|
- test/unit/rh_cloud_http_proxy_test.rb
|