foreman_rh_cloud 14.2.0 → 14.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/models/concerns/rh_cloud_host.rb +36 -1
- data/config/routes.rb +1 -0
- data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +2 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/package.json +1 -1
- data/test/jobs/upload_report_direct_job_test.rb +29 -0
- data/test/unit/insights_facet_test.rb +5 -0
- data/test/unit/rh_cloud_host_test.rb +68 -0
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +5 -1
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListConstants.js +5 -0
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/EmptyResults.js +15 -6
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/emptyResults.scss +1 -5
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/EmptyState.js +15 -7
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/EmptyState.test.js +1 -3
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/emptyState.scss +1 -5
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/ErrorState.js +2 -2
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/ListItem.test.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountList.test.js +2 -6
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/Components/ClearButton/ClearButton.js +13 -10
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/InventoryFilter.js +30 -11
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/integration.test.js +3 -1
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/inventoryFilter.scss +28 -22
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/InventorySettings.js +4 -4
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/InventorySettings.scss +16 -3
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/MinimalInventoryDropdown.js +13 -14
- data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +7 -7
- data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.scss +3 -2
- data/webpack/ForemanInventoryUpload/Components/PageHeader/PageTitle.js +63 -46
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +4 -7
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageTitle.test.js +83 -12
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudPingModal/index.js +31 -38
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButton.test.js +3 -1
- data/webpack/ForemanInventoryUpload/Components/TaskConstants.js +6 -0
- data/webpack/ForemanInventoryUpload/Components/TaskHistory/TaskHistory.js +3 -2
- data/webpack/ForemanInventoryUpload/Components/TaskProgress/TaskProgress.js +4 -3
- data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +1 -1
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/InventoryAutoUpload.js +29 -22
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/InventoryAutoUpload.test.js +6 -2
- data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryHelpers.test.js +9 -6
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +90 -23
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableActions.js +1 -2
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableConstants.js +24 -30
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableHelpers.js +5 -6
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +193 -8
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableActions.test.js +4 -1
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationHelpers.js +8 -9
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +26 -13
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModalFooter.js +5 -2
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationTableConstants.js +16 -10
- data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +21 -9
- data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +12 -19
- data/webpack/InsightsCloudSync/__tests__/InsightsCloudSyncActions.test.js +0 -1
- data/webpack/IopRecommendationDetails/IopRecommendationDetails.js +19 -0
- data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware.js +5 -1
- data/webpack/common/DropdownToggle.js +20 -6
- metadata +3 -45
- data/webpack/ForemanInventoryUpload/Components/FileDownload/FileDownload.fixtures.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/FileDownload/FileDownload.js +0 -25
- data/webpack/ForemanInventoryUpload/Components/FileDownload/FileDownloadHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/FileDownload.test.js +0 -17
- data/webpack/ForemanInventoryUpload/Components/FileDownload/fileDownload.scss +0 -5
- data/webpack/ForemanInventoryUpload/Components/FileDownload/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.fixtures.js +0 -20
- data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.js +0 -66
- data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainerHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/NavContainer/__tests__/NavContainer.test.js +0 -69
- data/webpack/ForemanInventoryUpload/Components/NavContainer/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/NavContainer/navContainer.scss +0 -9
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/ScheduledRun.fixtures.js +0 -4
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/ScheduledRun.js +0 -29
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/ScheduledRunHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/ScheduledRun.test.js +0 -33
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/index.js +0 -12
- data/webpack/ForemanInventoryUpload/Components/ScheduledRun/scheduledRun.scss +0 -13
- data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChart.fixtures.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChart.js +0 -57
- data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChartHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/StatusChart.test.js +0 -30
- data/webpack/ForemanInventoryUpload/Components/StatusChart/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/StatusChart/statusChart.scss +0 -10
- data/webpack/ForemanInventoryUpload/Components/TabContainer/TabContainer.fixtures.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabContainer/TabContainer.js +0 -24
- data/webpack/ForemanInventoryUpload/Components/TabContainer/TabContainerHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/TabContainer.test.js +0 -15
- data/webpack/ForemanInventoryUpload/Components/TabContainer/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/TabContainer/tabContainer.scss +0 -10
- data/webpack/ForemanInventoryUpload/Components/TabFooter/TabFooter.fixtures.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabFooter/TabFooter.js +0 -19
- data/webpack/ForemanInventoryUpload/Components/TabFooter/TabFooterHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/TabFooter.test.js +0 -15
- data/webpack/ForemanInventoryUpload/Components/TabFooter/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/TabFooter/tabFooter.scss +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.fixtures.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.js +0 -85
- data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeaderHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabHeader/__tests__/TabHeader.test.js +0 -114
- data/webpack/ForemanInventoryUpload/Components/TabHeader/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/TabHeader/tabHeader.scss +0 -23
- data/webpack/common/Switcher/index.js +0 -80
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c2ad7078753ff687e7dfa1cf410efbfbc3f1ad9b8dc74500f9e77bf0c9a7fc9
|
|
4
|
+
data.tar.gz: 9eb2679eb38c91fa3c1163915328d91959f7e6408e73bd4fc0c4d7fe98b9a0cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0e4cb92c3a41b257ebf018a988ced92b086aa5cec373214888bb22a3dc43f765a007f42182b1da6a058c97b8633d59e1239efef658c3e0d53744bb9b8fc41125
|
|
7
|
+
data.tar.gz: 780195de89dcac76c3774a8b48ae67c7c52ad8b2b4f1029020e1c1e42ec701092f2122ff4caec64de083cf01094ebac40b42cf1d0985d06fdbbc46838331d541
|
|
@@ -10,7 +10,8 @@ module RhCloudHost
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
has_many :insights_hits, through: :insights, source: :hits
|
|
13
|
-
scoped_search :
|
|
13
|
+
scoped_search :on => :id, :rename => :insights_recommendations_count, :only_explicit => true,
|
|
14
|
+
:ext_method => :search_by_insights_recommendations_count, :complete_value => false
|
|
14
15
|
|
|
15
16
|
has_one :insights_client_report_status_object, :class_name => '::InsightsClientReportStatus', :foreign_key => 'host_id'
|
|
16
17
|
scoped_search :relation => :insights_client_report_status_object, :on => :status, :rename => :insights_client_report_status,
|
|
@@ -46,6 +47,40 @@ module RhCloudHost
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
module ClassMethods
|
|
50
|
+
def search_by_insights_recommendations_count(_key, operator, value)
|
|
51
|
+
if ForemanRhCloud.with_iop_smart_proxy?
|
|
52
|
+
raise ScopedSearch::QueryNotSupported.new(
|
|
53
|
+
_('Searching by recommendations count is not available in IoP mode.')
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
unless ScopedSearch::QueryBuilder::SQL_OPERATORS.value?(operator)
|
|
58
|
+
raise ScopedSearch::QueryNotSupported.new(
|
|
59
|
+
_('Unsupported operator for recommendations count search: %s') % operator
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
facet_table = InsightsFacet.table_name
|
|
64
|
+
hits_count_col = "COALESCE(#{facet_table}.hits_count, 0)"
|
|
65
|
+
|
|
66
|
+
if ['IN', 'NOT IN'].include?(operator)
|
|
67
|
+
values = value.is_a?(Array) ? value : value.to_s.split(',').map(&:strip)
|
|
68
|
+
placeholders = (['?'] * values.size).join(',')
|
|
69
|
+
condition = sanitize_sql_for_conditions(
|
|
70
|
+
["#{hits_count_col} #{operator} (#{placeholders})", *values]
|
|
71
|
+
)
|
|
72
|
+
else
|
|
73
|
+
condition = sanitize_sql_for_conditions(
|
|
74
|
+
["#{hits_count_col} #{operator} ?", value_to_sql(operator, value)]
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
joins: "LEFT JOIN #{facet_table} ON #{facet_table}.host_id = #{Host::Managed.table_name}.id",
|
|
80
|
+
conditions: condition,
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
49
84
|
def search_by_insights_uuid(_key, operator, value)
|
|
50
85
|
# Determine which facet table to search based on IoP mode
|
|
51
86
|
facet_table = ForemanRhCloud.with_iop_smart_proxy? ? Katello::Host::SubscriptionFacet.table_name : InsightsFacet.table_name
|
data/config/routes.rb
CHANGED
|
@@ -33,6 +33,7 @@ Rails.application.routes.draw do
|
|
|
33
33
|
namespace :foreman_rh_cloud do
|
|
34
34
|
get 'inventory_upload', to: 'foreman_rh_cloud#inventory_upload'
|
|
35
35
|
get 'recommendations', to: 'foreman_rh_cloud#recommendations'
|
|
36
|
+
get 'recommendations/pathways/:slug', to: 'foreman_rh_cloud#recommendations'
|
|
36
37
|
get 'recommendations/:rule_id', to: 'foreman_rh_cloud#recommendations'
|
|
37
38
|
get 'insights_cloud', to: '/react#index' # Uses foreman's react controller
|
|
38
39
|
get 'insights_vulnerability', to: '/react#index'
|
|
@@ -59,11 +59,13 @@ module ForemanInventoryUpload
|
|
|
59
59
|
def try_execute
|
|
60
60
|
if content_disconnected?
|
|
61
61
|
logger.info("Upload canceled: connection to Insights is not enabled. Report location: #{filename}")
|
|
62
|
+
done!
|
|
62
63
|
return
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
unless organization.owner_details&.dig('upstreamConsumer', 'idCert')
|
|
66
67
|
logger.info("Skipping organization '#{organization}', no candlepin certificate defined.")
|
|
68
|
+
done!
|
|
67
69
|
return
|
|
68
70
|
end
|
|
69
71
|
|
data/package.json
CHANGED
|
@@ -401,4 +401,33 @@ class UploadReportDirectJobTest < ActiveSupport::TestCase
|
|
|
401
401
|
file.close
|
|
402
402
|
end
|
|
403
403
|
end
|
|
404
|
+
|
|
405
|
+
test 'mark as done when upload aborted due to missing certificate' do
|
|
406
|
+
# Remove certificate from organization
|
|
407
|
+
Organization.any_instance.stubs(:owner_details).returns({})
|
|
408
|
+
|
|
409
|
+
action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
|
|
410
|
+
action.expects(:action_subject).with(@organization)
|
|
411
|
+
plan_action(action, nil, @organization.id)
|
|
412
|
+
|
|
413
|
+
# Execute the action
|
|
414
|
+
action.send(:try_execute)
|
|
415
|
+
|
|
416
|
+
# Verify it is marked as done
|
|
417
|
+
assert action.done?
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
test 'mark as done when upload aborted due to disconnected mode' do
|
|
421
|
+
Setting.stubs(:[]).with(:subscription_connection_enabled).returns(false)
|
|
422
|
+
|
|
423
|
+
action = create_action(ForemanInventoryUpload::Async::UploadReportDirectJob)
|
|
424
|
+
action.expects(:action_subject).with(@organization)
|
|
425
|
+
plan_action(action, nil, @organization.id)
|
|
426
|
+
|
|
427
|
+
# Execute the action
|
|
428
|
+
action.send(:try_execute)
|
|
429
|
+
|
|
430
|
+
# Verify it is marked as done
|
|
431
|
+
assert action.done?
|
|
432
|
+
end
|
|
404
433
|
end
|
|
@@ -6,6 +6,10 @@ class InsightsFacetTest < ActiveSupport::TestCase
|
|
|
6
6
|
InsightsFacet.reset_counters(@host.insights.id, :hits_count)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
teardown do
|
|
10
|
+
ForemanRhCloud.unstub(:with_iop_smart_proxy?)
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
test 'host with hits can be deleted' do
|
|
10
14
|
assert_equal 1, @host.insights.hits.count
|
|
11
15
|
|
|
@@ -16,6 +20,7 @@ class InsightsFacetTest < ActiveSupport::TestCase
|
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
test 'search host by recommendations_count' do
|
|
23
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
19
24
|
FactoryBot.create(:host) # create another host with no recommendations
|
|
20
25
|
|
|
21
26
|
assert_equal 1, Host.search_for('insights_recommendations_count = 1').count
|
|
@@ -343,6 +343,74 @@ class RhCloudHostTest < ActiveSupport::TestCase
|
|
|
343
343
|
end
|
|
344
344
|
end
|
|
345
345
|
|
|
346
|
+
context 'scoped search on insights_recommendations_count' do
|
|
347
|
+
setup do
|
|
348
|
+
@org = FactoryBot.create(:organization)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
teardown do
|
|
352
|
+
ForemanRhCloud.unstub(:with_iop_smart_proxy?)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
test 'searches insights_facets.hits_count in non-IoP mode' do
|
|
356
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
357
|
+
host_with_hits = FactoryBot.create(:host, :with_insights_hits, organization: @org)
|
|
358
|
+
InsightsFacet.reset_counters(host_with_hits.insights.id, :hits_count)
|
|
359
|
+
host_without_hits = FactoryBot.create(:host, :managed, organization: @org)
|
|
360
|
+
|
|
361
|
+
results = Host::Managed.search_for('insights_recommendations_count = 1')
|
|
362
|
+
|
|
363
|
+
assert_includes results, host_with_hits
|
|
364
|
+
assert_not_includes results, host_without_hits
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
test 'supports greater-than operator in non-IoP mode' do
|
|
368
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
369
|
+
host_with_hits = FactoryBot.create(:host, :with_insights_hits, organization: @org)
|
|
370
|
+
InsightsFacet.reset_counters(host_with_hits.insights.id, :hits_count)
|
|
371
|
+
host_without_hits = FactoryBot.create(:host, :managed, organization: @org)
|
|
372
|
+
|
|
373
|
+
results = Host::Managed.search_for('insights_recommendations_count > 0')
|
|
374
|
+
|
|
375
|
+
assert_includes results, host_with_hits
|
|
376
|
+
assert_not_includes results, host_without_hits
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
test 'raises QueryNotSupported in IoP mode' do
|
|
380
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(true)
|
|
381
|
+
|
|
382
|
+
error = assert_raises(ScopedSearch::QueryNotSupported) do
|
|
383
|
+
Host::Managed.search_for('insights_recommendations_count = 1')
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
assert_match(/not available in IoP mode/, error.message)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
test 'supports IN operator in non-IoP mode' do
|
|
390
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
391
|
+
host_with_hits = FactoryBot.create(:host, :with_insights_hits, organization: @org)
|
|
392
|
+
InsightsFacet.reset_counters(host_with_hits.insights.id, :hits_count)
|
|
393
|
+
host_without_hits = FactoryBot.create(:host, :managed, organization: @org)
|
|
394
|
+
|
|
395
|
+
results = Host::Managed.search_for('insights_recommendations_count ^ (1,2,3)')
|
|
396
|
+
|
|
397
|
+
assert_includes results, host_with_hits
|
|
398
|
+
assert_not_includes results, host_without_hits
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
test 'handles hosts without insights facets in non-IoP mode' do
|
|
402
|
+
ForemanRhCloud.stubs(:with_iop_smart_proxy?).returns(false)
|
|
403
|
+
host_without_facet = FactoryBot.create(:host, :managed, organization: @org)
|
|
404
|
+
host_with_facet = FactoryBot.create(:host, :with_insights_hits, organization: @org)
|
|
405
|
+
InsightsFacet.reset_counters(host_with_facet.insights.id, :hits_count)
|
|
406
|
+
|
|
407
|
+
results = Host::Managed.search_for('insights_recommendations_count = 1')
|
|
408
|
+
|
|
409
|
+
assert_includes results, host_with_facet
|
|
410
|
+
assert_not_includes results, host_without_facet
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
346
414
|
test 'scoped search for user_omitted inventory status works' do
|
|
347
415
|
host1 = FactoryBot.create(:host, :managed)
|
|
348
416
|
host2 = FactoryBot.create(:host, :managed)
|
|
@@ -8,6 +8,7 @@ import EmptyState from './Components/EmptyState';
|
|
|
8
8
|
import ErrorState from './Components/ErrorState';
|
|
9
9
|
import EmptyResults from './Components/EmptyResults';
|
|
10
10
|
import { filterAccounts } from './AccountListHelper';
|
|
11
|
+
import { ACCOUNT_STATUS_POLLING_INTERVAL_MS } from './AccountListConstants';
|
|
11
12
|
import './accountList.scss';
|
|
12
13
|
|
|
13
14
|
const AccountList = ({
|
|
@@ -21,7 +22,10 @@ const AccountList = ({
|
|
|
21
22
|
}) => {
|
|
22
23
|
useEffect(() => {
|
|
23
24
|
fetchAccountsStatus();
|
|
24
|
-
const pollingID = setInterval(
|
|
25
|
+
const pollingID = setInterval(
|
|
26
|
+
fetchAccountsStatus,
|
|
27
|
+
ACCOUNT_STATUS_POLLING_INTERVAL_MS
|
|
28
|
+
);
|
|
25
29
|
startAccountStatusPolling(pollingID);
|
|
26
30
|
|
|
27
31
|
return () => {
|
|
@@ -7,3 +7,8 @@ export const INVENTORY_ACCOUNT_STATUS_POLLING_START =
|
|
|
7
7
|
export const INVENTORY_ACCOUNT_STATUS_POLLING_STOP =
|
|
8
8
|
'INVENTORY_ACCOUNT_STATUS_POLLING_STOP';
|
|
9
9
|
export const INVENTORY_PROCESS_RESTART = 'INVENTORY_PROCESS_RESTART';
|
|
10
|
+
|
|
11
|
+
// How often to re-fetch account statuses while generation/upload is in
|
|
12
|
+
// progress. Frequent enough to reflect status changes promptly, without
|
|
13
|
+
// hammering the status endpoint.
|
|
14
|
+
export const ACCOUNT_STATUS_POLLING_INTERVAL_MS = 2000;
|
data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/EmptyResults.js
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
EmptyState,
|
|
4
|
+
EmptyStateHeader,
|
|
5
|
+
EmptyStateIcon,
|
|
6
|
+
EmptyStateVariant,
|
|
7
|
+
} from '@patternfly/react-core';
|
|
8
|
+
import { OutlinedMehIcon } from '@patternfly/react-icons';
|
|
3
9
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
10
|
import './emptyResults.scss';
|
|
5
11
|
|
|
6
12
|
const inventoryEmptyResults = () => (
|
|
7
|
-
<EmptyState>
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
{
|
|
11
|
-
|
|
13
|
+
<EmptyState variant={EmptyStateVariant.lg}>
|
|
14
|
+
<EmptyStateIcon icon={OutlinedMehIcon} />
|
|
15
|
+
<EmptyStateHeader
|
|
16
|
+
titleText={
|
|
17
|
+
<>{__("Oops! Couldn't find organization that matches your query")}</>
|
|
18
|
+
}
|
|
19
|
+
headingLevel="h2"
|
|
20
|
+
/>
|
|
12
21
|
</EmptyState>
|
|
13
22
|
);
|
|
14
23
|
|
data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/EmptyState.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
EmptyState,
|
|
4
|
+
EmptyStateHeader,
|
|
5
|
+
EmptyStateIcon,
|
|
6
|
+
EmptyStateBody,
|
|
7
|
+
EmptyStateVariant,
|
|
8
|
+
Spinner,
|
|
9
|
+
} from '@patternfly/react-core';
|
|
3
10
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
11
|
import './emptyState.scss';
|
|
5
12
|
|
|
6
13
|
const inventoryEmptyState = () => (
|
|
7
|
-
<EmptyState>
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
{__('Fetching data about your accounts')}
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
<EmptyState variant={EmptyStateVariant.lg}>
|
|
15
|
+
<EmptyStateIcon icon={Spinner} />
|
|
16
|
+
<EmptyStateHeader
|
|
17
|
+
titleText={<>{__('Fetching data about your accounts')}</>}
|
|
18
|
+
headingLevel="h2"
|
|
19
|
+
/>
|
|
20
|
+
<EmptyStateBody>{__('Loading...')}</EmptyStateBody>
|
|
13
21
|
</EmptyState>
|
|
14
22
|
);
|
|
15
23
|
|
|
@@ -5,9 +5,7 @@ import EmptyState from '../EmptyState';
|
|
|
5
5
|
describe('EmptyState', () => {
|
|
6
6
|
it('renders fetching data message', () => {
|
|
7
7
|
render(<EmptyState />);
|
|
8
|
-
expect(
|
|
9
|
-
screen.getByText('Fetching data about your accounts')
|
|
10
|
-
).toBeTruthy();
|
|
8
|
+
expect(screen.getByText('Fetching data about your accounts')).toBeTruthy();
|
|
11
9
|
});
|
|
12
10
|
|
|
13
11
|
it('renders loading indicator', () => {
|
data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/ErrorState.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
3
|
+
import { TimesIcon } from '@patternfly/react-icons';
|
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
5
5
|
import './errorState.scss';
|
|
6
6
|
|
|
7
7
|
const ErrorState = ({ error }) => (
|
|
8
8
|
<div className="error_state">
|
|
9
|
-
<
|
|
9
|
+
<TimesIcon className="error_icon" size="xl" />
|
|
10
10
|
<p>{__('Encountered an error while trying to access the server:')}</p>
|
|
11
11
|
<p className="error_description">{error}</p>
|
|
12
12
|
</div>
|
|
@@ -3,9 +3,7 @@ import { render, screen } from '@testing-library/react';
|
|
|
3
3
|
import AccountList from '../AccountList';
|
|
4
4
|
import { accounts } from '../AccountList.fixtures';
|
|
5
5
|
|
|
6
|
-
jest.mock('../Components/ListItem', () => ({ label }) =>
|
|
7
|
-
<div>{label}</div>
|
|
8
|
-
));
|
|
6
|
+
jest.mock('../Components/ListItem', () => ({ label }) => <div>{label}</div>);
|
|
9
7
|
|
|
10
8
|
describe('AccountList', () => {
|
|
11
9
|
it('renders account labels', () => {
|
|
@@ -26,9 +24,7 @@ describe('AccountList', () => {
|
|
|
26
24
|
|
|
27
25
|
it('shows empty state when accounts are empty', () => {
|
|
28
26
|
render(<AccountList accounts={{}} />);
|
|
29
|
-
expect(
|
|
30
|
-
screen.getByText('Fetching data about your accounts')
|
|
31
|
-
).toBeTruthy();
|
|
27
|
+
expect(screen.getByText('Fetching data about your accounts')).toBeTruthy();
|
|
32
28
|
});
|
|
33
29
|
|
|
34
30
|
it('shows error state when error is present', () => {
|
data/webpack/ForemanInventoryUpload/Components/InventoryFilter/Components/ClearButton/ClearButton.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { noop } from 'foremanReact/common/helpers';
|
|
4
|
-
import {
|
|
4
|
+
import { Tooltip, Button } from '@patternfly/react-core';
|
|
5
|
+
import { TimesIcon } from '@patternfly/react-icons';
|
|
5
6
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
6
7
|
|
|
7
8
|
const ClearButton = ({ onClear }) => (
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
<Tooltip content={__('Clear')} position="top">
|
|
10
|
+
<Button
|
|
11
|
+
variant="plain"
|
|
12
|
+
className="inventory-clear-button"
|
|
13
|
+
onClick={onClear}
|
|
14
|
+
aria-label={__('Clear')}
|
|
15
|
+
ouiaId="inventory-clear-button"
|
|
16
|
+
>
|
|
17
|
+
<TimesIcon />
|
|
18
|
+
</Button>
|
|
19
|
+
</Tooltip>
|
|
17
20
|
);
|
|
18
21
|
|
|
19
22
|
ClearButton.propTypes = {
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
2
|
import React, { useEffect } from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
FormGroup,
|
|
6
|
+
TextInput,
|
|
7
|
+
InputGroup,
|
|
8
|
+
InputGroupItem,
|
|
9
|
+
Button,
|
|
10
|
+
} from '@patternfly/react-core';
|
|
11
|
+
import { TimesIcon } from '@patternfly/react-icons';
|
|
5
12
|
import { noop } from 'foremanReact/common/helpers';
|
|
6
13
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
7
14
|
import { useForemanOrganization } from 'foremanReact/Root/Context/ForemanContext';
|
|
8
|
-
import ClearButton from './Components/ClearButton';
|
|
9
15
|
import './inventoryFilter.scss';
|
|
10
16
|
import { ANY_ORGANIZATION } from './InventoryFilterConstants';
|
|
11
17
|
|
|
@@ -24,15 +30,28 @@ const InventoryFilter = ({
|
|
|
24
30
|
return (
|
|
25
31
|
<form id="inventory_filter_form">
|
|
26
32
|
<FormGroup>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
<InputGroup>
|
|
34
|
+
<InputGroupItem isFill>
|
|
35
|
+
<TextInput
|
|
36
|
+
id="inventory_filter_input"
|
|
37
|
+
ouiaId="inventory_filter_input"
|
|
38
|
+
value={filterTerm}
|
|
39
|
+
type="text"
|
|
40
|
+
placeholder={__('Filter..')}
|
|
41
|
+
onChange={(e, v) => handleFilterChange(v)}
|
|
42
|
+
/>
|
|
43
|
+
</InputGroupItem>
|
|
44
|
+
<InputGroupItem>
|
|
45
|
+
<Button
|
|
46
|
+
ouiaId="inventory-filter-clear-button"
|
|
47
|
+
variant="plain"
|
|
48
|
+
aria-label={__('Clear')}
|
|
49
|
+
onClick={handleFilterClear}
|
|
50
|
+
>
|
|
51
|
+
<TimesIcon />
|
|
52
|
+
</Button>
|
|
53
|
+
</InputGroupItem>
|
|
54
|
+
</InputGroup>
|
|
36
55
|
</FormGroup>
|
|
37
56
|
</form>
|
|
38
57
|
);
|
data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/integration.test.js
CHANGED
|
@@ -31,7 +31,9 @@ describe('InventoryFilter integration test', () => {
|
|
|
31
31
|
|
|
32
32
|
const actions = store.getActions();
|
|
33
33
|
const filterAction = actions.find(
|
|
34
|
-
a =>
|
|
34
|
+
a =>
|
|
35
|
+
a.type === INVENTORY_FILTER_UPDATE &&
|
|
36
|
+
a.payload.filterTerm === 'some_new_filter'
|
|
35
37
|
);
|
|
36
38
|
expect(filterAction).toBeTruthy();
|
|
37
39
|
});
|
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
#inventory_filter_form {
|
|
2
|
-
.pf-v5-c-form__group
|
|
3
|
-
display: -ms-flexbox;
|
|
4
|
-
display: -webkit-flex;
|
|
5
|
-
display: flex;
|
|
6
|
-
-webkit-flex-direction: row;
|
|
7
|
-
-ms-flex-direction: row;
|
|
8
|
-
flex-direction: row;
|
|
9
|
-
-webkit-flex-wrap: nowrap;
|
|
10
|
-
-ms-flex-wrap: nowrap;
|
|
11
|
-
flex-wrap: nowrap;
|
|
12
|
-
-webkit-justify-content: flex-end;
|
|
13
|
-
-ms-flex-pack: end;
|
|
14
|
-
justify-content: flex-end;
|
|
15
|
-
-webkit-align-content: center;
|
|
16
|
-
-ms-flex-line-pack: center;
|
|
17
|
-
align-content: center;
|
|
18
|
-
-webkit-align-items: center;
|
|
19
|
-
-ms-flex-align: center;
|
|
20
|
-
align-items: center;
|
|
2
|
+
.pf-v5-c-form__group {
|
|
21
3
|
margin-bottom: 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.pf-v5-c-input-group {
|
|
7
|
+
width: 100%;
|
|
8
|
+
position: relative;
|
|
9
|
+
|
|
10
|
+
.pf-v5-c-input-group__item {
|
|
11
|
+
&:last-child {
|
|
12
|
+
position: absolute;
|
|
13
|
+
right: 0;
|
|
14
|
+
top: 0;
|
|
15
|
+
bottom: 0;
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
pointer-events: auto;
|
|
19
|
+
|
|
20
|
+
.pf-v5-c-button {
|
|
21
|
+
margin-right: 8px;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&:first-child {
|
|
26
|
+
flex: 1;
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
.pf-v5-c-form-control {
|
|
29
|
+
padding-right: 40px;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
26
32
|
}
|
|
27
33
|
}
|
|
28
34
|
}
|
|
@@ -52,12 +52,12 @@ const InventorySettings = () => {
|
|
|
52
52
|
})}
|
|
53
53
|
<MinimalInventoryDropdown setChosenValue={setChosenValue} />
|
|
54
54
|
{Object.keys(childSettingsDict).length > 0 && ( // Only render this section if there are child settings
|
|
55
|
-
<div
|
|
55
|
+
<div className="child-settings">
|
|
56
56
|
<Flex>
|
|
57
|
-
<FlexItem>
|
|
58
|
-
<span
|
|
57
|
+
<FlexItem flex={{ default: 'flexNone' }}>
|
|
58
|
+
<span className="child-settings-spacer" />
|
|
59
59
|
</FlexItem>
|
|
60
|
-
<FlexItem>
|
|
60
|
+
<FlexItem flex={{ default: 'flex_1' }}>
|
|
61
61
|
{Object.keys(childSettingsDict).map(key => (
|
|
62
62
|
<AdvancedSetting
|
|
63
63
|
key={key}
|
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
.rh-cloud-inventory-page {
|
|
2
2
|
.inventory-settings {
|
|
3
|
-
border: 1px solid
|
|
4
|
-
border-radius:
|
|
3
|
+
border: 1px solid var(--pf-v5-global--BorderColor--100);
|
|
4
|
+
border-radius: var(--pf-v5-global--BorderRadius--sm);
|
|
5
5
|
padding: 10px;
|
|
6
|
-
margin-bottom:
|
|
6
|
+
margin-bottom: var(--pf-v5-global--spacer--md);
|
|
7
|
+
margin-left: 20px;
|
|
7
8
|
float: right;
|
|
9
|
+
max-width: 450px;
|
|
8
10
|
|
|
9
11
|
h3 {
|
|
10
12
|
margin-top: 0;
|
|
13
|
+
margin-bottom: 10px;
|
|
11
14
|
font-weight: 600;
|
|
15
|
+
font-size: var(--pf-v5-global--FontSize--md);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.child-settings {
|
|
19
|
+
margin-top: 10px;
|
|
20
|
+
margin-left: 0;
|
|
21
|
+
|
|
22
|
+
.child-settings-spacer {
|
|
23
|
+
display: none;
|
|
24
|
+
}
|
|
12
25
|
}
|
|
13
26
|
}
|
|
14
27
|
}
|
data/webpack/ForemanInventoryUpload/Components/InventorySettings/MinimalInventoryDropdown.js
CHANGED
|
@@ -72,6 +72,7 @@ const MinimalInventoryDropdown = ({ setChosenValue }) => {
|
|
|
72
72
|
isOpen={isOpen}
|
|
73
73
|
onSelect={onSelect}
|
|
74
74
|
onOpenChange={val => setIsOpen(val)}
|
|
75
|
+
popperProps={{ width: '350px' }}
|
|
75
76
|
toggle={toggleRef => (
|
|
76
77
|
<MenuToggle
|
|
77
78
|
ref={toggleRef}
|
|
@@ -84,20 +85,18 @@ const MinimalInventoryDropdown = ({ setChosenValue }) => {
|
|
|
84
85
|
)}
|
|
85
86
|
shouldFocusToggleOnSelect
|
|
86
87
|
>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
</DropdownList>
|
|
100
|
-
</div>
|
|
88
|
+
<DropdownList>
|
|
89
|
+
{Object.entries(dropdownValues).map(([value, item]) => (
|
|
90
|
+
<DropdownItem
|
|
91
|
+
value={value}
|
|
92
|
+
key={value}
|
|
93
|
+
description={item.description}
|
|
94
|
+
ouiaId={`inventory-dropdownItem-${value}`}
|
|
95
|
+
>
|
|
96
|
+
{item.title}
|
|
97
|
+
</DropdownItem>
|
|
98
|
+
))}
|
|
99
|
+
</DropdownList>
|
|
101
100
|
</Dropdown>
|
|
102
101
|
);
|
|
103
102
|
};
|