foreman_scc_manager 1.8.20 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -11
- data/app/controllers/api/v2/scc_accounts_controller.rb +55 -4
- data/app/controllers/api/v2/scc_products_controller.rb +1 -1
- data/app/controllers/scc_accounts_controller.rb +49 -0
- data/app/lib/actions/scc_manager/subscribe_product.rb +56 -22
- data/app/models/scc_account.rb +22 -1
- data/app/models/scc_product.rb +2 -5
- data/app/views/api/v2/scc_accounts/main.json.rabl +1 -1
- data/app/views/scc_accounts/_form.html.erb +8 -4
- data/app/views/scc_accounts/show.html.erb +7 -51
- data/config/routes.rb +1 -0
- data/db/migrate/20220429102717_populate_scc_katello_repositories.rb +5 -0
- data/db/migrate/20220531120722_add_product_category_to_scc_products.rb +5 -0
- data/db/migrate/20221013214310_add_sync_options_to_scc_account.rb +11 -0
- data/lib/foreman_scc_manager/engine.rb +1 -1
- data/lib/foreman_scc_manager/version.rb +1 -1
- data/lib/tasks/republish_repositories.rake +13 -0
- data/package.json +54 -0
- data/test/controllers/scc_accounts_controller_test.rb +7 -1
- data/test/fixtures/models/katello_root_repositories.yml +0 -12
- data/test/test_plugin_helper.rb +0 -6
- data/webpack/components/SCCProductPage/EmptySccProducts.js +46 -0
- data/webpack/components/SCCProductPage/SCCProductPage.js +96 -0
- data/webpack/components/SCCProductPage/SCCProductPageActions.js +27 -0
- data/webpack/components/SCCProductPage/SCCProductPageConstants.js +5 -0
- data/webpack/components/SCCProductPage/SCCProductPageReducer.js +34 -0
- data/webpack/components/SCCProductPage/SCCProductPageSelectors.js +7 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCGenericPicker/index.js +80 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/components/SCCRepoPicker/index.js +174 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/components/SCCRepoPicker/styles.scss +3 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/index.js +303 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/index.js +235 -0
- data/webpack/components/SCCProductPage/components/SCCProductPicker/styles.scss +10 -0
- data/webpack/components/SCCProductPage/components/SCCProductPickerModal/index.js +81 -0
- data/webpack/components/SCCProductPage/components/SCCProductView/components/SCCRepoView/index.js +113 -0
- data/webpack/components/SCCProductPage/components/SCCProductView/components/SCCRepoView/styles.scss +14 -0
- data/webpack/components/SCCProductPage/components/SCCProductView/index.js +236 -0
- data/webpack/components/SCCProductPage/components/common/SCCGenericExpander/index.js +58 -0
- data/webpack/components/SCCProductPage/components/common/SCCProductTreeExpander/index.js +21 -0
- data/webpack/components/SCCProductPage/components/common/SCCSubscribedProductsExpander/index.js +21 -0
- data/webpack/components/SCCProductPage/index.js +18 -0
- data/webpack/components/SCCProductPage/sccProductPage.scss +8 -0
- data/webpack/index.js +11 -0
- data/webpack/reducer.js +7 -0
- metadata +43 -15
@@ -2,6 +2,12 @@ require 'test_plugin_helper'
|
|
2
2
|
|
3
3
|
class SccAccountsControllerTest < ActionController::TestCase
|
4
4
|
def setup
|
5
|
+
# rubocop: disable Lint/SuppressedException
|
6
|
+
begin
|
7
|
+
::Katello::ContentCredential
|
8
|
+
rescue NameError
|
9
|
+
end
|
10
|
+
# rubocop: enable Lint/SuppressedException
|
5
11
|
@scc_account = scc_accounts(:one)
|
6
12
|
end
|
7
13
|
|
@@ -13,7 +19,7 @@ class SccAccountsControllerTest < ActionController::TestCase
|
|
13
19
|
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
14
20
|
'Authorization' => auth,
|
15
21
|
'Host' => host,
|
16
|
-
'User-Agent' => "rest-client/2.1.0 (linux
|
22
|
+
'User-Agent' => "rest-client/2.1.0 (linux x86_64) ruby/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}"
|
17
23
|
}
|
18
24
|
)
|
19
25
|
.to_return(status: 200, body: '', headers: {})
|
@@ -5,11 +5,7 @@ sles12_sp4_updates:
|
|
5
5
|
product_id: <%= ActiveRecord::FixtureSet.identify(:suse) %>
|
6
6
|
url: www.not-valid.com
|
7
7
|
download_policy: immediate
|
8
|
-
<% if ActiveRecord::Base.connection.column_exists? :katello_root_repositories, :mirroring_policy %>
|
9
8
|
mirroring_policy: "mirror_content_only"
|
10
|
-
<% else %>
|
11
|
-
mirror_on_sync: "true"
|
12
|
-
<% end %>
|
13
9
|
created_at: <%= Time.now %>
|
14
10
|
updated_at: <%= Time.now %>
|
15
11
|
|
@@ -20,11 +16,7 @@ sles12_sp4_installer_updates:
|
|
20
16
|
product_id: <%= ActiveRecord::FixtureSet.identify(:suse) %>
|
21
17
|
url: www.not-valid.com
|
22
18
|
download_policy: on_demand
|
23
|
-
<% if ActiveRecord::Base.connection.column_exists? :katello_root_repositories, :mirroring_policy %>
|
24
19
|
mirroring_policy: "mirror_content_only"
|
25
|
-
<% else %>
|
26
|
-
mirror_on_sync: "true"
|
27
|
-
<% end %>
|
28
20
|
created_at: <%= Time.now %>
|
29
21
|
updated_at: <%= Time.now %>
|
30
22
|
|
@@ -35,10 +27,6 @@ sles12_sp4_installer_updates_2:
|
|
35
27
|
product_id: <%= ActiveRecord::FixtureSet.identify(:suse) %>
|
36
28
|
url: www.not-valid.com
|
37
29
|
download_policy: on_demand
|
38
|
-
<% if ActiveRecord::Base.connection.column_exists? :katello_root_repositories, :mirroring_policy %>
|
39
30
|
mirroring_policy: "mirror_content_only"
|
40
|
-
<% else %>
|
41
|
-
mirror_on_sync: "true"
|
42
|
-
<% end %>
|
43
31
|
created_at: <%= Time.now %>
|
44
32
|
updated_at: <%= Time.now %>
|
data/test/test_plugin_helper.rb
CHANGED
@@ -29,8 +29,6 @@ module FixtureTestCase
|
|
29
29
|
fixtures(:all)
|
30
30
|
FIXTURES = load_fixtures(ActiveRecord::Base)
|
31
31
|
|
32
|
-
Setting::Content.load_defaults
|
33
|
-
|
34
32
|
User.current = ::User.unscoped.find(FIXTURES['users']['admin']['id'])
|
35
33
|
end
|
36
34
|
end
|
@@ -41,10 +39,6 @@ class ActiveSupport::TestCase
|
|
41
39
|
include FixtureTestCase
|
42
40
|
include ForemanTasks::TestHelpers::WithInThreadExecutor
|
43
41
|
|
44
|
-
before do
|
45
|
-
Setting::Content.load_defaults
|
46
|
-
end
|
47
|
-
|
48
42
|
def get_organization(org = nil)
|
49
43
|
saved_user = User.current
|
50
44
|
User.current = User.unscoped.find(users(:admin).id)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useDispatch } from 'react-redux';
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
import { Button } from '@patternfly/react-core';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import EmptyState from 'foremanReact/components/common/EmptyState';
|
7
|
+
import { syncSccAccountAction } from './SCCProductPageActions';
|
8
|
+
|
9
|
+
export const EmptySccProducts = ({ canCreate, sccAccountId }) => {
|
10
|
+
const dispatch = useDispatch();
|
11
|
+
const onSyncStart = (evt) => {
|
12
|
+
dispatch(syncSccAccountAction(sccAccountId));
|
13
|
+
};
|
14
|
+
|
15
|
+
const content = __(
|
16
|
+
`Please synchronize your SUSE account before you can subscribe to SUSE products.`
|
17
|
+
);
|
18
|
+
return (
|
19
|
+
<>
|
20
|
+
<EmptyState
|
21
|
+
icon="th"
|
22
|
+
iconType="fa"
|
23
|
+
header={__('SUSE Customer Center')}
|
24
|
+
description={<div dangerouslySetInnerHTML={{ __html: content }} />}
|
25
|
+
documentation={{
|
26
|
+
url: 'https://docs.orcharhino.com/or/docs/sources/usage_guides/managing_sles_systems_guide.html#mssg_adding_scc_accounts',
|
27
|
+
}}
|
28
|
+
/>
|
29
|
+
<Button onClick={onSyncStart} className="btn btn-primary">
|
30
|
+
{__('Synchronize SUSE Account')}
|
31
|
+
</Button>
|
32
|
+
</>
|
33
|
+
);
|
34
|
+
};
|
35
|
+
|
36
|
+
EmptySccProducts.propTypes = {
|
37
|
+
canCreate: PropTypes.bool,
|
38
|
+
sccAccountId: PropTypes.number,
|
39
|
+
};
|
40
|
+
|
41
|
+
EmptySccProducts.defaultProps = {
|
42
|
+
canCreate: false,
|
43
|
+
sccAccountId: undefined,
|
44
|
+
};
|
45
|
+
|
46
|
+
export default EmptySccProducts;
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import PropTypes from 'prop-types';
|
2
|
+
import React, { useState } from 'react';
|
3
|
+
import { Stack, StackItem } from '@patternfly/react-core';
|
4
|
+
import { useDispatch } from 'react-redux';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
|
7
|
+
import SCCProductView from './components/SCCProductView';
|
8
|
+
import EmptySccProducts from './EmptySccProducts';
|
9
|
+
import SCCProductPicker from './components/SCCProductPicker';
|
10
|
+
import SCCProductPickerModal from './components/SCCProductPickerModal';
|
11
|
+
import { SCCPRODUCTPAGE_SUMMARY_MODAL_ID } from './SCCProductPageConstants';
|
12
|
+
import './sccProductPage.scss';
|
13
|
+
|
14
|
+
const SCCProductPage = ({
|
15
|
+
canCreate,
|
16
|
+
sccAccountId,
|
17
|
+
sccProductsInit,
|
18
|
+
...props
|
19
|
+
}) => {
|
20
|
+
const dispatch = useDispatch();
|
21
|
+
const [productToEdit, setProductToEdit] = useState(0);
|
22
|
+
const [reposToSubscribe, setReposToSubscribe] = useState([]);
|
23
|
+
const [subscriptionTaskId, setSubscriptionTaskId] = useState();
|
24
|
+
|
25
|
+
const editProductTree = (productId) => {
|
26
|
+
setProductToEdit(productId);
|
27
|
+
};
|
28
|
+
|
29
|
+
const { setModalOpen, setModalClosed } = useForemanModal({
|
30
|
+
id: SCCPRODUCTPAGE_SUMMARY_MODAL_ID,
|
31
|
+
});
|
32
|
+
|
33
|
+
const handleSubscribeCallback = (
|
34
|
+
subscriptionTaskIdFromAction,
|
35
|
+
reposToSubscribeFromAction
|
36
|
+
) => {
|
37
|
+
setSubscriptionTaskId(subscriptionTaskIdFromAction);
|
38
|
+
const newReposToSubscribe = [];
|
39
|
+
Object.keys(reposToSubscribeFromAction).forEach((k) => {
|
40
|
+
const repo = {
|
41
|
+
productName: reposToSubscribeFromAction[k].productName,
|
42
|
+
repoNames: reposToSubscribeFromAction[k].repoNames,
|
43
|
+
};
|
44
|
+
newReposToSubscribe.push(repo);
|
45
|
+
});
|
46
|
+
setReposToSubscribe(newReposToSubscribe);
|
47
|
+
dispatch(setModalOpen({ id: SCCPRODUCTPAGE_SUMMARY_MODAL_ID }));
|
48
|
+
};
|
49
|
+
|
50
|
+
return sccProductsInit.length > 0 ? (
|
51
|
+
<Stack>
|
52
|
+
<StackItem>
|
53
|
+
<SCCProductPickerModal
|
54
|
+
id={SCCPRODUCTPAGE_SUMMARY_MODAL_ID}
|
55
|
+
title={__('The subscription task has been started successfully')}
|
56
|
+
taskId={subscriptionTaskId}
|
57
|
+
reposToSubscribe={reposToSubscribe}
|
58
|
+
/>
|
59
|
+
</StackItem>
|
60
|
+
<StackItem>
|
61
|
+
<SCCProductView
|
62
|
+
sccProducts={sccProductsInit.filter(
|
63
|
+
(prod) => prod.product_id !== null
|
64
|
+
)}
|
65
|
+
subscriptionTaskId={subscriptionTaskId}
|
66
|
+
editProductTreeGlobal={editProductTree}
|
67
|
+
/>
|
68
|
+
</StackItem>
|
69
|
+
<br />
|
70
|
+
<StackItem />
|
71
|
+
<StackItem>
|
72
|
+
<SCCProductPicker
|
73
|
+
sccProducts={sccProductsInit}
|
74
|
+
sccAccountId={sccAccountId}
|
75
|
+
editProductId={productToEdit}
|
76
|
+
handleSubscribeCallback={handleSubscribeCallback}
|
77
|
+
/>
|
78
|
+
</StackItem>
|
79
|
+
</Stack>
|
80
|
+
) : (
|
81
|
+
<EmptySccProducts sccAccountId={sccAccountId} canCreate={canCreate} />
|
82
|
+
);
|
83
|
+
};
|
84
|
+
|
85
|
+
SCCProductPage.propTypes = {
|
86
|
+
canCreate: PropTypes.bool,
|
87
|
+
sccAccountId: PropTypes.number.isRequired,
|
88
|
+
sccProductsInit: PropTypes.array,
|
89
|
+
};
|
90
|
+
|
91
|
+
SCCProductPage.defaultProps = {
|
92
|
+
canCreate: false,
|
93
|
+
sccProductsInit: [],
|
94
|
+
};
|
95
|
+
|
96
|
+
export default SCCProductPage;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { API_OPERATIONS, put } from 'foremanReact/redux/API';
|
2
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
3
|
+
|
4
|
+
export const subscribeProductsWithReposAction = (
|
5
|
+
sccAccountId,
|
6
|
+
sccProductData,
|
7
|
+
handleSubscription,
|
8
|
+
sccSubscriptionData
|
9
|
+
) =>
|
10
|
+
put({
|
11
|
+
type: API_OPERATIONS.PUT,
|
12
|
+
key: `subscribe_key_${sccAccountId}_${sccProductData[0].scc_product_id}_${sccProductData[0].repository_list}`,
|
13
|
+
url: `/api/scc_accounts/${sccAccountId}/bulk_subscribe_with_repos`,
|
14
|
+
params: { scc_product_data: sccProductData },
|
15
|
+
errorToast: (error) => __('Starting the subscription task failed.'),
|
16
|
+
handleSuccess: (response) =>
|
17
|
+
handleSubscription(response.data.id, sccSubscriptionData),
|
18
|
+
});
|
19
|
+
|
20
|
+
export const syncSccAccountAction = (sccAccountId) =>
|
21
|
+
put({
|
22
|
+
type: API_OPERATIONS.PUT,
|
23
|
+
key: `syncSccAccountKey_${sccAccountId}`,
|
24
|
+
url: `/api/scc_accounts/${sccAccountId}/sync`,
|
25
|
+
successToast: () => __('Sync task started.'),
|
26
|
+
errorToast: (error) => __('Failed to add task to queue.'),
|
27
|
+
});
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import Immutable from 'seamless-immutable';
|
2
|
+
import { actionTypeGenerator } from 'foremanReact/redux/API';
|
3
|
+
|
4
|
+
export const initialState = Immutable({
|
5
|
+
sccProducts: [],
|
6
|
+
sccAccountId: undefined,
|
7
|
+
});
|
8
|
+
|
9
|
+
export default (state = initialState, action) => {
|
10
|
+
const { payload } = action;
|
11
|
+
|
12
|
+
const listTypes = actionTypeGenerator('SCC_PRODUCT_LIST');
|
13
|
+
|
14
|
+
switch (action.type) {
|
15
|
+
case 'FETCH_PRODUCT_SUCCESS':
|
16
|
+
return state.merge({
|
17
|
+
sccProducts: payload,
|
18
|
+
});
|
19
|
+
case listTypes.REQUEST:
|
20
|
+
return state.merge({
|
21
|
+
sccProducts: [],
|
22
|
+
});
|
23
|
+
case listTypes.SUCCESS:
|
24
|
+
return state.merge({
|
25
|
+
sccProducts: payload.data,
|
26
|
+
});
|
27
|
+
case listTypes.FAILURE:
|
28
|
+
return state.merge({
|
29
|
+
sccProducts: [payload.error],
|
30
|
+
});
|
31
|
+
default:
|
32
|
+
return state;
|
33
|
+
}
|
34
|
+
};
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { ContextSelector, ContextSelectorItem } from '@patternfly/react-core';
|
4
|
+
|
5
|
+
const GenericSelector = ({
|
6
|
+
selectionItems,
|
7
|
+
setGlobalSelected,
|
8
|
+
screenReaderLabel,
|
9
|
+
initialLabel,
|
10
|
+
}) => {
|
11
|
+
const [isOpen, setIsOpen] = useState(false);
|
12
|
+
const [selected, setSelected] = useState(initialLabel);
|
13
|
+
const [searchValue, setSearchValue] = useState('');
|
14
|
+
const [filteredItems, setFilteredItems] = useState(selectionItems);
|
15
|
+
|
16
|
+
useEffect(() => {
|
17
|
+
setFilteredItems(selectionItems);
|
18
|
+
setSelected(initialLabel);
|
19
|
+
}, [selectionItems, initialLabel]);
|
20
|
+
|
21
|
+
const onToggle = (selectorOpen) => {
|
22
|
+
setIsOpen(selectorOpen);
|
23
|
+
};
|
24
|
+
|
25
|
+
const onSelect = (event, value) => {
|
26
|
+
setSelected(value);
|
27
|
+
setIsOpen(!isOpen);
|
28
|
+
setGlobalSelected(value);
|
29
|
+
};
|
30
|
+
|
31
|
+
const onSearchInputChange = (value) => {
|
32
|
+
setSearchValue(value);
|
33
|
+
};
|
34
|
+
|
35
|
+
const onSearchButtonClick = (event) => {
|
36
|
+
const filtered =
|
37
|
+
searchValue === ''
|
38
|
+
? selectionItems
|
39
|
+
: selectionItems.filter((item) => {
|
40
|
+
const str = item.text ? item.text : item;
|
41
|
+
return str.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1;
|
42
|
+
});
|
43
|
+
|
44
|
+
setFilteredItems(filtered || []);
|
45
|
+
};
|
46
|
+
|
47
|
+
return (
|
48
|
+
<ContextSelector
|
49
|
+
toggleText={selected}
|
50
|
+
onSearchInputChange={onSearchInputChange}
|
51
|
+
isOpen={isOpen}
|
52
|
+
searchInputValue={searchValue}
|
53
|
+
onToggle={onToggle}
|
54
|
+
onSelect={onSelect}
|
55
|
+
onSearchButtonClick={onSearchButtonClick}
|
56
|
+
screenReaderLabel={screenReaderLabel}
|
57
|
+
>
|
58
|
+
{filteredItems.map((item, index) => (
|
59
|
+
<ContextSelectorItem key={index}>
|
60
|
+
{item || 'no arch'}
|
61
|
+
</ContextSelectorItem>
|
62
|
+
))}
|
63
|
+
</ContextSelector>
|
64
|
+
);
|
65
|
+
};
|
66
|
+
|
67
|
+
GenericSelector.propTypes = {
|
68
|
+
selectionItems: PropTypes.array,
|
69
|
+
setGlobalSelected: PropTypes.func.isRequired,
|
70
|
+
screenReaderLabel: PropTypes.string,
|
71
|
+
initialLabel: PropTypes.string,
|
72
|
+
};
|
73
|
+
|
74
|
+
GenericSelector.defaultProps = {
|
75
|
+
selectionItems: [],
|
76
|
+
screenReaderLabel: '',
|
77
|
+
initialLabel: '',
|
78
|
+
};
|
79
|
+
|
80
|
+
export default GenericSelector;
|
@@ -0,0 +1,174 @@
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
|
5
|
+
|
6
|
+
import './styles.scss';
|
7
|
+
|
8
|
+
const createRepoSelectOption = (repo, disableRepos) => (
|
9
|
+
<SelectOption
|
10
|
+
key={repo.id}
|
11
|
+
isDisabled={repo.katello_root_repository_id !== null || disableRepos}
|
12
|
+
value={repo.name}
|
13
|
+
/>
|
14
|
+
);
|
15
|
+
|
16
|
+
const setRepoSelection = (
|
17
|
+
sccRepos,
|
18
|
+
disableRepos,
|
19
|
+
activateDebugFilter,
|
20
|
+
productAlreadySynced
|
21
|
+
) => {
|
22
|
+
let res = [];
|
23
|
+
// this is necessary because the logic of this option was inverted
|
24
|
+
// Instead of filtering the debug repositories with the corresponding option,
|
25
|
+
// this option now includes the repositories, instead - means, it does exactly the opposite.
|
26
|
+
activateDebugFilter = !activateDebugFilter;
|
27
|
+
if (!disableRepos && !productAlreadySynced) {
|
28
|
+
if (activateDebugFilter) {
|
29
|
+
res = sccRepos.filter(
|
30
|
+
(repo) =>
|
31
|
+
(!repo.name.includes('Debug') &&
|
32
|
+
!repo.name.includes('Source-Pool') &&
|
33
|
+
repo.katello_root_repository_id === null) ||
|
34
|
+
repo.katello_root_repository_id !== null
|
35
|
+
);
|
36
|
+
} else {
|
37
|
+
res = sccRepos;
|
38
|
+
}
|
39
|
+
} else {
|
40
|
+
res = sccRepos.filter((repo) => repo.katello_root_repository_id !== null);
|
41
|
+
}
|
42
|
+
return res.map((repo) => repo.name);
|
43
|
+
};
|
44
|
+
|
45
|
+
// disableRepos makes sure that repos can only be selected if the corresponding product
|
46
|
+
// is also selected
|
47
|
+
const SCCRepoPicker = ({
|
48
|
+
sccRepos,
|
49
|
+
disableRepos,
|
50
|
+
activateDebugFilter,
|
51
|
+
productAlreadySynced,
|
52
|
+
sccProductId,
|
53
|
+
sccProductName,
|
54
|
+
setSelectedReposFromChild,
|
55
|
+
}) => {
|
56
|
+
const [isOpen, setIsOpen] = useState(false);
|
57
|
+
// set initial selected values to the already checked repos
|
58
|
+
const [selected, setSelected] = useState(
|
59
|
+
setRepoSelection(
|
60
|
+
sccRepos,
|
61
|
+
disableRepos,
|
62
|
+
activateDebugFilter,
|
63
|
+
productAlreadySynced
|
64
|
+
)
|
65
|
+
);
|
66
|
+
const onToggle = (toggle) => {
|
67
|
+
setIsOpen(toggle);
|
68
|
+
};
|
69
|
+
|
70
|
+
useEffect(() => {
|
71
|
+
const selectedRepos = setRepoSelection(
|
72
|
+
sccRepos,
|
73
|
+
disableRepos,
|
74
|
+
activateDebugFilter,
|
75
|
+
productAlreadySynced
|
76
|
+
);
|
77
|
+
setSelected(selectedRepos);
|
78
|
+
setSelectedReposFromChild(
|
79
|
+
sccProductId,
|
80
|
+
sccProductName,
|
81
|
+
sccRepos
|
82
|
+
// make sure that we do not request already subscribed repositories
|
83
|
+
.filter(
|
84
|
+
(repo) =>
|
85
|
+
selectedRepos.includes(repo.name) &&
|
86
|
+
repo.katello_root_repository_id === null
|
87
|
+
)
|
88
|
+
.map((repo) => repo.id),
|
89
|
+
sccRepos
|
90
|
+
// make sure that we do not request already subscribed repositories
|
91
|
+
.filter(
|
92
|
+
(repo) =>
|
93
|
+
selectedRepos.includes(repo.name) &&
|
94
|
+
repo.katello_root_repository_id === null
|
95
|
+
)
|
96
|
+
.map((repo) => repo.name)
|
97
|
+
);
|
98
|
+
}, [
|
99
|
+
sccRepos,
|
100
|
+
disableRepos,
|
101
|
+
activateDebugFilter,
|
102
|
+
productAlreadySynced,
|
103
|
+
sccProductId,
|
104
|
+
setSelectedReposFromChild,
|
105
|
+
]);
|
106
|
+
|
107
|
+
const onSelect = (event, selection) => {
|
108
|
+
let selectedRepos = [];
|
109
|
+
if (event.target.checked) {
|
110
|
+
selectedRepos = [...new Set(selected.concat([selection]))];
|
111
|
+
} else {
|
112
|
+
selectedRepos = selected.filter((item) => item !== selection);
|
113
|
+
}
|
114
|
+
setSelected(selectedRepos);
|
115
|
+
setSelectedReposFromChild(
|
116
|
+
sccProductId,
|
117
|
+
sccProductName,
|
118
|
+
sccRepos
|
119
|
+
// make sure that we do not request already subscribed repositories
|
120
|
+
.filter(
|
121
|
+
(repo) =>
|
122
|
+
selectedRepos.includes(repo.name) &&
|
123
|
+
repo.katello_root_repository_id === null
|
124
|
+
)
|
125
|
+
.map((repo) => repo.id),
|
126
|
+
sccRepos
|
127
|
+
// make sure that we do not request already subscribed repositories
|
128
|
+
.filter(
|
129
|
+
(repo) =>
|
130
|
+
selectedRepos.includes(repo.name) &&
|
131
|
+
repo.katello_root_repository_id === null
|
132
|
+
)
|
133
|
+
.map((repo) => repo.name)
|
134
|
+
);
|
135
|
+
};
|
136
|
+
|
137
|
+
const selectOptions = sccRepos.map((repo) =>
|
138
|
+
createRepoSelectOption(repo, disableRepos)
|
139
|
+
);
|
140
|
+
|
141
|
+
return (
|
142
|
+
<Select
|
143
|
+
variant={SelectVariant.checkbox}
|
144
|
+
isCheckboxSelectionBadgeHidden
|
145
|
+
onToggle={onToggle}
|
146
|
+
onSelect={onSelect}
|
147
|
+
selections={selected}
|
148
|
+
isOpen={isOpen}
|
149
|
+
isDisabled={disableRepos}
|
150
|
+
placeholderText={sprintf(__('%s/%s'), selected.length, sccRepos.length)}
|
151
|
+
>
|
152
|
+
{selectOptions}
|
153
|
+
</Select>
|
154
|
+
);
|
155
|
+
};
|
156
|
+
|
157
|
+
SCCRepoPicker.propTypes = {
|
158
|
+
sccRepos: PropTypes.array,
|
159
|
+
disableRepos: PropTypes.bool,
|
160
|
+
activateDebugFilter: PropTypes.bool,
|
161
|
+
productAlreadySynced: PropTypes.bool,
|
162
|
+
sccProductId: PropTypes.number.isRequired,
|
163
|
+
sccProductName: PropTypes.string.isRequired,
|
164
|
+
setSelectedReposFromChild: PropTypes.func.isRequired,
|
165
|
+
};
|
166
|
+
|
167
|
+
SCCRepoPicker.defaultProps = {
|
168
|
+
sccRepos: [],
|
169
|
+
disableRepos: false,
|
170
|
+
activateDebugFilter: false,
|
171
|
+
productAlreadySynced: false,
|
172
|
+
};
|
173
|
+
|
174
|
+
export default SCCRepoPicker;
|