katello 4.8.0.rc1 → 4.8.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of katello might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/controllers/katello/api/registry/registry_proxies_controller.rb +2 -18
- data/app/controllers/katello/api/v2/alternate_content_sources_controller.rb +7 -5
- data/app/controllers/katello/api/v2/repositories_controller.rb +3 -18
- data/app/helpers/katello/hosts_and_hostgroups_helper.rb +13 -8
- data/app/lib/actions/katello/alternate_content_source/create.rb +3 -1
- data/app/lib/actions/katello/alternate_content_source/update.rb +3 -1
- data/app/lib/actions/pulp3/orchestration/content_view_version/copy_version_units_to_library.rb +1 -1
- data/app/lib/actions/pulp3/orchestration/content_view_version/export.rb +11 -11
- data/app/lib/actions/pulp3/orchestration/content_view_version/syncable_export.rb +0 -2
- data/app/lib/actions/pulp3/repository/reclaim_space.rb +1 -1
- data/app/lib/katello/api/v2/error_handling.rb +12 -2
- data/app/lib/katello/concerns/base_template_scope_extensions.rb +7 -3
- data/app/models/katello/alternate_content_source.rb +54 -4
- data/app/models/katello/concerns/host_managed_extensions.rb +14 -0
- data/app/models/katello/glue/provider.rb +1 -1
- data/app/models/katello/host/content_facet.rb +2 -0
- data/app/services/katello/pulp3/content_view_version/export_validation_error.rb +1 -1
- data/app/services/katello/pulp3/content_view_version/export_validator.rb +16 -0
- data/app/views/foreman/smart_proxies/_content_sync.html.erb +1 -1
- data/db/migrate/20230203141353_set_new_acs_verify_ssl_default.rb +5 -0
- data/db/seeds.d/111-upgrade_tasks.rb +2 -1
- data/lib/katello/tasks/upgrades/4.8/regenerate_imported_repository_metadata.rake +33 -0
- data/lib/katello/version.rb +1 -1
- data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ChangeHostCVModal.js +10 -72
- data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -3
- data/webpack/scenes/AlternateContentSources/Details/ACSExpandableDetails.js +6 -5
- data/webpack/scenes/AlternateContentSources/Details/EditModals/ACSEditCredentials.js +1 -0
- data/webpack/scenes/AlternateContentSources/Details/EditModals/ACSEditDetails.js +3 -2
- data/webpack/scenes/AlternateContentSources/Details/EditModals/ACSEditProducts.js +1 -0
- data/webpack/scenes/AlternateContentSources/Details/EditModals/ACSEditSmartProxies.js +2 -0
- data/webpack/scenes/AlternateContentSources/Details/EditModals/ACSEditURLPaths.js +1 -0
- data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +1 -0
- data/webpack/scenes/ContentViews/components/ContentViewSelect/ContentViewSelectOption.js +87 -0
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +1 -1
- data/webpack/scenes/Hosts/ChangeContentSource/components/ContentSourceForm.js +153 -28
- data/webpack/scenes/Hosts/ChangeContentSource/index.js +14 -15
- data/webpack/scenes/Hosts/ChangeContentSource/selectors.js +4 -0
- data/webpack/scenes/Hosts/ChangeContentSource/styles.scss +4 -0
- metadata +6 -3
data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ChangeHostCVModal.js
CHANGED
@@ -1,11 +1,7 @@
|
|
1
1
|
import React, { useState, useCallback } from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
|
-
import { FormattedMessage } from 'react-intl';
|
4
3
|
import { useDispatch, useSelector } from 'react-redux';
|
5
|
-
import { Modal, Button,
|
6
|
-
import {
|
7
|
-
global_palette_black_600 as pfDescriptionColor,
|
8
|
-
} from '@patternfly/react-tokens';
|
4
|
+
import { Modal, Button, Alert } from '@patternfly/react-core';
|
9
5
|
import { translate as __ } from 'foremanReact/common/I18n';
|
10
6
|
import { STATUS } from 'foremanReact/constants';
|
11
7
|
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
@@ -15,42 +11,15 @@ import { ENVIRONMENT_PATHS_KEY } from '../../../../../scenes/ContentViews/compon
|
|
15
11
|
import api from '../../../../../services/api';
|
16
12
|
import getContentViews from '../../../../../scenes/ContentViews/ContentViewsActions';
|
17
13
|
import { selectContentViews, selectContentViewStatus } from '../../../../../scenes/ContentViews/ContentViewSelectors';
|
18
|
-
import { uniq } from '../../../../../utils/helpers';
|
19
|
-
import ContentViewIcon from '../../../../../scenes/ContentViews/components/ContentViewIcon';
|
20
14
|
import updateHostContentViewAndEnvironment from './HostContentViewActions';
|
21
15
|
import HOST_CV_AND_ENV_KEY from './HostContentViewConstants';
|
22
16
|
import { getHostDetails } from '../../HostDetailsActions';
|
23
17
|
import ContentViewSelect from '../../../../../scenes/ContentViews/components/ContentViewSelect/ContentViewSelect';
|
18
|
+
import ContentViewSelectOption
|
19
|
+
from '../../../../../scenes/ContentViews/components/ContentViewSelect/ContentViewSelectOption';
|
24
20
|
|
25
21
|
const ENV_PATH_OPTIONS = { key: ENVIRONMENT_PATHS_KEY };
|
26
22
|
|
27
|
-
const ContentViewDescription = ({ cv, versionNumber }) => {
|
28
|
-
const descriptionStyle = {
|
29
|
-
fontSize: '12px',
|
30
|
-
fontWeight: 400,
|
31
|
-
color: pfDescriptionColor.value,
|
32
|
-
};
|
33
|
-
if (cv.default) return <span style={descriptionStyle}>{__('Library')}</span>;
|
34
|
-
return (
|
35
|
-
<span style={descriptionStyle}>
|
36
|
-
<FormattedMessage
|
37
|
-
id={`content-view-${cv.id}-version-${cv.latest_version}`}
|
38
|
-
defaultMessage="Version {versionNumber}"
|
39
|
-
values={{ versionNumber }}
|
40
|
-
/>
|
41
|
-
</span>
|
42
|
-
);
|
43
|
-
};
|
44
|
-
|
45
|
-
ContentViewDescription.propTypes = {
|
46
|
-
cv: PropTypes.shape({
|
47
|
-
default: PropTypes.bool.isRequired,
|
48
|
-
id: PropTypes.number.isRequired,
|
49
|
-
latest_version: PropTypes.string.isRequired,
|
50
|
-
}).isRequired,
|
51
|
-
versionNumber: PropTypes.string.isRequired,
|
52
|
-
};
|
53
|
-
|
54
23
|
const ChangeHostCVModal = ({
|
55
24
|
isOpen,
|
56
25
|
closeModal,
|
@@ -66,6 +35,7 @@ const ChangeHostCVModal = ({
|
|
66
35
|
const [cvSelectOpen, setCVSelectOpen] = useState(false);
|
67
36
|
const dispatch = useDispatch();
|
68
37
|
const contentViewsInEnvResponse = useSelector(state => selectContentViews(state, `FOR_ENV_${hostEnvId}`));
|
38
|
+
const { results } = contentViewsInEnvResponse;
|
69
39
|
const contentViewsInEnvStatus = useSelector(state => selectContentViewStatus(state, `FOR_ENV_${hostEnvId}`));
|
70
40
|
const hostUpdateStatus = useSelector(state => selectAPIStatus(state, HOST_CV_AND_ENV_KEY));
|
71
41
|
useAPI( // No TableWrapper here, so we can useAPI from Foreman
|
@@ -73,6 +43,7 @@ const ChangeHostCVModal = ({
|
|
73
43
|
api.getApiUrl(`/organizations/${orgId}/environments/paths?permission_type=promotable`),
|
74
44
|
ENV_PATH_OPTIONS,
|
75
45
|
);
|
46
|
+
const selectedCVForHostId = results?.find(cv => cv.name === selectedCVForHost)?.id;
|
76
47
|
|
77
48
|
const handleModalClose = () => {
|
78
49
|
setCVSelectOpen(false);
|
@@ -102,13 +73,6 @@ const ChangeHostCVModal = ({
|
|
102
73
|
const { results: contentViewsInEnv = [] } = contentViewsInEnvResponse;
|
103
74
|
const canSave = !!(selectedCVForHost && selectedEnvForHost.length);
|
104
75
|
|
105
|
-
const relevantVersionObjFromCv = (cv, env) => { // returns the entire version object
|
106
|
-
const versions = cv.versions.filter(version => new Set(version.environment_ids).has(env.id));
|
107
|
-
return uniq(versions)?.[0];
|
108
|
-
};
|
109
|
-
const relevantVersionFromCv = (cv, env) =>
|
110
|
-
relevantVersionObjFromCv(cv, env)?.version; // returns the version text e.g. "1.0"
|
111
|
-
|
112
76
|
const refreshHostDetails = () => {
|
113
77
|
handleModalClose();
|
114
78
|
return dispatch(getHostDetails({ hostname: hostName }));
|
@@ -119,7 +83,7 @@ const ChangeHostCVModal = ({
|
|
119
83
|
id: hostId,
|
120
84
|
host: {
|
121
85
|
content_facet_attributes: {
|
122
|
-
content_view_id:
|
86
|
+
content_view_id: selectedCVForHostId,
|
123
87
|
lifecycle_environment_id: selectedEnvId,
|
124
88
|
},
|
125
89
|
},
|
@@ -183,7 +147,7 @@ const ChangeHostCVModal = ({
|
|
183
147
|
headerText={__('Select environment')}
|
184
148
|
isDisabled={hostUpdateStatus === STATUS.PENDING}
|
185
149
|
/>
|
186
|
-
{selectedEnvForHost.length > 0 &&
|
150
|
+
{selectedEnvForHost.length > 0 && contentViewsInEnvStatus !== STATUS.PENDING &&
|
187
151
|
<ContentViewSelect
|
188
152
|
selections={selectedCVForHost}
|
189
153
|
onClear={() => setSelectedCVForHost(null)}
|
@@ -193,35 +157,9 @@ const ChangeHostCVModal = ({
|
|
193
157
|
onToggle={isExpanded => setCVSelectOpen(isExpanded)}
|
194
158
|
placeholderText={cvPlaceholderText()}
|
195
159
|
>
|
196
|
-
{contentViewsInEnv
|
197
|
-
|
198
|
-
|
199
|
-
value={cv.id}
|
200
|
-
>
|
201
|
-
<Flex
|
202
|
-
direction={{ default: 'row', sm: 'row' }}
|
203
|
-
flexWrap={{ default: 'nowrap' }}
|
204
|
-
alignItems={{ default: 'alignItemsCenter', sm: 'alignItemsCenter' }}
|
205
|
-
>
|
206
|
-
<ContentViewIcon
|
207
|
-
composite={cv.composite}
|
208
|
-
size="sm"
|
209
|
-
/>
|
210
|
-
<Flex
|
211
|
-
direction={{ default: 'column', sm: 'column' }}
|
212
|
-
flexWrap={{ default: 'nowrap' }}
|
213
|
-
alignItems={{ default: 'alignItemsFlexStart', sm: 'alignItemsFlexStart' }}
|
214
|
-
>
|
215
|
-
{cv.name}
|
216
|
-
<ContentViewDescription
|
217
|
-
cv={cv}
|
218
|
-
versionNumber={relevantVersionFromCv(cv, selectedEnv)}
|
219
|
-
/>
|
220
|
-
</Flex>
|
221
|
-
</Flex>
|
222
|
-
</SelectOption>
|
223
|
-
))
|
224
|
-
}
|
160
|
+
{(contentViewsInEnv.length !== 0) &&
|
161
|
+
contentViewsInEnv?.map(cv =>
|
162
|
+
<ContentViewSelectOption key={cv.id} cv={cv} env={selectedEnvForHost[0]} />)}
|
225
163
|
</ContentViewSelect>
|
226
164
|
}
|
227
165
|
</Modal>
|
@@ -11,7 +11,9 @@ const repoTypeSearchQueryMap = {
|
|
11
11
|
|
12
12
|
const recommendedRepositoriesRHEL = [
|
13
13
|
'rhel-9-for-x86_64-baseos-rpms',
|
14
|
+
'rhel-9-for-x86_64-baseos-kickstart',
|
14
15
|
'rhel-9-for-x86_64-appstream-rpms',
|
16
|
+
'rhel-9-for-x86_64-appstream-kickstart',
|
15
17
|
'rhel-8-for-x86_64-baseos-rpms',
|
16
18
|
'rhel-8-for-x86_64-baseos-kickstart',
|
17
19
|
'rhel-8-for-x86_64-appstream-rpms',
|
@@ -33,10 +35,10 @@ const recommendedRepositoriesSatTools = [
|
|
33
35
|
];
|
34
36
|
|
35
37
|
const recommendedRepositoriesMisc = [
|
36
|
-
'satellite-capsule-6.
|
38
|
+
'satellite-capsule-6.13-for-rhel-8-x86_64-rpms',
|
37
39
|
'ansible-2-for-rhel-8-x86_64-rpms',
|
38
|
-
'satellite-maintenance-6.
|
39
|
-
'satellite-utils-6.
|
40
|
+
'satellite-maintenance-6.13-for-rhel-8-x86_64-rpms',
|
41
|
+
'satellite-utils-6.13-for-rhel-8-x86_64-rpms',
|
40
42
|
];
|
41
43
|
|
42
44
|
const recommendedRepositorySetLables = recommendedRepositoriesRHEL
|
@@ -19,6 +19,7 @@ import {
|
|
19
19
|
TextListItem,
|
20
20
|
TextListItemVariants,
|
21
21
|
TextListVariants,
|
22
|
+
Text,
|
22
23
|
Flex,
|
23
24
|
FlexItem,
|
24
25
|
} from '@patternfly/react-core';
|
@@ -104,7 +105,7 @@ const ACSExpandableDetails = ({ expandedId }) => {
|
|
104
105
|
}}
|
105
106
|
contentId="showDetails"
|
106
107
|
>
|
107
|
-
{__('Details')}
|
108
|
+
<Text ouiaId="expandable-details-text">{__('Details')}</Text>
|
108
109
|
</ExpandableSectionToggle>
|
109
110
|
</SplitItem>
|
110
111
|
{canEdit &&
|
@@ -184,7 +185,7 @@ const ACSExpandableDetails = ({ expandedId }) => {
|
|
184
185
|
}}
|
185
186
|
contentId="showSmartProxies"
|
186
187
|
>
|
187
|
-
{__('Smart proxies')}
|
188
|
+
<Text ouiaId="expandable-smart-proxies-text">{__('Smart proxies')}</Text>
|
188
189
|
</ExpandableSectionToggle>
|
189
190
|
</SplitItem>
|
190
191
|
{canEdit &&
|
@@ -255,7 +256,7 @@ const ACSExpandableDetails = ({ expandedId }) => {
|
|
255
256
|
isExpanded={showProducts}
|
256
257
|
contentId="showProducts"
|
257
258
|
>
|
258
|
-
{__('Products')}
|
259
|
+
<Text ouiaId="expandable-products-text">{__('Products')}</Text>
|
259
260
|
</ExpandableSectionToggle>
|
260
261
|
</SplitItem>
|
261
262
|
{canEdit &&
|
@@ -306,7 +307,7 @@ const ACSExpandableDetails = ({ expandedId }) => {
|
|
306
307
|
isExpanded={showUrlPaths}
|
307
308
|
contentId="showUrlPaths"
|
308
309
|
>
|
309
|
-
{__('URL and subpaths')}
|
310
|
+
<Text ouiaId="expandable-url-paths-text">{__('URL and subpaths')}</Text>
|
310
311
|
</ExpandableSectionToggle>
|
311
312
|
</SplitItem>
|
312
313
|
{canEdit &&
|
@@ -367,7 +368,7 @@ const ACSExpandableDetails = ({ expandedId }) => {
|
|
367
368
|
isExpanded={showCredentials}
|
368
369
|
contentId="showCredentials"
|
369
370
|
>
|
370
|
-
{__('Credentials')}
|
371
|
+
<Text ouiaId="expandable-credentials-text">{__('Credentials')}</Text>
|
371
372
|
</ExpandableSectionToggle>
|
372
373
|
</SplitItem>
|
373
374
|
{canEdit &&
|
@@ -34,6 +34,7 @@ const ACSEditDetails = ({ onClose, acsId, acsDetails }) => {
|
|
34
34
|
isOpen
|
35
35
|
onClose={onClose}
|
36
36
|
appendTo={document.body}
|
37
|
+
ouiaId="acs-edit-details-modal"
|
37
38
|
>
|
38
39
|
<Form onSubmit={(e) => {
|
39
40
|
e.preventDefault();
|
@@ -43,9 +44,9 @@ const ACSEditDetails = ({ onClose, acsId, acsDetails }) => {
|
|
43
44
|
<FormGroup label={__('Name')} isRequired fieldId="acs_name">
|
44
45
|
<TextInput
|
45
46
|
isRequired
|
47
|
+
ouiaId="acs-edit-name-field"
|
46
48
|
type="text"
|
47
|
-
id=
|
48
|
-
ouiaId="acs_name_field"
|
49
|
+
id={`acs-edit-name-field-${acsId}`}
|
49
50
|
name="acs_name_field"
|
50
51
|
aria-label="acs_name_field"
|
51
52
|
value={acsName}
|
@@ -80,6 +80,7 @@ const ACSEditSmartProxies = ({ onClose, acsId, acsDetails }) => {
|
|
80
80
|
isOpen
|
81
81
|
onClose={onClose}
|
82
82
|
appendTo={document.body}
|
83
|
+
ouiaId="acs-edit-smart-proxies-modal"
|
83
84
|
>
|
84
85
|
<Form onSubmit={(e) => {
|
85
86
|
e.preventDefault();
|
@@ -110,6 +111,7 @@ const ACSEditSmartProxies = ({ onClose, acsId, acsDetails }) => {
|
|
110
111
|
<Switch
|
111
112
|
id="use-http-proxies-switch"
|
112
113
|
aria-label="use-http-proxies-switch"
|
114
|
+
ouiaId="use-http-proxies-switch"
|
113
115
|
isChecked={acsUseHttpProxies}
|
114
116
|
onChange={checked => setAcsUseHttpProxies(checked)}
|
115
117
|
/>
|
@@ -199,6 +199,7 @@ export default () => {
|
|
199
199
|
<FlexItem>
|
200
200
|
<Dropdown
|
201
201
|
position={DropdownPosition.right}
|
202
|
+
ouiaId="cv-details-actions"
|
202
203
|
style={{ marginLeft: 'auto' }}
|
203
204
|
toggle={<KebabToggle onToggle={setDropdownOpen} id="toggle-dropdown" />}
|
204
205
|
isOpen={dropDownOpen}
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Flex, SelectOption } from '@patternfly/react-core';
|
4
|
+
import { FormattedMessage } from 'react-intl';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import {
|
7
|
+
global_palette_black_600 as pfDescriptionColor,
|
8
|
+
} from '@patternfly/react-tokens';
|
9
|
+
import ContentViewIcon from '../../../../scenes/ContentViews/components/ContentViewIcon';
|
10
|
+
import { uniq } from '../../../../utils/helpers';
|
11
|
+
|
12
|
+
export const ContentViewDescription = ({ cv, versionNumber }) => {
|
13
|
+
const descriptionStyle = {
|
14
|
+
fontSize: '12px',
|
15
|
+
fontWeight: 400,
|
16
|
+
color: pfDescriptionColor.value,
|
17
|
+
};
|
18
|
+
if (cv.default) return <span style={descriptionStyle}>{__('Library')}</span>;
|
19
|
+
return (
|
20
|
+
<span style={descriptionStyle}>
|
21
|
+
<FormattedMessage
|
22
|
+
id={`content-view-${cv.id}-version-${cv.latest_version}`}
|
23
|
+
defaultMessage="Version {versionNumber}"
|
24
|
+
values={{ versionNumber }}
|
25
|
+
/>
|
26
|
+
</span>
|
27
|
+
);
|
28
|
+
};
|
29
|
+
|
30
|
+
ContentViewDescription.propTypes = {
|
31
|
+
cv: PropTypes.shape({
|
32
|
+
default: PropTypes.bool.isRequired,
|
33
|
+
id: PropTypes.number.isRequired,
|
34
|
+
latest_version: PropTypes.string.isRequired,
|
35
|
+
}).isRequired,
|
36
|
+
versionNumber: PropTypes.string.isRequired,
|
37
|
+
};
|
38
|
+
|
39
|
+
export const relevantVersionObjFromCv = (cv, env) => { // returns the entire version object
|
40
|
+
const versions = cv.versions.filter(version => new Set(version.environment_ids).has(env.id));
|
41
|
+
return uniq(versions)?.[0];
|
42
|
+
};
|
43
|
+
|
44
|
+
export const relevantVersionFromCv = (cv, env) =>
|
45
|
+
relevantVersionObjFromCv(cv, env)?.version; // returns the version text e.g. "1.0"
|
46
|
+
|
47
|
+
const ContentViewSelectOption = ({ cv, env }) => (
|
48
|
+
<SelectOption
|
49
|
+
key={cv.id}
|
50
|
+
value={`${cv.name}`}
|
51
|
+
>
|
52
|
+
<Flex
|
53
|
+
direction={{ default: 'row', sm: 'row' }}
|
54
|
+
flexWrap={{ default: 'nowrap' }}
|
55
|
+
alignItems={{ default: 'alignItemsCenter', sm: 'alignItemsCenter' }}
|
56
|
+
>
|
57
|
+
<ContentViewIcon
|
58
|
+
composite={cv.composite}
|
59
|
+
size="sm"
|
60
|
+
/>
|
61
|
+
<Flex
|
62
|
+
direction={{ default: 'column', sm: 'column' }}
|
63
|
+
flexWrap={{ default: 'nowrap' }}
|
64
|
+
alignItems={{ default: 'alignItemsFlexStart', sm: 'alignItemsFlexStart' }}
|
65
|
+
>
|
66
|
+
{cv.name}
|
67
|
+
<ContentViewDescription
|
68
|
+
cv={cv}
|
69
|
+
versionNumber={relevantVersionFromCv(cv, env)}
|
70
|
+
/>
|
71
|
+
</Flex>
|
72
|
+
</Flex>
|
73
|
+
</SelectOption>
|
74
|
+
);
|
75
|
+
|
76
|
+
ContentViewSelectOption.propTypes = {
|
77
|
+
cv: PropTypes.shape({
|
78
|
+
id: PropTypes.number.isRequired,
|
79
|
+
name: PropTypes.string.isRequired,
|
80
|
+
composite: PropTypes.bool.isRequired,
|
81
|
+
}).isRequired,
|
82
|
+
env: PropTypes.shape({
|
83
|
+
id: PropTypes.number.isRequired,
|
84
|
+
}).isRequired,
|
85
|
+
};
|
86
|
+
|
87
|
+
export default ContentViewSelectOption;
|
@@ -47,7 +47,7 @@ const EnvironmentPaths = ({
|
|
47
47
|
return (
|
48
48
|
<div className="env-path" key={index}>
|
49
49
|
{index === 0 && <hr />}
|
50
|
-
<FormGroup key={`fg-${index}`} isInline fieldId="environment-checkbox-group">
|
50
|
+
<FormGroup key={`fg-${index}`} isInline fieldId="environment-checkbox-group" style={{ display: 'block' }}>
|
51
51
|
{environments.map(env =>
|
52
52
|
(<CheckboxOrRadio
|
53
53
|
isChecked={(publishing && env.library) ||
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import { useSelector } from 'react-redux';
|
2
3
|
import {
|
3
4
|
ActionGroup,
|
4
5
|
Alert,
|
@@ -6,20 +7,87 @@ import {
|
|
6
7
|
Form,
|
7
8
|
Grid,
|
8
9
|
GridItem,
|
10
|
+
Select,
|
11
|
+
SelectOption,
|
12
|
+
SelectVariant,
|
13
|
+
TextContent,
|
9
14
|
} from '@patternfly/react-core';
|
10
15
|
import { translate as __ } from 'foremanReact/common/I18n';
|
11
16
|
import PropTypes from 'prop-types';
|
17
|
+
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
18
|
+
import { STATUS } from 'foremanReact/constants';
|
19
|
+
import api, { orgId } from '../../../../services/api';
|
20
|
+
import { ENVIRONMENT_PATHS_KEY } from '../../../../scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathConstants';
|
21
|
+
import EnvironmentPaths from '../../../../scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths';
|
22
|
+
import ContentViewSelect from '../../../../scenes/ContentViews/components/ContentViewSelect/ContentViewSelect';
|
23
|
+
import ContentViewSelectOption from '../../../../scenes/ContentViews/components/ContentViewSelect/ContentViewSelectOption';
|
24
|
+
import { selectContentViewsStatus } from '../selectors';
|
12
25
|
|
13
|
-
|
26
|
+
const ENV_PATH_OPTIONS = { key: ENVIRONMENT_PATHS_KEY };
|
27
|
+
|
28
|
+
const ContentSourceSelect = ({
|
29
|
+
contentSources,
|
30
|
+
selections,
|
31
|
+
onToggle,
|
32
|
+
onSelect,
|
33
|
+
isOpen,
|
34
|
+
isDisabled,
|
35
|
+
onClear,
|
36
|
+
}) => (
|
37
|
+
<div className="content_source_section">
|
38
|
+
<TextContent>{__('Content source')}</TextContent>
|
39
|
+
<Select
|
40
|
+
variant={SelectVariant.single}
|
41
|
+
aria-label="content-source-select"
|
42
|
+
ouiaId="content-source-select"
|
43
|
+
onToggle={onToggle}
|
44
|
+
onSelect={onSelect}
|
45
|
+
selections={selections}
|
46
|
+
isOpen={isOpen}
|
47
|
+
isDisabled={isDisabled}
|
48
|
+
onClear={onClear}
|
49
|
+
className="set-select-width"
|
50
|
+
placeholderText={__('Select a source')}
|
51
|
+
>
|
52
|
+
{contentSources.map(cs => (
|
53
|
+
<SelectOption
|
54
|
+
key={cs.id}
|
55
|
+
value={`${cs.id}`}
|
56
|
+
>
|
57
|
+
{cs.name}
|
58
|
+
</SelectOption>
|
59
|
+
))}
|
60
|
+
</Select>
|
61
|
+
</div>
|
62
|
+
);
|
63
|
+
|
64
|
+
ContentSourceSelect.propTypes = {
|
65
|
+
contentSources: PropTypes.arrayOf(PropTypes.shape({})),
|
66
|
+
selections: PropTypes.string,
|
67
|
+
onToggle: PropTypes.func,
|
68
|
+
onSelect: PropTypes.func,
|
69
|
+
onClear: PropTypes.func,
|
70
|
+
isOpen: PropTypes.bool,
|
71
|
+
isDisabled: PropTypes.bool,
|
72
|
+
};
|
73
|
+
|
74
|
+
ContentSourceSelect.defaultProps = {
|
75
|
+
contentSources: [],
|
76
|
+
selections: null,
|
77
|
+
onToggle: undefined,
|
78
|
+
onSelect: undefined,
|
79
|
+
onClear: undefined,
|
80
|
+
isOpen: false,
|
81
|
+
isDisabled: false,
|
82
|
+
};
|
14
83
|
|
15
84
|
const ContentSourceForm = ({
|
16
85
|
handleSubmit,
|
17
86
|
environments,
|
18
87
|
handleEnvironment,
|
19
|
-
environmentId,
|
20
88
|
contentViews,
|
21
89
|
handleContentView,
|
22
|
-
|
90
|
+
contentViewName,
|
23
91
|
contentSources,
|
24
92
|
handleContentSource,
|
25
93
|
contentSourceId,
|
@@ -27,17 +95,36 @@ const ContentSourceForm = ({
|
|
27
95
|
isLoading,
|
28
96
|
hostsUpdated,
|
29
97
|
}) => {
|
30
|
-
|
31
|
-
|
98
|
+
useAPI( // No TableWrapper here, so we can useAPI from Foreman
|
99
|
+
'get',
|
100
|
+
api.getApiUrl(`/organizations/${orgId()}/environments/paths?permission_type=promotable`),
|
101
|
+
ENV_PATH_OPTIONS,
|
102
|
+
);
|
103
|
+
const contentViewsStatus = useSelector(selectContentViewsStatus);
|
104
|
+
const [csSelectOpen, setCSSelectOpen] = useState(false);
|
105
|
+
const [cvSelectOpen, setCVSelectOpen] = useState(false);
|
106
|
+
|
107
|
+
const handleCSSelect = (_event, selection) => {
|
108
|
+
handleContentSource(selection);
|
109
|
+
setCSSelectOpen(false);
|
110
|
+
};
|
111
|
+
|
112
|
+
const handleCVSelect = (_event, selection) => {
|
113
|
+
handleContentView(selection);
|
114
|
+
setCVSelectOpen(false);
|
115
|
+
};
|
116
|
+
|
117
|
+
const formIsValid = () => (!!environments &&
|
118
|
+
!!contentViewName &&
|
32
119
|
!!contentSourceId &&
|
33
120
|
contentHosts.length !== 0);
|
34
121
|
|
35
122
|
const contentSourcesIsDisabled = (isLoading || contentSources.length === 0 ||
|
36
123
|
contentHosts.length === 0);
|
37
|
-
const environmentIsDisabled = (isLoading || environments
|
124
|
+
const environmentIsDisabled = (isLoading || environments === [] ||
|
38
125
|
contentSourceId === '');
|
39
126
|
const viewIsDisabled = (isLoading || contentViews.length === 0 ||
|
40
|
-
contentSourceId === '' ||
|
127
|
+
contentSourceId === '' || environments === []);
|
41
128
|
|
42
129
|
return (
|
43
130
|
<Form
|
@@ -56,24 +143,64 @@ const ContentSourceForm = ({
|
|
56
143
|
/>
|
57
144
|
</GridItem>
|
58
145
|
)}
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
variant="primary"
|
67
|
-
id="generate_btn"
|
68
|
-
onClick={e => handleSubmit(e)}
|
69
|
-
isDisabled={isLoading || !formIsValid() || hostsUpdated}
|
70
|
-
isLoading={isLoading}
|
146
|
+
{contentViewsStatus === STATUS.RESOLVED &&
|
147
|
+
!!environments.length && contentViews.length === 0 &&
|
148
|
+
<Alert
|
149
|
+
variant="warning"
|
150
|
+
className="margin-top-20"
|
151
|
+
title={__('No content views available for the selected environment')}
|
152
|
+
style={{ marginBottom: '1rem' }}
|
71
153
|
>
|
72
|
-
{__('
|
73
|
-
|
74
|
-
|
75
|
-
|
154
|
+
<a href="/content_views">{__('View the Content Views page')}</a>
|
155
|
+
{__(' to manage and promote content views, or select a different environment.')}
|
156
|
+
</Alert>
|
157
|
+
}
|
76
158
|
</Grid>
|
159
|
+
<ContentSourceSelect
|
160
|
+
contentSources={contentSources}
|
161
|
+
selections={contentSourceId}
|
162
|
+
onToggle={isExpanded => setCSSelectOpen(isExpanded)}
|
163
|
+
onSelect={handleCSSelect}
|
164
|
+
onClear={() => handleContentSource(null)}
|
165
|
+
isOpen={csSelectOpen}
|
166
|
+
isDisabled={contentSourcesIsDisabled || hostsUpdated}
|
167
|
+
/>
|
168
|
+
<EnvironmentPaths
|
169
|
+
style={{ display: 'block' }}
|
170
|
+
userCheckedItems={environments}
|
171
|
+
setUserCheckedItems={handleEnvironment}
|
172
|
+
publishing={false}
|
173
|
+
multiSelect={false}
|
174
|
+
headerText={__('Environment')}
|
175
|
+
isDisabled={environmentIsDisabled || hostsUpdated}
|
176
|
+
/>
|
177
|
+
{environments.length > 0 && contentViewsStatus !== STATUS.PENDING &&
|
178
|
+
<ContentViewSelect
|
179
|
+
selections={contentViewName}
|
180
|
+
onClear={() => handleContentView(null)}
|
181
|
+
onSelect={handleCVSelect}
|
182
|
+
isOpen={cvSelectOpen}
|
183
|
+
isDisabled={viewIsDisabled || hostsUpdated}
|
184
|
+
onToggle={isExpanded => setCVSelectOpen(isExpanded)}
|
185
|
+
headerText={__('Content view')}
|
186
|
+
ouiaId="SelectContentView"
|
187
|
+
className="set-select-width"
|
188
|
+
placeholderText={(contentViews.length === 0) ? __('No content views available') : __('Select a content view')}
|
189
|
+
>
|
190
|
+
{contentViews?.map(cv => <ContentViewSelectOption key={`${cv.id}`} cv={cv} env={environments[0]} />)}
|
191
|
+
</ContentViewSelect>
|
192
|
+
}
|
193
|
+
<ActionGroup style={{ display: 'block' }}>
|
194
|
+
<Button
|
195
|
+
variant="primary"
|
196
|
+
id="generate_btn"
|
197
|
+
onClick={e => handleSubmit(e)}
|
198
|
+
isDisabled={isLoading || !formIsValid() || hostsUpdated}
|
199
|
+
isLoading={isLoading}
|
200
|
+
>
|
201
|
+
{__('Update')}
|
202
|
+
</Button>
|
203
|
+
</ActionGroup>
|
77
204
|
</Form>);
|
78
205
|
};
|
79
206
|
|
@@ -81,10 +208,9 @@ ContentSourceForm.propTypes = {
|
|
81
208
|
handleSubmit: PropTypes.func.isRequired,
|
82
209
|
environments: PropTypes.arrayOf(PropTypes.shape({})),
|
83
210
|
handleEnvironment: PropTypes.func.isRequired,
|
84
|
-
environmentId: PropTypes.string,
|
85
211
|
contentViews: PropTypes.arrayOf(PropTypes.shape({})),
|
86
212
|
handleContentView: PropTypes.func.isRequired,
|
87
|
-
|
213
|
+
contentViewName: PropTypes.string,
|
88
214
|
contentSources: PropTypes.arrayOf(PropTypes.shape({})),
|
89
215
|
handleContentSource: PropTypes.func.isRequired,
|
90
216
|
contentSourceId: PropTypes.string,
|
@@ -95,9 +221,8 @@ ContentSourceForm.propTypes = {
|
|
95
221
|
|
96
222
|
ContentSourceForm.defaultProps = {
|
97
223
|
environments: [],
|
98
|
-
environmentId: '',
|
99
224
|
contentViews: [],
|
100
|
-
|
225
|
+
contentViewName: '',
|
101
226
|
contentSources: [],
|
102
227
|
contentSourceId: '',
|
103
228
|
contentHosts: [],
|