foreman_rh_cloud 5.0.28 → 5.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/insights_cloud/hits_controller.rb +1 -1
  3. data/app/models/insights_hit.rb +4 -0
  4. data/lib/foreman_inventory_upload/generators/queries.rb +1 -0
  5. data/lib/foreman_rh_cloud/version.rb +1 -1
  6. data/lib/inventory_sync/async/host_result.rb +0 -5
  7. data/lib/inventory_sync/async/inventory_full_sync.rb +14 -9
  8. data/lib/inventory_sync/async/inventory_hosts_sync.rb +2 -6
  9. data/package.json +1 -1
  10. data/test/controllers/insights_sync/settings_controller_test.rb +2 -2
  11. data/test/controllers/uploads_settings_controller_test.rb +2 -2
  12. data/test/jobs/insights_full_sync_test.rb +2 -2
  13. data/test/jobs/insights_resolutions_sync_test.rb +1 -1
  14. data/test/jobs/insights_rules_sync_test.rb +2 -2
  15. data/test/jobs/inventory_full_sync_test.rb +31 -5
  16. data/test/jobs/inventory_hosts_sync_test.rb +16 -1
  17. data/test/jobs/inventory_scheduled_sync_test.rb +2 -2
  18. data/test/jobs/inventory_self_host_sync_test.rb +1 -1
  19. data/test/jobs/upload_report_job_test.rb +1 -1
  20. data/test/test_plugin_helper.rb +0 -2
  21. data/test/unit/rh_cloud_http_proxy_test.rb +1 -1
  22. data/test/unit/services/foreman_rh_cloud/branch_info_test.rb +1 -1
  23. data/test/unit/services/foreman_rh_cloud/template_renderer_helper_test.rb +1 -1
  24. data/test/unit/slice_generator_test.rb +15 -3
  25. data/test/unit/tags_generator_test.rb +2 -2
  26. data/webpack/ForemanRhCloudFills.js +7 -0
  27. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +22 -6
  28. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableActions.js +23 -16
  29. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableConstants.js +49 -2
  30. data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableHelpers.js +31 -14
  31. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +15 -1
  32. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableActions.test.js.snap +0 -1
  33. data/webpack/InsightsCloudSync/Components/InsightsTable/table.scss +11 -13
  34. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.scss +0 -14
  35. data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +13 -0
  36. data/webpack/InsightsCloudSync/InsightsCloudSyncHelpers.js +8 -0
  37. data/webpack/InsightsHostDetailsTab/InsightsTab.scss +4 -0
  38. data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +104 -0
  39. data/webpack/common/DropdownToggle.js +24 -0
  40. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 012fcceed18a6413326df32052bee870b299682bc114d5c3e59954bda33b2475
4
- data.tar.gz: bb72b384fc54d6c19951c453c69b0ecaae5d26bf844fc85b4d4b56f04e1e76bd
3
+ metadata.gz: 5a77b9dd6b10d3bf354a1ea4888ac4c829a66de66da4f432860e76ffe1b2a887
4
+ data.tar.gz: 81913ea4f9745d2c2e08d2c6b5117395c3b360b1879acdd21c8b83fabeccb4a0
5
5
  SHA512:
6
- metadata.gz: 70904b446fce3a2ca3a794c418aaa1591f4276bd35d1636a629e7361b75c62163fc76ad45caa024752a763ef6a9e6344e979dff8f9e9a017c19ac878f667a649
7
- data.tar.gz: 4fd0e25610e4848565178f19e5a3ffb1da2d9b1505cd604f18a9276bba931f6fea3fbbddcd6c1e375a9d655a4c0b506c3937d89e9e7899b4aed37ae05c72b542
6
+ metadata.gz: 026bf10d2031f225aa64539059ed9fea96254a04e4dfb305db0ffc3ca7c0c3c9676b5d331a78cdf98320c3175765a1d862e7be707f4f69ef0f95a8149977119f
7
+ data.tar.gz: c8b159a4c8f86bfcf710094b3b6e49b06b00d0d8448f8d853ee0777bf356cd6a3165c497b98f9bba3106f1c1ac234f49f297ff3cf58b548fcb2d462c04eb4f33
@@ -7,7 +7,7 @@ module InsightsCloud
7
7
 
8
8
  render json: {
9
9
  hasToken: !Setting[:rh_cloud_token].empty?,
10
- hits: hits.map { |hit| hit.attributes.merge(hostname: hit.host&.name, has_playbook: hit.has_playbook?) },
10
+ hits: hits.map { |hit| hit.attributes.merge(hostname: hit.host&.name, has_playbook: hit.has_playbook?, host_uuid: hit.host_uuid) },
11
11
  itemCount: hits.count,
12
12
  }, status: :ok
13
13
  end
@@ -22,4 +22,8 @@ class InsightsHit < ApplicationRecord
22
22
  def has_playbook?
23
23
  !rule.nil?
24
24
  end
25
+
26
+ def host_uuid
27
+ insights_facet.uuid
28
+ end
25
29
  end
@@ -34,6 +34,7 @@ module ForemanInventoryUpload
34
34
 
35
35
  def self.for_slice(base)
36
36
  base
37
+ .search_for("not params.#{InsightsCloud.enable_client_param} = f")
37
38
  .joins(:subscription_facet)
38
39
  .preload(
39
40
  :interfaces,
@@ -1,3 +1,3 @@
1
1
  module ForemanRhCloud
2
- VERSION = '5.0.28'.freeze
2
+ VERSION = '5.0.29'.freeze
3
3
  end
@@ -17,7 +17,6 @@ module InventorySync
17
17
  @sub_ids.map do |sub_id|
18
18
  host_id = host_id(sub_id)
19
19
  if host_id
20
- touched << host_id
21
20
  {
22
21
  host_id: host_id,
23
22
  status: InventorySync::InventoryStatus::SYNC,
@@ -28,10 +27,6 @@ module InventorySync
28
27
  end.compact
29
28
  end
30
29
 
31
- def touched
32
- @touched ||= []
33
- end
34
-
35
30
  def host_id(sub_id)
36
31
  hosts[sub_id]
37
32
  end
@@ -14,11 +14,7 @@ module InventorySync
14
14
  end
15
15
 
16
16
  def setup_statuses
17
- @subscribed_hosts_ids = Set.new(
18
- ForemanInventoryUpload::Generators::Queries.for_slice(
19
- Host.unscoped.where(organization: input[:organization_id])
20
- ).pluck(:id)
21
- )
17
+ @subscribed_hosts_ids = Set.new(affected_host_ids)
22
18
 
23
19
  InventorySync::InventoryStatus.transaction do
24
20
  InventorySync::InventoryStatus.where(host_id: @subscribed_hosts_ids).delete_all
@@ -35,8 +31,10 @@ module InventorySync
35
31
  def update_statuses_batch
36
32
  results = yield
37
33
 
38
- update_hosts_status(results.status_hashes, results.touched)
39
- host_statuses[:sync] += results.touched.size
34
+ existing_hosts = results.status_hashes.select { |hash| @subscribed_hosts_ids.include?(hash[:host_id]) }
35
+
36
+ update_hosts_status(existing_hosts)
37
+ host_statuses[:sync] += existing_hosts.size
40
38
  end
41
39
 
42
40
  def rescue_strategy_for_self
@@ -45,9 +43,10 @@ module InventorySync
45
43
 
46
44
  private
47
45
 
48
- def update_hosts_status(status_hashes, touched)
46
+ def update_hosts_status(status_hashes)
49
47
  InventorySync::InventoryStatus.create(status_hashes)
50
- @subscribed_hosts_ids.subtract(touched)
48
+ updated_ids = status_hashes.map { |hash| hash[:host_id] }
49
+ @subscribed_hosts_ids.subtract(updated_ids)
51
50
  end
52
51
 
53
52
  def add_missing_hosts_statuses(hosts_ids)
@@ -68,6 +67,12 @@ module InventorySync
68
67
  disconnect: 0,
69
68
  }
70
69
  end
70
+
71
+ def affected_host_ids
72
+ ForemanInventoryUpload::Generators::Queries.for_slice(
73
+ Host.unscoped.where(organization: input[:organization_id])
74
+ ).pluck(:id)
75
+ end
71
76
  end
72
77
  end
73
78
  end
@@ -35,18 +35,14 @@ module InventorySync
35
35
  private
36
36
 
37
37
  def add_missing_insights_facets(uuids_hash)
38
- existing_facets = InsightsFacet.where(host_id: uuids_hash.keys).pluck(:host_id, :uuid)
39
- missing_facets = uuids_hash.except(*existing_facets.map(&:first)).map do |host_id, uuid|
38
+ all_facets = uuids_hash.map do |host_id, uuid|
40
39
  {
41
40
  host_id: host_id,
42
41
  uuid: uuid,
43
42
  }
44
43
  end
45
- InsightsFacet.create(missing_facets)
46
44
 
47
- existing_facets.select { |host_id, uuid| uuid.empty? }.each do |host_id, _uuid|
48
- InsightsFacet.where(host_id: host_id).update_all(uuid: uuids_hash[host_id])
49
- end
45
+ InsightsFacet.upsert_all(all_facets, unique_by: :host_id) unless all_facets.empty?
50
46
  end
51
47
 
52
48
  def plan_self_host_sync
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreman_rh_cloud",
3
- "version": "5.0.28",
3
+ "version": "5.0.29",
4
4
  "description": "Inventory Upload =============",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -4,7 +4,7 @@ class SettingsControllerTest < ActionController::TestCase
4
4
  tests InsightsCloud::SettingsController
5
5
 
6
6
  test 'should return allow_auto_insights_sync setting' do
7
- FactoryBot.create(:setting, :name => 'allow_auto_insights_sync', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => false)
7
+ Setting[:allow_auto_insights_sync] = false
8
8
 
9
9
  assert_equal false, Setting[:allow_auto_insights_sync]
10
10
 
@@ -16,7 +16,7 @@ class SettingsControllerTest < ActionController::TestCase
16
16
  end
17
17
 
18
18
  test 'should update allow_auto_insights_sync setting' do
19
- FactoryBot.create(:setting, :name => 'allow_auto_insights_sync', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => false)
19
+ Setting[:allow_auto_insights_sync] = false
20
20
 
21
21
  assert_equal false, Setting[:allow_auto_insights_sync]
22
22
 
@@ -4,7 +4,7 @@ class UploadsSettingsControllerTest < ActionController::TestCase
4
4
  tests ForemanInventoryUpload::UploadsSettingsController
5
5
 
6
6
  test 'should get upload inventory settings' do
7
- FactoryBot.create(:setting, :name => 'allow_auto_inventory_upload', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => true)
7
+ Setting[:allow_auto_inventory_upload] = true
8
8
 
9
9
  assert_equal true, Setting[:allow_auto_inventory_upload]
10
10
 
@@ -17,7 +17,7 @@ class UploadsSettingsControllerTest < ActionController::TestCase
17
17
  end
18
18
 
19
19
  test 'should update allow_auto_inventory_upload setting' do
20
- FactoryBot.create(:setting, :name => 'allow_auto_inventory_upload', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => false)
20
+ Setting[:allow_auto_inventory_upload] = false
21
21
 
22
22
  assert_equal false, Setting[:allow_auto_inventory_upload]
23
23
 
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class InsightsFullSyncTest < ActiveSupport::TestCase
@@ -7,7 +7,7 @@ class InsightsFullSyncTest < ActiveSupport::TestCase
7
7
  setup do
8
8
  InsightsCloud::Async::InsightsFullSync.any_instance.stubs(:plan_rules_sync)
9
9
  InsightsCloud::Async::InsightsFullSync.any_instance.stubs(:plan_notifications)
10
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'MOCK_TOKEN')
10
+ Setting[:rh_cloud_token] = 'MOCK_TOKEN'
11
11
 
12
12
  uuid1 = 'accdf444-5628-451d-bf3e-cf909ad72756'
13
13
  @host1 = FactoryBot.create(:host, :managed, name: 'host1')
@@ -63,7 +63,7 @@ class InsightsResolutionsSyncTest < ActiveSupport::TestCase
63
63
  }
64
64
 
65
65
  @rule = FactoryBot.create(:insights_rule, rule_id: 'network_tcp_connection_hang|NETWORK_TCP_CONNECTION_HANG_WARN')
66
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'MOCK_TOKEN')
66
+ Setting[:rh_cloud_token] = 'MOCK_TOKEN'
67
67
  end
68
68
 
69
69
  test 'Resolutions data is replaced with data from cloud' do
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require 'test_plugin_helper'
2
2
  require 'foreman_tasks/test_helpers'
3
3
 
4
4
  class InsightsRulesSyncTest < ActiveSupport::TestCase
@@ -112,7 +112,7 @@ class InsightsRulesSyncTest < ActiveSupport::TestCase
112
112
  @hit = FactoryBot.create(:insights_hit, host_id: @host.id)
113
113
 
114
114
  InsightsCloud::Async::InsightsRulesSync.any_instance.stubs(:plan_resolutions)
115
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'MOCK_TOKEN')
115
+ Setting[:rh_cloud_token] = 'MOCK_TOKEN'
116
116
  end
117
117
 
118
118
  test 'Hits data is replaced with data from cloud' do
@@ -37,6 +37,18 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
37
37
  @host2.subscription_facet.pools << pool
38
38
  @host2_inventory_id = '4536bf5c-ff03-4154-a8c9-32ff4b40e40c'
39
39
 
40
+ # this host would pass our plugin queries, so it could be uploaded to the cloud.
41
+ @host3 = FactoryBot.create(
42
+ :host,
43
+ :with_subscription,
44
+ :with_content,
45
+ content_view: cv.first,
46
+ lifecycle_environment: env,
47
+ organization: env.organization
48
+ )
49
+
50
+ @host3.subscription_facet.pools << pool
51
+
40
52
  ForemanInventoryUpload::Generators::Queries.instance_variable_set(:@fact_names, nil)
41
53
 
42
54
  inventory_json = <<-INVENTORY_JSON
@@ -151,7 +163,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
151
163
  {
152
164
  "insights_id": "b533848e-465f-4f1a-9b2b-b71cb2d5239d",
153
165
  "rhel_machine_id": null,
154
- "subscription_manager_id": "d29bde40-348e-437c-8acf-8fa98320fc1b",
166
+ "subscription_manager_id": "#{@host3.subscription_facet.uuid}",
155
167
  "satellite_id": "d29bde40-348e-437c-8acf-8fa98320fc1b",
156
168
  "bios_uuid": "3cd5d972-cfb5-451a-8314-fd2f56629d7c",
157
169
  "ip_addresses": [
@@ -159,7 +171,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
159
171
  "fd6e:2298:736e::857",
160
172
  "fd6e:2298:736e:0:2c66:6101:9cc6:2b23"
161
173
  ],
162
- "fqdn": "rhel8-demo.oss-lab.net",
174
+ "fqdn": "#{@host3.fqdn}",
163
175
  "mac_addresses": [
164
176
  "6e:66:a6:fe:fc:07",
165
177
  "00:00:00:00:00:00"
@@ -242,7 +254,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
242
254
  end
243
255
 
244
256
  test 'Host status should be SYNC for inventory hosts' do
245
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'TEST TOKEN')
257
+ Setting[:rh_cloud_token] = 'TEST TOKEN'
246
258
  InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
247
259
 
248
260
  ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host2.organization)
@@ -254,7 +266,7 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
254
266
  end
255
267
 
256
268
  test 'Host status should be DISCONNECT for hosts that are not returned from cloud' do
257
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'TEST TOKEN')
269
+ Setting[:rh_cloud_token] = 'TEST TOKEN'
258
270
  InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
259
271
  FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
260
272
 
@@ -265,10 +277,24 @@ class InventoryFullSyncTest < ActiveSupport::TestCase
265
277
  end
266
278
 
267
279
  test 'Task should be aborted if token is not present' do
268
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: '')
280
+ Setting[:rh_cloud_token] = ''
269
281
 
270
282
  InventorySync::Async::InventoryFullSync.any_instance.expects(:plan_self).never
271
283
 
272
284
  ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host1.organization)
273
285
  end
286
+
287
+ test 'Should skip hosts that are not returned in query' do
288
+ assert_nil InventorySync::InventoryStatus.where(host_id: @host3.id).first
289
+
290
+ Setting[:rh_cloud_token] = 'TEST TOKEN'
291
+ InventorySync::Async::InventoryFullSync.any_instance.expects(:query_inventory).returns(@inventory)
292
+ InventorySync::Async::InventoryFullSync.any_instance.expects(:affected_host_ids).returns([@host1.id, @host2.id])
293
+ FactoryBot.create(:fact_value, fact_name: fact_names['virt::uuid'], value: '1234', host: @host2)
294
+
295
+ ForemanTasks.sync_task(InventorySync::Async::InventoryFullSync, @host1.organization)
296
+ @host2.reload
297
+
298
+ assert_nil InventorySync::InventoryStatus.where(host_id: @host3.id).first
299
+ end
274
300
  end
@@ -6,7 +6,7 @@ class InventoryHostsSyncTest < ActiveSupport::TestCase
6
6
 
7
7
  setup do
8
8
  User.current = User.find_by(login: 'secret_admin')
9
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'MOCK_TOKEN')
9
+ Setting[:rh_cloud_token] = 'MOCK_TOKEN'
10
10
 
11
11
  env = FactoryBot.create(:katello_k_t_environment)
12
12
  cv = env.content_views << FactoryBot.create(:katello_content_view, organization: env.organization)
@@ -265,4 +265,19 @@ class InventoryHostsSyncTest < ActiveSupport::TestCase
265
265
 
266
266
  assert_equal @host2_inventory_id, @host2.insights.uuid
267
267
  end
268
+
269
+ test 'Inventory should sync empty facets list' do
270
+ empty_inventory = @inventory.deep_clone
271
+ empty_inventory['results'] = []
272
+ InventorySync::Async::InventoryHostsSync.any_instance.expects(:query_inventory).returns(empty_inventory)
273
+ InventorySync::Async::InventoryHostsSync.any_instance.expects(:plan_self_host_sync)
274
+
275
+ assert_nil @host2.insights
276
+
277
+ ForemanTasks.sync_task(InventorySync::Async::InventoryHostsSync)
278
+
279
+ @host2.reload
280
+
281
+ assert_nil @host2.insights
282
+ end
268
283
  end
@@ -5,7 +5,7 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
5
5
  include ForemanTasks::TestHelpers::WithInThreadExecutor
6
6
 
7
7
  test 'Schedules an execution if auto upload is enabled' do
8
- FactoryBot.create(:setting, :name => 'allow_auto_inventory_upload', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => true)
8
+ Setting[:allow_auto_inventory_upload] = true
9
9
 
10
10
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).times(Organization.unscoped.count)
11
11
 
@@ -13,7 +13,7 @@ class InventoryScheduledSyncTest < ActiveSupport::TestCase
13
13
  end
14
14
 
15
15
  test 'Skips execution if auto upload is disabled' do
16
- FactoryBot.create(:setting, :name => 'allow_auto_inventory_upload', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => false)
16
+ Setting[:allow_auto_inventory_upload] = false
17
17
 
18
18
  InventorySync::Async::InventoryScheduledSync.any_instance.expects(:plan_org_sync).never
19
19
 
@@ -6,7 +6,7 @@ class InventorySelfHostSyncTest < ActiveSupport::TestCase
6
6
 
7
7
  setup do
8
8
  User.current = User.find_by(login: 'secret_admin')
9
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'MOCK_TOKEN')
9
+ Setting[:rh_cloud_token] = 'MOCK_TOKEN'
10
10
 
11
11
  # this host would pass our plugin queries, so it could be uploaded to the cloud.
12
12
  @host1 = FactoryBot.create(:host)
@@ -12,7 +12,7 @@ class UploadReportJobTest < ActiveSupport::TestCase
12
12
  'idCert' => 'TEST_CERT',
13
13
  }
14
14
  )
15
- FactoryBot.create(:setting, :name => 'content_disconnected', :value => true)
15
+ Setting[:content_disconnected] = true
16
16
 
17
17
  ForemanTasks.sync_task(ForemanInventoryUpload::Async::UploadReportJob, '', organization.id)
18
18
 
@@ -32,8 +32,6 @@ module KatelloLocationFix
32
32
 
33
33
  included do
34
34
  setup do
35
- FactoryBot.create(:setting, name: 'default_location_subscribed_hosts')
36
- FactoryBot.create(:setting, name: 'default_location_puppet_content')
37
35
  Setting[:default_location_subscribed_hosts] = Location.first.title
38
36
  end
39
37
  end
@@ -41,7 +41,7 @@ class RhCloudHttpProxyTest < ActiveSupport::TestCase
41
41
  end
42
42
 
43
43
  def setup_global_foreman_proxy
44
- FactoryBot.create(:setting, :name => 'http_proxy', :value => @global_foreman_proxy_mock)
44
+ Setting[:http_proxy] = @global_foreman_proxy_mock
45
45
  end
46
46
 
47
47
  def setup_cdn_proxy
@@ -38,7 +38,7 @@ class BranchInfoTest < ActiveSupport::TestCase
38
38
  test 'should generate appropriate labels' do
39
39
  2.times { FactoryBot.create(:katello_host_collection, :organization_id => @host.organization.id) }
40
40
 
41
- FactoryBot.create(:setting, :settings_type => 'boolean', :default => true, :name => :include_parameter_tags)
41
+ Setting[:include_parameter_tags] = true
42
42
 
43
43
  Katello::HostCollection.all.map do |collection|
44
44
  FactoryBot.create(:katello_host_collection_host, :host_id => @host.id, :host_collection_id => collection.id)
@@ -8,7 +8,7 @@ class TemplateRendererHelperTest < ActiveSupport::TestCase
8
8
  response.stubs(:body).returns('TEST PLAYBOOK')
9
9
  ForemanRhCloud::RemediationsRetriever.any_instance.stubs(:query_playbook).returns(response)
10
10
  @host1 = FactoryBot.create(:host)
11
- FactoryBot.create(:setting, name: 'rh_cloud_token', value: 'MOCK_TOKEN')
11
+ Setting[:rh_cloud_token] = 'MOCK_TOKEN'
12
12
  end
13
13
 
14
14
  test 'Generates a playbook for hit and remediation' do
@@ -189,7 +189,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
189
189
  end
190
190
 
191
191
  test 'generates obfuscated ip_address fields without inisghts-client' do
192
- FactoryBot.create(:setting, :name => 'obfuscate_inventory_ips', :value => true)
192
+ Setting[:obfuscate_inventory_ips] = true
193
193
 
194
194
  @host.interfaces << FactoryBot.build(:nic_managed)
195
195
  batch = Host.where(id: @host.id).in_batches.first
@@ -260,7 +260,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
260
260
  end
261
261
 
262
262
  test 'obfuscates fqdn when setting set' do
263
- FactoryBot.create(:setting, :name => 'obfuscate_inventory_hostnames', :value => true)
263
+ Setting[:obfuscate_inventory_hostnames] = true
264
264
 
265
265
  batch = Host.where(id: @host.id).in_batches.first
266
266
  generator = create_generator(batch)
@@ -447,6 +447,18 @@ class SliceGeneratorTest < ActiveSupport::TestCase
447
447
  assert_equal 1, generator.hosts_count
448
448
  end
449
449
 
450
+ test 'excludes hosts with host_registration_insights set to false' do
451
+ @host.host_parameters << HostParameter.create(
452
+ name: 'host_registration_insights',
453
+ value: "false",
454
+ parameter_type: 'boolean'
455
+ )
456
+
457
+ count = ForemanInventoryUpload::Generators::Queries.for_org(@host.organization_id).count
458
+
459
+ assert_equal 0, count
460
+ end
461
+
450
462
  test 'shows system_memory_bytes in bytes' do
451
463
  FactoryBot.create(:fact_value, fact_name: fact_names['memory::memtotal'], value: '1', host: @host)
452
464
 
@@ -625,7 +637,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
625
637
  end
626
638
 
627
639
  test 'include packages installed in the report' do
628
- FactoryBot.create(:setting, :name => 'exclude_installed_packages', :value => false)
640
+ Setting[:exclude_installed_packages] = false
629
641
  installed_package = ::Katello::InstalledPackage.create(name: 'test-package', nvrea: 'test-package-1.0.x86_64', nvra: 'test-package-1.0.x86_64')
630
642
 
631
643
  another_host = FactoryBot.create(
@@ -63,7 +63,7 @@ class TagsGeneratorTest < ActiveSupport::TestCase
63
63
  end
64
64
 
65
65
  test 'generates parameter tags' do
66
- FactoryBot.create(:setting, :name => 'include_parameter_tags', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => true)
66
+ Setting[:include_parameter_tags] = true
67
67
 
68
68
  @host.stubs(:host_params).returns(
69
69
  {
@@ -85,7 +85,7 @@ class TagsGeneratorTest < ActiveSupport::TestCase
85
85
  end
86
86
 
87
87
  test 'skips parameter tags if include_parameter_tags setting is off' do
88
- FactoryBot.create(:setting, :name => 'include_parameter_tags', :settings_type => "boolean", :category => "Setting::RhCloud", :default => false, :value => false)
88
+ Setting[:include_parameter_tags] = false
89
89
 
90
90
  @host.stubs(:host_params).returns(
91
91
  {
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
3
3
  import InventoryAutoUploadSwitcher from './ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload';
4
+ import NewHostDetailsTab from './InsightsHostDetailsTab/NewHostDetailsTab';
4
5
 
5
6
  const fills = [
6
7
  {
@@ -9,6 +10,12 @@ const fills = [
9
10
  component: () => <InventoryAutoUploadSwitcher />,
10
11
  weight: 50,
11
12
  },
13
+ {
14
+ slot: 'host-details-page-tabs',
15
+ name: 'Insights',
16
+ component: props => <NewHostDetailsTab {...props} />,
17
+ weight: 700,
18
+ },
12
19
  ];
13
20
 
14
21
  export const registerFills = () => {
@@ -4,7 +4,10 @@ import PropTypes from 'prop-types';
4
4
  import { Table, TableHeader, TableBody } from '@patternfly/react-table';
5
5
  import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
6
6
  import SelectAllAlert from './SelectAllAlert';
7
- import { columns } from './InsightsTableConstants';
7
+ import {
8
+ columns as defaultColumns,
9
+ getColumnsWithoutHostname,
10
+ } from './InsightsTableConstants';
8
11
  import TableEmptyState from '../../../common/table/EmptyState';
9
12
  import { modifySelectedRows, getSortColumnIndex } from './InsightsTableHelpers';
10
13
  import Pagination from './Pagination';
@@ -27,10 +30,12 @@ const InsightsTable = ({
27
30
  clearAllSelection,
28
31
  error,
29
32
  isAllSelected,
33
+ hideHost,
30
34
  }) => {
31
35
  const { perPage: appPerPage } = useForemanSettings();
32
36
  const perPage = urlPerPage || appPerPage;
33
37
  const [rows, setRows] = React.useState([]);
38
+ const [columns, setColumns] = React.useState(defaultColumns);
34
39
 
35
40
  // acts as componentDidMount
36
41
  useEffect(() => {
@@ -38,8 +43,12 @@ const InsightsTable = ({
38
43
  }, []);
39
44
 
40
45
  useEffect(() => {
41
- setRows(modifySelectedRows(hits, selectedIds, showSelectAllAlert));
42
- }, [hits, selectedIds]);
46
+ setRows(
47
+ modifySelectedRows(hits, selectedIds, showSelectAllAlert, hideHost)
48
+ );
49
+
50
+ if (hideHost) setColumns(getColumnsWithoutHostname());
51
+ }, [hits, selectedIds, hideHost]);
43
52
 
44
53
  return (
45
54
  <React.Fragment>
@@ -51,14 +60,19 @@ const InsightsTable = ({
51
60
  isAllSelected={isAllSelected}
52
61
  />
53
62
  <Table
54
- className="recommendations-table"
63
+ className="rh-cloud-recommendations-table"
55
64
  aria-label="Recommendations Table"
56
65
  onSelect={(_event, isSelected, rowId) =>
57
66
  onTableSelect(isSelected, rowId, rows, selectedIds)
58
67
  }
59
68
  canSelectAll
60
- sortBy={{ index: getSortColumnIndex(sortBy), direction: sortOrder }}
61
- onSort={onTableSort}
69
+ sortBy={{
70
+ index: getSortColumnIndex(columns, sortBy),
71
+ direction: sortOrder,
72
+ }}
73
+ onSort={(_event, index, direction) =>
74
+ onTableSort(columns, index, direction)
75
+ }
62
76
  cells={columns}
63
77
  rows={rows}
64
78
  variant="compact"
@@ -89,6 +103,7 @@ InsightsTable.propTypes = {
89
103
  query: PropTypes.string,
90
104
  error: PropTypes.string,
91
105
  isAllSelected: PropTypes.bool,
106
+ hideHost: PropTypes.bool,
92
107
  };
93
108
 
94
109
  InsightsTable.defaultProps = {
@@ -102,6 +117,7 @@ InsightsTable.defaultProps = {
102
117
  query: '',
103
118
  error: '',
104
119
  isAllSelected: false,
120
+ hideHost: false,
105
121
  };
106
122
 
107
123
  export default InsightsTable;
@@ -2,14 +2,13 @@ import URI from 'urijs';
2
2
  import { push } from 'connected-react-router';
3
3
  import { get } from 'foremanReact/redux/API';
4
4
  import { selectQueryParams } from './InsightsTableSelectors';
5
- import { INSIGHTS_PATH } from '../../InsightsCloudSyncConstants';
6
5
  import {
7
- columns,
8
6
  INSIGHTS_HITS_API_KEY,
9
7
  INSIGHTS_HITS_PATH,
10
8
  INSIGHTS_SET_SELECTED_IDS,
11
9
  INSIGHTS_SET_SELECT_ALL_ALERT,
12
10
  INSIGHTS_SET_SELECT_ALL,
11
+ NEW_HOST_PATH,
13
12
  } from './InsightsTableConstants';
14
13
 
15
14
  export const fetchInsights = (queryParams = {}) => (dispatch, getState) => {
@@ -29,17 +28,20 @@ export const fetchInsights = (queryParams = {}) => (dispatch, getState) => {
29
28
  select_all: isSelectAll,
30
29
  });
31
30
 
32
- dispatch(
33
- push({
34
- pathname: INSIGHTS_PATH,
35
- search: uri.search(),
36
- })
37
- );
31
+ updateUrl(uri, dispatch);
38
32
 
39
33
  if (!isSelectAll) {
40
34
  dispatch(setSelectAllAlert(false));
41
35
  }
42
36
 
37
+ let search = query;
38
+ if (isNewHostPage(uri)) {
39
+ const hostname = uri.pathname().split('/new/hosts/')[1];
40
+ const hostQuery = `hostname = ${hostname}`;
41
+ const q = query?.trim();
42
+ search = q ? `${hostQuery} AND (${q})` : hostQuery;
43
+ }
44
+
43
45
  return dispatch(
44
46
  get({
45
47
  key: INSIGHTS_HITS_API_KEY,
@@ -47,7 +49,7 @@ export const fetchInsights = (queryParams = {}) => (dispatch, getState) => {
47
49
  params: {
48
50
  page,
49
51
  per_page: perPage,
50
- search: query,
52
+ search,
51
53
  order: `${sortBy} ${sortOrder}`,
52
54
  },
53
55
  handleSuccess: response => {
@@ -96,7 +98,7 @@ export const clearAllSelection = () => dispatch => {
96
98
  dispatch(setSelectAll(false));
97
99
  };
98
100
 
99
- export const onTableSort = (_event, index, direction) => {
101
+ export const onTableSort = (columns, index, direction) => {
100
102
  // The checkbox column shifts the data columns by 1;
101
103
  const { sortKey } = columns[index - 1];
102
104
  return fetchInsights({
@@ -144,11 +146,16 @@ export const onTableSelect = (
144
146
  const setSelectAllUrl = selectAllValue => dispatch => {
145
147
  const uri = new URI();
146
148
  uri.setSearch({ select_all: selectAllValue });
149
+ updateUrl(uri, dispatch);
150
+ };
147
151
 
148
- dispatch(
149
- push({
150
- pathname: INSIGHTS_PATH,
151
- search: uri.search(),
152
- })
153
- );
152
+ const updateUrl = (uri, dispatch) => {
153
+ const nextUrlParams = { search: uri.search() };
154
+ if (isNewHostPage(uri)) {
155
+ // we need to keep the hash so the insights tab will remain selected in the new host details page.
156
+ nextUrlParams.hash = '/Insights';
157
+ }
158
+ dispatch(push(nextUrlParams));
154
159
  };
160
+
161
+ const isNewHostPage = uri => uri.pathname().includes(NEW_HOST_PATH);
@@ -4,10 +4,12 @@ import {
4
4
  InsightsLabel,
5
5
  Section,
6
6
  } from '@redhat-cloud-services/frontend-components';
7
+ import { DropdownItem } from '@patternfly/react-core';
7
8
  import { sortable, cellWidth } from '@patternfly/react-table';
8
- import { AnsibeTowerIcon } from '@patternfly/react-icons';
9
+ import { AnsibeTowerIcon, ExternalLinkAltIcon } from '@patternfly/react-icons';
9
10
  import { translate as __ } from 'foremanReact/common/I18n';
10
11
  import { foremanUrl } from '../../../ForemanRhCloudHelpers';
12
+ import DropdownToggle from '../../../common/DropdownToggle';
11
13
 
12
14
  export const totalRiskFormatter = ({ title: totalRisk }) => ({
13
15
  children: (
@@ -28,30 +30,73 @@ export const hasPlaybookFormatter = ({ title: hasPlaybook }) => ({
28
30
  ),
29
31
  });
30
32
 
33
+ export const actionsFormatter = (props, { rowData = {} }) => {
34
+ const { recommendationUrl, accessRHUrl } = rowData;
35
+ const dropdownItems = [];
36
+
37
+ recommendationUrl &&
38
+ dropdownItems.push(
39
+ <DropdownItem key="recommendation-url">
40
+ <a href={recommendationUrl} target="_blank" rel="noopener noreferrer">
41
+ {__('View in Red Hat Insights')} <ExternalLinkAltIcon />
42
+ </a>
43
+ </DropdownItem>
44
+ );
45
+
46
+ accessRHUrl &&
47
+ dropdownItems.push(
48
+ <DropdownItem key="access-url">
49
+ <a href={accessRHUrl} target="_blank" rel="noopener noreferrer">
50
+ {__('Knowledgebase article')} <ExternalLinkAltIcon />
51
+ </a>
52
+ </DropdownItem>
53
+ );
54
+
55
+ return {
56
+ children: <DropdownToggle items={dropdownItems} />,
57
+ };
58
+ };
59
+
31
60
  export const columns = [
32
61
  {
62
+ id: 'hostname',
33
63
  sortKey: 'hostname',
34
64
  title: __('Hostname'),
35
65
  transforms: [cellWidth(20), sortable],
36
66
  },
37
67
  {
68
+ id: 'recommendation',
38
69
  sortKey: 'title',
39
70
  title: __('Recommendation'),
40
71
  transforms: [cellWidth(50), sortable],
41
72
  },
42
73
  {
74
+ id: 'total risk',
43
75
  sortKey: 'total_risk',
44
76
  title: __('Total risk'),
45
77
  transforms: [cellWidth(15), sortable],
46
78
  cellTransforms: [totalRiskFormatter],
47
79
  },
48
80
  {
81
+ id: 'remediate',
49
82
  title: __('Remediate'),
50
- transforms: [cellWidth(15)],
83
+ transforms: [cellWidth(10)],
51
84
  cellTransforms: [hasPlaybookFormatter],
52
85
  },
86
+ {
87
+ id: 'actions',
88
+ title: '',
89
+ transforms: [cellWidth(5)],
90
+ cellTransforms: [actionsFormatter],
91
+ },
53
92
  ];
54
93
 
94
+ export const getColumnsWithoutHostname = () => {
95
+ const nextCols = columns.slice(1);
96
+ nextCols[0].transforms = [cellWidth(70), sortable];
97
+ return nextCols;
98
+ };
99
+
55
100
  export const paginationTitles = {
56
101
  items: __('items'),
57
102
  page: __('page'),
@@ -75,3 +120,5 @@ export const INSIGHTS_SET_SELECTED_IDS = 'INSIGHTS_SET_SELECTED_IDS';
75
120
  export const INSIGHTS_SET_SELECT_ALL_ALERT = 'INSIGHTS_SET_SELECT_ALL_ALERT';
76
121
 
77
122
  export const INSIGHTS_SET_SELECT_ALL = 'INSIGHTS_SET_SELECT_ALL';
123
+
124
+ export const NEW_HOST_PATH = '/new/hosts/';
@@ -1,25 +1,42 @@
1
1
  /* eslint-disable camelcase */
2
- import { columns } from './InsightsTableConstants';
3
-
4
- export const modifySelectedRows = (hits, selectedIds, showSelectAllAlert) => {
2
+ export const modifySelectedRows = (
3
+ hits,
4
+ selectedIds,
5
+ showSelectAllAlert,
6
+ hideHost
7
+ ) => {
5
8
  if (hits.length === 0) return [];
6
9
 
7
10
  return hits
8
11
  .asMutable()
9
- .map(({ id, hostname, title, total_risk, has_playbook }) => {
10
- const disableCheckbox = !has_playbook;
11
- return {
12
- cells: [hostname, title, total_risk, has_playbook],
13
- disableCheckbox,
12
+ .map(
13
+ ({
14
14
  id,
15
- /** The main table checkbox will be seen as selected only if all rows are selected,
16
- * in this case we need to select also the disabled once and hide it with css */
17
- selected: selectedIds[id] || (disableCheckbox && showSelectAllAlert),
18
- };
19
- });
15
+ hostname,
16
+ title,
17
+ total_risk,
18
+ has_playbook,
19
+ results_url,
20
+ solution_url,
21
+ }) => {
22
+ const disableCheckbox = !has_playbook;
23
+ const cells = [hostname, title, total_risk, has_playbook, results_url];
24
+ if (hideHost) cells.shift();
25
+ return {
26
+ cells,
27
+ disableCheckbox,
28
+ id,
29
+ /** The main table checkbox will be seen as selected only if all rows are selected,
30
+ * in this case we need to select also the disabled once and hide it with css */
31
+ selected: selectedIds[id] || (disableCheckbox && showSelectAllAlert),
32
+ recommendationUrl: results_url,
33
+ accessRHUrl: solution_url,
34
+ };
35
+ }
36
+ );
20
37
  };
21
38
 
22
- export const getSortColumnIndex = sortBy => {
39
+ export const getSortColumnIndex = (columns, sortBy) => {
23
40
  let colIndex = 0;
24
41
  columns.forEach((col, index) => {
25
42
  if (col.sortKey === sortBy) {
@@ -17,6 +17,7 @@ exports[`InsightsTable rendering render with Props 1`] = `
17
17
  cells={
18
18
  Array [
19
19
  Object {
20
+ "id": "hostname",
20
21
  "sortKey": "hostname",
21
22
  "title": "Hostname",
22
23
  "transforms": Array [
@@ -25,6 +26,7 @@ exports[`InsightsTable rendering render with Props 1`] = `
25
26
  ],
26
27
  },
27
28
  Object {
29
+ "id": "recommendation",
28
30
  "sortKey": "title",
29
31
  "title": "Recommendation",
30
32
  "transforms": Array [
@@ -36,6 +38,7 @@ exports[`InsightsTable rendering render with Props 1`] = `
36
38
  "cellTransforms": Array [
37
39
  [Function],
38
40
  ],
41
+ "id": "total risk",
39
42
  "sortKey": "total_risk",
40
43
  "title": "Total risk",
41
44
  "transforms": Array [
@@ -47,14 +50,25 @@ exports[`InsightsTable rendering render with Props 1`] = `
47
50
  "cellTransforms": Array [
48
51
  [Function],
49
52
  ],
53
+ "id": "remediate",
50
54
  "title": "Remediate",
51
55
  "transforms": Array [
52
56
  [Function],
53
57
  ],
54
58
  },
59
+ Object {
60
+ "cellTransforms": Array [
61
+ [Function],
62
+ ],
63
+ "id": "actions",
64
+ "title": "",
65
+ "transforms": Array [
66
+ [Function],
67
+ ],
68
+ },
55
69
  ]
56
70
  }
57
- className="recommendations-table"
71
+ className="rh-cloud-recommendations-table"
58
72
  contentId="expanded-content"
59
73
  dropdownDirection="down"
60
74
  dropdownPosition="right"
@@ -31,7 +31,6 @@ Array [
31
31
  "payload": Object {
32
32
  "args": Array [
33
33
  Object {
34
- "pathname": "/foreman_rh_cloud/insights_cloud",
35
34
  "search": "?page=2&per_page=7&search=&sort_by=&sort_order=&select_all=true",
36
35
  },
37
36
  ],
@@ -1,21 +1,19 @@
1
1
  @import '~@redhat-cloud-services/frontend-components/index.css';
2
2
 
3
- .rh-cloud-insights {
4
- .recommendations-table {
5
- .pf-c-table__check {
6
- input:disabled {
7
- display: none;
8
- }
3
+ .rh-cloud-recommendations-table {
4
+ .pf-c-table__check {
5
+ input:disabled {
6
+ display: none;
9
7
  }
8
+ }
10
9
 
11
- .td-insights-remediate-playbook {
12
- svg {
13
- margin-right: 5px;
14
- }
10
+ .td-insights-remediate-playbook {
11
+ svg {
12
+ margin-right: 5px;
15
13
  }
14
+ }
16
15
 
17
- .td-insights-remediate-manual {
18
- padding-left: 18px;
19
- }
16
+ .td-insights-remediate-manual {
17
+ padding-left: 18px;
20
18
  }
21
19
  }
@@ -6,18 +6,4 @@
6
6
  margin-top: 5px;
7
7
  }
8
8
  }
9
-
10
- // applies to the backdrop parent of the modal
11
- @at-root .pf-c-backdrop {
12
- width: calc(100% - 200px);
13
- left: 200px;
14
- }
15
-
16
- // where the vertical nav breaks: https://github.com/theforeman/foreman/blob/3347fa49d500964f0209122d8d36c920d1feafcc/webpack/assets/javascripts/react_app/components/Layout/components/Toolbar/HeaderToolbar.scss#L26
17
- @media (max-width: 768px) {
18
- @at-root .pf-c-backdrop {
19
- width: 100%;
20
- left: 0;
21
- }
22
- }
23
9
  }
@@ -2,6 +2,8 @@ import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { translate as __ } from 'foremanReact/common/I18n';
4
4
  import { Dropdown, DropdownItem, KebabToggle } from '@patternfly/react-core';
5
+ import { ExternalLinkAltIcon } from '@patternfly/react-icons';
6
+ import { redHatAdvisorSystems } from '../InsightsCloudSyncHelpers';
5
7
 
6
8
  const ToolbarDropdown = ({ onRecommendationSync }) => {
7
9
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
@@ -12,6 +14,17 @@ const ToolbarDropdown = ({ onRecommendationSync }) => {
12
14
  >
13
15
  {__('Sync recommendations')}
14
16
  </DropdownItem>,
17
+ <DropdownItem key="cloud-advisor-systems-link">
18
+ <a
19
+ href={redHatAdvisorSystems()}
20
+ target="_blank"
21
+ rel="noopener noreferrer"
22
+ >
23
+ {__('View in Red Hat Insights')}
24
+ {' '}
25
+ <ExternalLinkAltIcon />
26
+ </a>
27
+ </DropdownItem>,
15
28
  ];
16
29
  return (
17
30
  <Dropdown
@@ -8,3 +8,11 @@ export const cloudTokenSettingUrl = () => {
8
8
  settingsUrl.setSearch({ search: 'name = rh_cloud_token' });
9
9
  return settingsUrl.toString();
10
10
  };
11
+
12
+ export const redHatConsole = path => `https://console.redhat.com/${path || ''}`;
13
+ export const redHatInsights = path => redHatConsole(`insights/${path || ''}`);
14
+ export const redHatInventory = path =>
15
+ redHatInsights(`inventory/${path || ''}`);
16
+ export const redHatAdvisor = path => redHatInsights(`advisor/${path || ''}`);
17
+ export const redHatAdvisorSystems = path =>
18
+ redHatAdvisor(`systems/${path || ''}`);
@@ -84,3 +84,7 @@
84
84
  }
85
85
  }
86
86
  }
87
+
88
+ #new_host_details_insights_tab {
89
+ padding: 24px;
90
+ }
@@ -0,0 +1,104 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
+ import SearchBar from 'foremanReact/components/SearchBar';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+ import {
7
+ Grid,
8
+ GridItem,
9
+ Dropdown,
10
+ DropdownItem,
11
+ KebabToggle,
12
+ } from '@patternfly/react-core';
13
+ import { ExternalLinkAltIcon } from '@patternfly/react-icons';
14
+ import InsightsTable from '../InsightsCloudSync/Components/InsightsTable';
15
+ import RemediationModal from '../InsightsCloudSync/Components/RemediationModal';
16
+ import Pagination from '../InsightsCloudSync/Components/InsightsTable/Pagination';
17
+ import { INSIGHTS_SEARCH_PROPS } from '../InsightsCloudSync/InsightsCloudSyncConstants';
18
+ import { fetchInsights } from '../InsightsCloudSync/Components/InsightsTable/InsightsTableActions';
19
+ import {
20
+ selectSearch,
21
+ selectHits,
22
+ } from '../InsightsCloudSync/Components/InsightsTable/InsightsTableSelectors';
23
+ import './InsightsTab.scss';
24
+ import { redHatAdvisorSystems } from '../InsightsCloudSync/InsightsCloudSyncHelpers';
25
+
26
+ const NewHostDetailsTab = ({ hostName, router }) => {
27
+ const dispatch = useDispatch();
28
+ const query = useSelector(selectSearch);
29
+ const hits = useSelector(selectHits);
30
+
31
+ useEffect(() => () => router.replace({ search: null }), [router]);
32
+
33
+ const onSearch = q => dispatch(fetchInsights({ query: q, page: 1 }));
34
+
35
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
36
+ const onSatInsightsClick = () =>
37
+ router.push({ pathname: '/foreman_rh_cloud/insights_cloud' });
38
+
39
+ const dropdownItems = [
40
+ <DropdownItem key="insights-link">
41
+ <a onClick={onSatInsightsClick}>{__('Go to Satellite Insights page')}</a>
42
+ </DropdownItem>,
43
+ ];
44
+
45
+ if (hits.length) {
46
+ const { host_uuid: uuid } = hits[0];
47
+ dropdownItems.push(
48
+ <DropdownItem key="insights-advisor-link">
49
+ <a
50
+ href={redHatAdvisorSystems(uuid)}
51
+ target="_blank"
52
+ rel="noopener noreferrer"
53
+ >
54
+ {__('View in Red Hat Insights')}
55
+ {' '}
56
+ <ExternalLinkAltIcon />
57
+ </a>
58
+ </DropdownItem>
59
+ );
60
+ }
61
+
62
+ return (
63
+ <Grid id="new_host_details_insights_tab" hasGutter>
64
+ <GridItem span={5}>
65
+ <SearchBar
66
+ data={INSIGHTS_SEARCH_PROPS}
67
+ onSearch={onSearch}
68
+ initialQuery={query}
69
+ />
70
+ </GridItem>
71
+ <GridItem span={4}>
72
+ <RemediationModal />
73
+ <Dropdown
74
+ className="insights-dropdown"
75
+ onSelect={() => setIsDropdownOpen(false)}
76
+ toggle={
77
+ <KebabToggle onToggle={isOpen => setIsDropdownOpen(isOpen)} />
78
+ }
79
+ isOpen={isDropdownOpen}
80
+ isPlain
81
+ dropdownItems={dropdownItems}
82
+ />
83
+ </GridItem>
84
+ <GridItem span={3}>
85
+ <Pagination variant="top" isCompact />
86
+ </GridItem>
87
+ <GridItem>
88
+ <InsightsTable hideHost hostname={hostName} />
89
+ </GridItem>
90
+ </Grid>
91
+ );
92
+ };
93
+
94
+ NewHostDetailsTab.propTypes = {
95
+ hostName: PropTypes.string,
96
+ router: PropTypes.object,
97
+ };
98
+
99
+ NewHostDetailsTab.defaultProps = {
100
+ hostName: '',
101
+ router: {},
102
+ };
103
+
104
+ export default NewHostDetailsTab;
@@ -0,0 +1,24 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Dropdown, KebabToggle } from '@patternfly/react-core';
4
+
5
+ const DropdownToggle = ({ items, ...props }) => {
6
+ const [isOpen, setOpen] = useState(false);
7
+ return (
8
+ <Dropdown
9
+ onSelect={() => setOpen(false)}
10
+ toggle={<KebabToggle onToggle={value => setOpen(value)} />}
11
+ isOpen={isOpen}
12
+ isPlain
13
+ dropdownItems={items}
14
+ position="right"
15
+ {...props}
16
+ />
17
+ );
18
+ };
19
+
20
+ DropdownToggle.propTypes = {
21
+ items: PropTypes.array.isRequired,
22
+ };
23
+
24
+ export default DropdownToggle;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_rh_cloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.28
4
+ version: 5.0.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Red Hat Cloud team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-04 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: katello
@@ -595,6 +595,7 @@ files:
595
595
  - webpack/InsightsHostDetailsTab/InsightsTabConstants.js
596
596
  - webpack/InsightsHostDetailsTab/InsightsTabReducer.js
597
597
  - webpack/InsightsHostDetailsTab/InsightsTabSelectors.js
598
+ - webpack/InsightsHostDetailsTab/NewHostDetailsTab.js
598
599
  - webpack/InsightsHostDetailsTab/__tests__/InsightsTab.fixtures.js
599
600
  - webpack/InsightsHostDetailsTab/__tests__/InsightsTab.test.js
600
601
  - webpack/InsightsHostDetailsTab/__tests__/InsightsTabActions.test.js
@@ -628,6 +629,7 @@ files:
628
629
  - webpack/__tests__/__snapshots__/ForemanRhCloudHelpers.test.js.snap
629
630
  - webpack/__tests__/__snapshots__/ForemanRhCloudSelectors.test.js.snap
630
631
  - webpack/__tests__/__snapshots__/ForemanRhCloudTestHelpers.test.js.snap
632
+ - webpack/common/DropdownToggle.js
631
633
  - webpack/common/ForemanTasks/ForemanTasksActions.js
632
634
  - webpack/common/ForemanTasks/ForemanTasksHelpers.js
633
635
  - webpack/common/ForemanTasks/index.js