foreman_rh_cloud 5.0.28 → 5.0.29

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