katello 4.3.0.rc3 → 4.3.0.rc4
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/concerns/api/v2/registration_controller_extensions.rb +1 -9
- data/app/lib/actions/katello/capsule_content/refresh_repos.rb +1 -1
- data/app/lib/actions/pulp3/capsule_content/generate_metadata.rb +5 -4
- data/app/lib/katello/resources/registry.rb +1 -1
- data/app/models/katello/concerns/smart_proxy_extensions.rb +15 -7
- data/app/services/katello/pulp3/repository.rb +0 -4
- data/app/services/katello/pulp3/repository_mirror.rb +1 -1
- data/app/views/foreman/job_templates/change_content_source.erb +42 -0
- data/db/migrate/20210331180353_katello_pool_organization_id_not_nullable.rb +2 -0
- data/lib/katello/version.rb +1 -1
- data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +7 -11
- data/webpack/scenes/ContentViews/Copy/CopyContentViewModal.js +1 -1
- data/webpack/scenes/ContentViews/Copy/__tests__/copyContentView.test.js +1 -1
- data/webpack/scenes/ContentViews/Delete/Steps/CVDeletionFinish.js +10 -4
- data/webpack/scenes/ContentViews/Delete/__tests__/contentViewDelete.test.js +5 -2
- data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +106 -41
- data/webpack/scenes/ContentViews/Details/Promote/ContentViewVersionPromote.js +17 -4
- data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +25 -3
- data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveCVVersionWizard.js +4 -1
- data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVVersionDeleteFinish.js +14 -3
- data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetails.js +4 -2
- data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetailsHeader.js +133 -31
- data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionDetails.test.js +21 -1
- data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionDetailsEmpty.test.js +22 -1
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsActions.test.js.snap +1 -0
- data/webpack/scenes/Tasks/TaskActions.js +4 -3
- data/webpack/scenes/Tasks/__tests__/__snapshots__/TaskActions.test.js.snap +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83de032a01e5d20e480d164eb425db853b1eea9a4668eb1aa853bfaa5d6a703a
|
4
|
+
data.tar.gz: aad3b14f2608df9c71cb039222ced8baf98bc89537c28967fcc1d47ac3ce3c5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb93e596697f4639b7b4ba3d512524f9880f63442bdba0b4963f73fe6aa18b61d4565f71f488a7cd99f54c0908cbe74cc59468b987450c5d02c9f8020a521761
|
7
|
+
data.tar.gz: 9ffa356f5005ab1e2119c119c24837530c23d4d004c1a9c99a3b120fc648f443bf126faae8adff3a5763714ba70d9aa412976f50b6d886e6dc3bd0fb4257dcce
|
@@ -28,7 +28,7 @@ module Katello
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def context_urls
|
31
|
-
super.merge(rhsm_url: rhsm_url, pulp_content_url: pulp_content_url)
|
31
|
+
super.merge(rhsm_url: smart_proxy.rhsm_url, pulp_content_url: smart_proxy.pulp_content_url)
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -43,14 +43,6 @@ module Katello
|
|
43
43
|
proxy
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
47
|
-
def rhsm_url
|
48
|
-
URI(smart_proxy.rhsm_url)
|
49
|
-
end
|
50
|
-
|
51
|
-
def pulp_content_url
|
52
|
-
smart_proxy.setting(SmartProxy::PULP3_FEATURE, 'content_app_url')
|
53
|
-
end
|
54
46
|
end
|
55
47
|
end
|
56
48
|
end
|
@@ -40,7 +40,7 @@ module Actions
|
|
40
40
|
pulp_repo = repo.backend_service(smart_proxy)
|
41
41
|
if !current_repos_on_capsule_ids.include?(repo.id)
|
42
42
|
pulp_repo.create_mirror_entities
|
43
|
-
|
43
|
+
else
|
44
44
|
tasks += pulp_repo.refresh_mirror_entities
|
45
45
|
end
|
46
46
|
end
|
@@ -17,13 +17,14 @@ module Actions
|
|
17
17
|
|
18
18
|
def invoke_external_task
|
19
19
|
repository = ::Katello::Repository.find(input[:repository_id])
|
20
|
+
backend = repository.backend_service(smart_proxy).with_mirror_adapter
|
20
21
|
#yum repositories use metadata mirroring always, so we should never
|
21
|
-
# regenerate metadata on proxies
|
22
|
-
|
22
|
+
# regenerate metadata on proxies. but if there is no publication,
|
23
|
+
# it means the repo was likely empty and syncing didn't generate one
|
24
|
+
if repository.yum? && backend.publication_href.present?
|
23
25
|
[]
|
24
26
|
else
|
25
|
-
|
26
|
-
repository.backend_service(smart_proxy).with_mirror_adapter.create_publication
|
27
|
+
backend.create_publication
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
@@ -32,7 +32,7 @@ module Katello
|
|
32
32
|
self.prefix = "/pulpcore_registry/"
|
33
33
|
self.site = "#{uri.scheme}://#{uri.host}:#{uri.port}"
|
34
34
|
self.ca_cert_file = Setting[:ssl_ca_file]
|
35
|
-
pulp_primary.pulp3_ssl_configuration(self)
|
35
|
+
pulp_primary.pulp3_ssl_configuration(self, :net_http)
|
36
36
|
|
37
37
|
self
|
38
38
|
end
|
@@ -185,17 +185,17 @@ module Katello
|
|
185
185
|
end
|
186
186
|
end
|
187
187
|
|
188
|
-
def pulp3_ssl_configuration(config)
|
188
|
+
def pulp3_ssl_configuration(config, connection_adapter = Faraday.default_adapter)
|
189
189
|
legacy_pulp_cert = !self.setting(PULP3_FEATURE, 'client_authentication')&.include?('client_certificate')
|
190
190
|
|
191
|
-
if
|
191
|
+
if connection_adapter == :excon
|
192
192
|
config.ssl_client_cert = ::Cert::Certs.ssl_client_cert_filename(use_admin_as_cn_cert: legacy_pulp_cert)
|
193
193
|
config.ssl_client_key = ::Cert::Certs.ssl_client_key_filename(use_admin_as_cn_cert: legacy_pulp_cert)
|
194
|
-
elsif
|
194
|
+
elsif connection_adapter == :net_http
|
195
195
|
config.ssl_client_cert = ::Cert::Certs.ssl_client_cert(use_admin_as_cn_cert: legacy_pulp_cert)
|
196
196
|
config.ssl_client_key = ::Cert::Certs.ssl_client_key(use_admin_as_cn_cert: legacy_pulp_cert)
|
197
197
|
else
|
198
|
-
fail "Unexpected
|
198
|
+
fail "Unexpected connection_adapter #{Faraday.default_adapter}! Cannot continue, this is likely a bug."
|
199
199
|
end
|
200
200
|
end
|
201
201
|
|
@@ -446,14 +446,22 @@ module Katello
|
|
446
446
|
def rhsm_url
|
447
447
|
# Since Foreman 3.1 this setting is set
|
448
448
|
if (rhsm_url = setting(SmartProxy::PULP3_FEATURE, 'rhsm_url'))
|
449
|
-
rhsm_url
|
449
|
+
URI(rhsm_url)
|
450
450
|
# Compatibility fall back
|
451
451
|
elsif pulp_primary?
|
452
|
-
"https://#{URI.parse(url).host}/rhsm"
|
452
|
+
URI("https://#{URI.parse(url).host}/rhsm")
|
453
453
|
elsif pulp_mirror?
|
454
|
-
"https://#{URI.parse(url).host}:8443/rhsm"
|
454
|
+
URI("https://#{URI.parse(url).host}:8443/rhsm")
|
455
455
|
end
|
456
456
|
end
|
457
|
+
|
458
|
+
def pulp_content_url
|
459
|
+
URI(setting(SmartProxy::PULP3_FEATURE, 'content_app_url'))
|
460
|
+
end
|
461
|
+
|
462
|
+
class ::SmartProxy::Jail < ::Safemode::Jail
|
463
|
+
allow :rhsm_url, :pulp_content_url
|
464
|
+
end
|
457
465
|
end
|
458
466
|
end
|
459
467
|
end
|
@@ -183,10 +183,6 @@ module Katello
|
|
183
183
|
RepositoryMirror.new(self).refresh_entities
|
184
184
|
end
|
185
185
|
|
186
|
-
def mirror_needs_updates?
|
187
|
-
RepositoryMirror.new(self).needs_updates?
|
188
|
-
end
|
189
|
-
|
190
186
|
def refresh_if_needed
|
191
187
|
tasks = []
|
192
188
|
tasks << update_remote #always update remote
|
@@ -79,7 +79,7 @@ module Katello
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def publication_href
|
82
|
-
api.publications_api.list(:repository_version => version_href).results.first
|
82
|
+
api.publications_api.list(:repository_version => version_href).results.first&.pulp_href
|
83
83
|
end
|
84
84
|
|
85
85
|
def create_version(options = {})
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<%#
|
2
|
+
kind: job_template
|
3
|
+
name: Change content source
|
4
|
+
job_category: Katello
|
5
|
+
model: JobTemplate
|
6
|
+
provider_type: SSH
|
7
|
+
description_format: Configure subscription manager to new content source
|
8
|
+
feature: katello_change_content_source
|
9
|
+
%>
|
10
|
+
#!/bin/sh
|
11
|
+
<%
|
12
|
+
content_source = @host.content_source
|
13
|
+
-%>
|
14
|
+
|
15
|
+
<% if content_source -%>
|
16
|
+
SSL_CA_CERT=$(mktemp)
|
17
|
+
cat << EOF > $SSL_CA_CERT
|
18
|
+
<%= foreman_server_ca_cert %>
|
19
|
+
EOF
|
20
|
+
|
21
|
+
KATELLO_SERVER_CA_CERT=/etc/rhsm/ca/katello-server-ca.pem
|
22
|
+
RHSM_CFG=/etc/rhsm/rhsm.conf
|
23
|
+
|
24
|
+
# Prepare SSL certificate
|
25
|
+
mkdir -p /etc/rhsm/ca
|
26
|
+
cp -f $SSL_CA_CERT $KATELLO_SERVER_CA_CERT
|
27
|
+
chmod 644 $KATELLO_SERVER_CA_CERT
|
28
|
+
|
29
|
+
# Configure subscription-manager
|
30
|
+
test -f $RHSM_CFG.bak || cp $RHSM_CFG $RHSM_CFG.bak
|
31
|
+
|
32
|
+
subscription-manager config \
|
33
|
+
--server.hostname="<%= content_source.rhsm_url.host %>" \
|
34
|
+
--server.port="<%= content_source.rhsm_url.port %>" \
|
35
|
+
--server.prefix="<%= content_source.rhsm_url.path %>" \
|
36
|
+
--rhsm.repo_ca_cert="$KATELLO_SERVER_CA_CERT" \
|
37
|
+
--rhsm.baseurl="<%= content_source.pulp_content_url %>"
|
38
|
+
|
39
|
+
<% else -%>
|
40
|
+
echo "Host [<%= @host.name %>] doesn't have assigned content source!"
|
41
|
+
exit 1
|
42
|
+
<% end -%>
|
@@ -1,6 +1,8 @@
|
|
1
1
|
class KatelloPoolOrganizationIdNotNullable < ActiveRecord::Migration[6.0]
|
2
2
|
def up
|
3
3
|
::Katello::Pool.where(organization_id: nil).destroy_all
|
4
|
+
::Katello::Pool.where(subscription_id: nil).destroy_all
|
5
|
+
|
4
6
|
change_column :katello_pools, :organization_id, :integer, null: false
|
5
7
|
change_column :katello_pools, :subscription_id, :integer, null: false
|
6
8
|
|
data/lib/katello/version.rb
CHANGED
@@ -3,7 +3,7 @@ import React, { useState } from 'react';
|
|
3
3
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
4
4
|
import PropTypes from 'prop-types';
|
5
5
|
import { useDispatch, useSelector } from 'react-redux';
|
6
|
-
import {
|
6
|
+
import { useHistory } from 'react-router-dom';
|
7
7
|
import { translate as __ } from 'foremanReact/common/I18n';
|
8
8
|
import { Form, FormGroup, TextInput, ActionGroup, Button } from '@patternfly/react-core';
|
9
9
|
import {
|
@@ -15,21 +15,22 @@ import { copyContentView } from '../ContentViewsActions';
|
|
15
15
|
const CopyContentViewForm = ({ cvId, setModalOpen }) => {
|
16
16
|
const dispatch = useDispatch();
|
17
17
|
const [name, setName] = useState('');
|
18
|
-
const [redirect, setRedirect] = useState(false);
|
19
18
|
const [saving, setSaving] = useState(false);
|
20
19
|
const response = useSelector(selectCopyContentViews);
|
21
20
|
const status = useSelector(selectCopyContentViewStatus);
|
22
21
|
const error = useSelector(selectCopyContentViewError);
|
22
|
+
const { push } = useHistory();
|
23
23
|
|
24
24
|
useDeepCompareEffect(() => {
|
25
25
|
const { id } = response;
|
26
|
-
if (id && status === STATUS.RESOLVED) {
|
26
|
+
if (saving && id && status === STATUS.RESOLVED) {
|
27
27
|
setSaving(false);
|
28
|
-
|
28
|
+
setModalOpen(false);
|
29
|
+
push(`/content_views/${id}`);
|
29
30
|
} else if (status === STATUS.ERROR) {
|
30
31
|
setSaving(false);
|
31
32
|
}
|
32
|
-
}, [response, status, error]);
|
33
|
+
}, [response, status, error, saving, setSaving, setModalOpen, push]);
|
33
34
|
|
34
35
|
const onSubmit = () => {
|
35
36
|
setSaving(true);
|
@@ -39,11 +40,6 @@ const CopyContentViewForm = ({ cvId, setModalOpen }) => {
|
|
39
40
|
}));
|
40
41
|
};
|
41
42
|
|
42
|
-
if (redirect) {
|
43
|
-
const { id } = response;
|
44
|
-
return (<Redirect to={`/content_views/${id}`} />);
|
45
|
-
}
|
46
|
-
|
47
43
|
return (
|
48
44
|
<Form onSubmit={(e) => {
|
49
45
|
e.preventDefault();
|
@@ -77,7 +73,7 @@ const CopyContentViewForm = ({ cvId, setModalOpen }) => {
|
|
77
73
|
};
|
78
74
|
|
79
75
|
CopyContentViewForm.propTypes = {
|
80
|
-
cvId: PropTypes.string,
|
76
|
+
cvId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
81
77
|
setModalOpen: PropTypes.func,
|
82
78
|
};
|
83
79
|
|
@@ -32,7 +32,7 @@ test('Can copy content view from form', async (done) => {
|
|
32
32
|
getByLabelText('copy_content_view').click();
|
33
33
|
// Form closes it self on success
|
34
34
|
await patientlyWaitFor(() => {
|
35
|
-
expect(
|
35
|
+
expect(setModalOpen).toBeCalled();
|
36
36
|
});
|
37
37
|
|
38
38
|
assertNockRequest(copyscope, done);
|
@@ -1,9 +1,10 @@
|
|
1
1
|
import React, { useContext, useState } from 'react';
|
2
2
|
import { useSelector, useDispatch } from 'react-redux';
|
3
|
+
import { useHistory } from 'react-router-dom';
|
3
4
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
4
5
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
6
|
import { STATUS } from 'foremanReact/constants';
|
6
|
-
import {
|
7
|
+
import { removeContentViewVersion } from '../../Details/ContentViewDetailActions';
|
7
8
|
import { selectRemoveCVVersionResponse, selectRemoveCVVersionStatus } from '../../Details/ContentViewDetailSelectors';
|
8
9
|
import getContentViews from '../../ContentViewsActions';
|
9
10
|
import CVDeleteContext from '../CVDeleteContext';
|
@@ -23,14 +24,19 @@ const CVDeletionFinish = () => {
|
|
23
24
|
const removeResolved = removeCVStatus === STATUS.RESOLVED;
|
24
25
|
const dispatch = useDispatch();
|
25
26
|
const [removeDispatched, setRemoveDispatched] = useState(false);
|
27
|
+
const { push } = useHistory();
|
26
28
|
|
27
29
|
useDeepCompareEffect(() => {
|
28
30
|
if (removeResolved && removeDispatched) {
|
31
|
+
dispatch(getContentViews());
|
32
|
+
push('/content_views');
|
29
33
|
setIsOpen(false);
|
30
|
-
dispatch(getContentViewVersions(cvId));
|
31
|
-
dispatch(getContentViews);
|
32
34
|
}
|
33
|
-
|
35
|
+
if (removeCVStatus === STATUS.ERROR) {
|
36
|
+
setIsOpen(false);
|
37
|
+
}
|
38
|
+
}, [removeCVResponse, removeCVStatus, removeResolved,
|
39
|
+
setIsOpen, dispatch, cvId, removeDispatched, push]);
|
34
40
|
|
35
41
|
useDeepCompareEffect(() => {
|
36
42
|
if (!removeDispatched) {
|
@@ -119,7 +119,6 @@ test('Can open Delete wizard and delete CV with all steps', async (done) => {
|
|
119
119
|
|
120
120
|
const cvVersionsScope = nockInstance
|
121
121
|
.get(cvVersionsPath)
|
122
|
-
.times(2)
|
123
122
|
.query(true)
|
124
123
|
.reply(200, cvVersionsData);
|
125
124
|
|
@@ -128,6 +127,10 @@ test('Can open Delete wizard and delete CV with all steps', async (done) => {
|
|
128
127
|
.query(true)
|
129
128
|
.reply(200, cvDetailsData);
|
130
129
|
|
130
|
+
const cvRedirectScope = nockInstance
|
131
|
+
.get(api.getApiUrl('/content_views?organization_id=1&nondefault=true&include_permissions=true'))
|
132
|
+
.reply(200, cvIndexData);
|
133
|
+
|
131
134
|
const cvDeleteParams = {
|
132
135
|
destroy_content_view: true,
|
133
136
|
system_content_view_id: 2,
|
@@ -239,5 +242,5 @@ test('Can open Delete wizard and delete CV with all steps', async (done) => {
|
|
239
242
|
assertNockRequest(activationKeysScope);
|
240
243
|
assertNockRequest(cVDropDownOptionsScope);
|
241
244
|
assertNockRequest(cvDeleteScope);
|
242
|
-
assertNockRequest(
|
245
|
+
assertNockRequest(cvRedirectScope, done);
|
243
246
|
});
|
@@ -1,7 +1,18 @@
|
|
1
1
|
import React, { useState } from 'react';
|
2
2
|
import { useSelector, shallowEqual } from 'react-redux';
|
3
3
|
import { useParams } from 'react-router-dom';
|
4
|
-
import { Grid,
|
4
|
+
import { Grid,
|
5
|
+
GridItem,
|
6
|
+
TextContent,
|
7
|
+
Text,
|
8
|
+
TextVariants,
|
9
|
+
Button,
|
10
|
+
Flex,
|
11
|
+
FlexItem,
|
12
|
+
Dropdown,
|
13
|
+
DropdownItem,
|
14
|
+
KebabToggle,
|
15
|
+
DropdownPosition } from '@patternfly/react-core';
|
5
16
|
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
6
17
|
import { translate as __ } from 'foremanReact/common/I18n';
|
7
18
|
|
@@ -18,6 +29,8 @@ import ContentViewIcon from '../components/ContentViewIcon';
|
|
18
29
|
import CVBreadCrumb from '../components/CVBreadCrumb';
|
19
30
|
import PublishContentViewWizard from '../Publish/PublishContentViewWizard';
|
20
31
|
import { hasPermission } from '../helpers';
|
32
|
+
import CopyContentViewModal from '../Copy/CopyContentViewModal';
|
33
|
+
import ContentViewDeleteWizard from '../Delete/ContentViewDeleteWizard';
|
21
34
|
|
22
35
|
export default () => {
|
23
36
|
const { id } = useParams();
|
@@ -25,8 +38,31 @@ export default () => {
|
|
25
38
|
const details = useSelector(state => selectCVDetails(state, cvId), shallowEqual);
|
26
39
|
const [isPublishModalOpen, setIsPublishModalOpen] = useState(false);
|
27
40
|
const [currentStep, setCurrentStep] = useState(1);
|
41
|
+
const [dropDownOpen, setDropdownOpen] = useState(false);
|
42
|
+
const [copying, setCopying] = useState(false);
|
43
|
+
const [deleting, setDeleting] = useState(false);
|
44
|
+
const dropDownItems = [
|
45
|
+
<DropdownItem
|
46
|
+
key="copy"
|
47
|
+
onClick={() => {
|
48
|
+
setCopying(true);
|
49
|
+
}}
|
50
|
+
>
|
51
|
+
{__('Copy')}
|
52
|
+
</DropdownItem>,
|
53
|
+
<DropdownItem
|
54
|
+
key="delete"
|
55
|
+
onClick={() => {
|
56
|
+
setDeleting(true);
|
57
|
+
}}
|
58
|
+
>
|
59
|
+
{__('Delete')}
|
60
|
+
</DropdownItem>,
|
61
|
+
];
|
28
62
|
|
29
|
-
const {
|
63
|
+
const {
|
64
|
+
name, composite, permissions, environments, versions,
|
65
|
+
} = details;
|
30
66
|
const tabs = [
|
31
67
|
{
|
32
68
|
key: 'details',
|
@@ -60,27 +96,28 @@ export default () => {
|
|
60
96
|
];
|
61
97
|
|
62
98
|
return (
|
63
|
-
|
64
|
-
<
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
<
|
99
|
+
<>
|
100
|
+
<Grid className="grid-with-margin">
|
101
|
+
<DetailsContainer cvId={cvId}>
|
102
|
+
<>
|
103
|
+
<CVBreadCrumb />
|
104
|
+
<GridItem xl={8} lg={7} sm={12} style={{ margin: '10px 0' }}>
|
105
|
+
<Flex alignItems={{
|
69
106
|
default: 'alignItemsCenter',
|
70
107
|
}}
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
108
|
+
>
|
109
|
+
<FlexItem>
|
110
|
+
<TextContent>
|
111
|
+
<Text component={TextVariants.h1}>
|
112
|
+
<ContentViewIcon count={name} composite={composite} />
|
113
|
+
</Text>
|
114
|
+
</TextContent>
|
115
|
+
</FlexItem>
|
116
|
+
</Flex>
|
117
|
+
</GridItem>
|
118
|
+
<GridItem xl={4} lg={5} sm={12} >
|
119
|
+
<Flex justifyContent={{ lg: 'justifyContentFlexEnd', sm: 'justifyContentFlexStart' }}>
|
120
|
+
{hasPermission(permissions, 'publish_content_views') &&
|
84
121
|
<FlexItem>
|
85
122
|
<Button onClick={() => { setIsPublishModalOpen(true); }} variant="primary" aria-label="publish_content_view">
|
86
123
|
{__('Publish new version')}
|
@@ -95,25 +132,53 @@ export default () => {
|
|
95
132
|
/>}
|
96
133
|
</FlexItem>
|
97
134
|
}
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
135
|
+
<FlexItem>
|
136
|
+
<Button
|
137
|
+
component="a"
|
138
|
+
aria-label="view tasks button"
|
139
|
+
href={`/foreman_tasks/tasks?search=resource_type%3D+Katello%3A%3AContentView+resource_id%3D${cvId}`}
|
140
|
+
target="_blank"
|
141
|
+
variant="secondary"
|
142
|
+
>
|
143
|
+
{'View tasks '}
|
144
|
+
<ExternalLinkAltIcon />
|
145
|
+
</Button>
|
146
|
+
</FlexItem>
|
147
|
+
<FlexItem>
|
148
|
+
<Dropdown
|
149
|
+
position={DropdownPosition.right}
|
150
|
+
style={{ marginLeft: 'auto' }}
|
151
|
+
toggle={<KebabToggle onToggle={setDropdownOpen} id="toggle-dropdown" />}
|
152
|
+
isOpen={dropDownOpen}
|
153
|
+
isPlain
|
154
|
+
dropdownItems={dropDownItems}
|
155
|
+
/>
|
156
|
+
</FlexItem>
|
157
|
+
</Flex>
|
158
|
+
</GridItem>
|
159
|
+
<GridItem span={12}>
|
160
|
+
<RoutedTabs tabs={tabs} defaultTabIndex={1} />
|
161
|
+
</GridItem>
|
162
|
+
</ >
|
163
|
+
</DetailsContainer >
|
164
|
+
</Grid >
|
165
|
+
{copying && <CopyContentViewModal
|
166
|
+
cvId={cvId}
|
167
|
+
cvName={name}
|
168
|
+
show={copying}
|
169
|
+
setIsOpen={setCopying}
|
170
|
+
aria-label="copy_content_view_modal"
|
171
|
+
/>}
|
172
|
+
{deleting && <ContentViewDeleteWizard
|
173
|
+
cvId={cvId && Number(cvId)}
|
174
|
+
cvEnvironments={environments}
|
175
|
+
cvVersions={versions}
|
176
|
+
show={deleting}
|
177
|
+
setIsOpen={setDeleting}
|
178
|
+
currentStep={currentStep}
|
179
|
+
setCurrentStep={setCurrentStep}
|
180
|
+
aria-label="delete_content_view_modal"
|
181
|
+
/>}
|
182
|
+
</>
|
118
183
|
);
|
119
184
|
};
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import React, { useState, useMemo, useCallback } from 'react';
|
2
2
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
3
3
|
import { useDispatch, useSelector } from 'react-redux';
|
4
|
+
import { Redirect } from 'react-router-dom';
|
4
5
|
import { STATUS } from 'foremanReact/constants';
|
5
6
|
import PropTypes from 'prop-types';
|
6
7
|
import {
|
@@ -8,7 +9,6 @@ import {
|
|
8
9
|
Modal, ModalVariant, Alert, TextContent, AlertActionCloseButton,
|
9
10
|
} from '@patternfly/react-core';
|
10
11
|
import { translate as __ } from 'foremanReact/common/I18n';
|
11
|
-
|
12
12
|
import {
|
13
13
|
selectEnvironmentPaths,
|
14
14
|
selectEnvironmentPathsStatus,
|
@@ -23,13 +23,15 @@ import ComponentEnvironments from '../ComponentContentViews/ComponentEnvironment
|
|
23
23
|
import Loading from '../../../../components/Loading';
|
24
24
|
|
25
25
|
const ContentViewVersionPromote = ({
|
26
|
-
cvId, versionIdToPromote, versionNameToPromote,
|
26
|
+
cvId, versionIdToPromote, versionNameToPromote,
|
27
|
+
versionEnvironments, setIsOpen, detailsPage,
|
27
28
|
}) => {
|
28
29
|
const [description, setDescription] = useState('');
|
29
30
|
const [userCheckedItems, setUserCheckedItems] = useState([]);
|
30
31
|
const [alertDismissed, setAlertDismissed] = useState(false);
|
31
32
|
const [loading, setLoading] = useState(false);
|
32
33
|
const [forcePromote, setForcePromote] = useState([]);
|
34
|
+
const [redirect, setRedirect] = useState(false);
|
33
35
|
const environmentPathResponse = useSelector(selectEnvironmentPaths);
|
34
36
|
const environmentPathStatus = useSelector(selectEnvironmentPathsStatus);
|
35
37
|
const environmentPathLoading = environmentPathStatus === STATUS.PENDING;
|
@@ -55,13 +57,18 @@ const ContentViewVersionPromote = ({
|
|
55
57
|
|
56
58
|
useDeepCompareEffect(() => {
|
57
59
|
if (promoteResolved && promoteResponse) {
|
58
|
-
setIsOpen(false);
|
59
60
|
dispatch(getContentViewVersions(cvId));
|
61
|
+
if (detailsPage) {
|
62
|
+
setRedirect(true);
|
63
|
+
} else {
|
64
|
+
setIsOpen(false);
|
65
|
+
}
|
60
66
|
}
|
61
67
|
if (promoteError) {
|
62
68
|
setLoading(false);
|
63
69
|
}
|
64
|
-
}, [promoteResponse, promoteResolved, promoteError,
|
70
|
+
}, [promoteResponse, promoteResolved, promoteError, detailsPage,
|
71
|
+
setRedirect, setLoading, setIsOpen, dispatch, cvId]);
|
65
72
|
|
66
73
|
const envPathFlat = useMemo(() => {
|
67
74
|
if (!environmentPathLoading) {
|
@@ -94,6 +101,10 @@ const ContentViewVersionPromote = ({
|
|
94
101
|
|
95
102
|
const submitDisabled = loading || userCheckedItems.length === 0;
|
96
103
|
|
104
|
+
if (redirect && detailsPage) {
|
105
|
+
return (<Redirect to="/versions" />);
|
106
|
+
}
|
107
|
+
|
97
108
|
return (
|
98
109
|
<Modal
|
99
110
|
title={__(`Promote version ${versionNameToPromote}`)}
|
@@ -169,11 +180,13 @@ ContentViewVersionPromote.propTypes = {
|
|
169
180
|
versionNameToPromote: PropTypes.string.isRequired,
|
170
181
|
versionEnvironments: PropTypes.arrayOf(PropTypes.shape({})),
|
171
182
|
setIsOpen: PropTypes.func,
|
183
|
+
detailsPage: PropTypes.bool,
|
172
184
|
};
|
173
185
|
|
174
186
|
ContentViewVersionPromote.defaultProps = {
|
175
187
|
versionEnvironments: [],
|
176
188
|
setIsOpen: null,
|
189
|
+
detailsPage: false,
|
177
190
|
};
|
178
191
|
|
179
192
|
export default ContentViewVersionPromote;
|
@@ -9,6 +9,7 @@ import { Link } from 'react-router-dom';
|
|
9
9
|
import PropTypes from 'prop-types';
|
10
10
|
import { selectIntervals } from 'foremanReact/redux/middlewares/IntervalMiddleware/IntervalSelectors.js';
|
11
11
|
|
12
|
+
import { useSet } from '../../../../components/Table/TableHooks';
|
12
13
|
import TableWrapper from '../../../../components/Table/TableWrapper';
|
13
14
|
import InactiveText from '../../components/InactiveText';
|
14
15
|
import ContentViewVersionEnvironments from './ContentViewVersionEnvironments';
|
@@ -23,7 +24,7 @@ import {
|
|
23
24
|
import getEnvironmentPaths from '../../components/EnvironmentPaths/EnvironmentPathActions';
|
24
25
|
import ContentViewVersionPromote from '../Promote/ContentViewVersionPromote';
|
25
26
|
import TaskPresenter from '../../components/TaskPresenter/TaskPresenter';
|
26
|
-
import { startPollingTask } from '../../../Tasks/TaskActions';
|
27
|
+
import { startPollingTask, stopPollingTask } from '../../../Tasks/TaskActions';
|
27
28
|
import RemoveCVVersionWizard from './Delete/RemoveCVVersionWizard';
|
28
29
|
import { hasPermission } from '../../helpers';
|
29
30
|
import { pollTaskKey } from '../../../Tasks/helpers';
|
@@ -48,6 +49,7 @@ const ContentViewVersions = ({ cvId, details }) => {
|
|
48
49
|
const [deleteVersion, setDeleteVersion] = useState(false);
|
49
50
|
const [currentStep, setCurrentStep] = useState(1);
|
50
51
|
const { permissions } = details;
|
52
|
+
const pendingTaskSet = useSet([]);
|
51
53
|
const intervals = useSelector(state => selectIntervals(state));
|
52
54
|
|
53
55
|
const columnHeaders = [
|
@@ -66,6 +68,16 @@ const ContentViewVersions = ({ cvId, details }) => {
|
|
66
68
|
[dispatch],
|
67
69
|
);
|
68
70
|
|
71
|
+
useEffect(() => { // eslint-disable-line arrow-body-style
|
72
|
+
return () => {
|
73
|
+
if (pendingTaskSet.size) {
|
74
|
+
pendingTaskSet.forEach(id =>
|
75
|
+
dispatch(stopPollingTask(id)));
|
76
|
+
pendingTaskSet.clear();
|
77
|
+
}
|
78
|
+
};
|
79
|
+
}, [pendingTaskSet, dispatch]);
|
80
|
+
|
69
81
|
const buildCells = useCallback((cvVersion) => {
|
70
82
|
const {
|
71
83
|
version,
|
@@ -98,8 +110,18 @@ const ContentViewVersions = ({ cvId, details }) => {
|
|
98
110
|
} = cvVersion;
|
99
111
|
const { task } = activeHistory[0];
|
100
112
|
const { result } = task || {};
|
113
|
+
|
101
114
|
if (result !== 'error' && !pollIntervals[pollTaskKey(task.id)]) {
|
102
|
-
|
115
|
+
pendingTaskSet.add(task.id);
|
116
|
+
dispatch(startPollingTask(
|
117
|
+
task.id, task,
|
118
|
+
({ data: { pending } = {} }) => {
|
119
|
+
if (!pending) {
|
120
|
+
dispatch(stopPollingTask(task.id));
|
121
|
+
pendingTaskSet.delete(task.id);
|
122
|
+
}
|
123
|
+
},
|
124
|
+
));
|
103
125
|
}
|
104
126
|
|
105
127
|
return [
|
@@ -115,7 +137,7 @@ const ContentViewVersions = ({ cvId, details }) => {
|
|
115
137
|
{ title: '' },
|
116
138
|
{ title: description ? <TableText wrapModifier="truncate">{description}</TableText> : <InactiveText text={__('No description')} /> },
|
117
139
|
];
|
118
|
-
}, [dispatch]);
|
140
|
+
}, [dispatch, pendingTaskSet]);
|
119
141
|
|
120
142
|
useDeepCompareEffect(() => {
|
121
143
|
const buildRows = () => {
|
@@ -17,7 +17,7 @@ import { useSet } from '../../../../../components/Table/TableHooks';
|
|
17
17
|
const RemoveCVVersionWizard = ({
|
18
18
|
cvId, versionIdToRemove, versionNameToRemove,
|
19
19
|
versionEnvironments, show, setIsOpen,
|
20
|
-
currentStep, setCurrentStep, deleteWizard,
|
20
|
+
currentStep, setCurrentStep, deleteWizard, detailsPage,
|
21
21
|
}) => {
|
22
22
|
const [selectedEnvForAK, setSelectedEnvForAK] = useState([]);
|
23
23
|
const [selectedEnvForHost, setSelectedEnvForHost] = useState([]);
|
@@ -133,6 +133,7 @@ const RemoveCVVersionWizard = ({
|
|
133
133
|
selectedEnvForHost,
|
134
134
|
setSelectedEnvForHost,
|
135
135
|
selectedEnvSet,
|
136
|
+
detailsPage,
|
136
137
|
}}
|
137
138
|
>
|
138
139
|
<Wizard
|
@@ -165,12 +166,14 @@ RemoveCVVersionWizard.propTypes = {
|
|
165
166
|
currentStep: PropTypes.number.isRequired,
|
166
167
|
setCurrentStep: PropTypes.func.isRequired,
|
167
168
|
deleteWizard: PropTypes.bool.isRequired,
|
169
|
+
detailsPage: PropTypes.bool,
|
168
170
|
};
|
169
171
|
|
170
172
|
RemoveCVVersionWizard.defaultProps = {
|
171
173
|
versionEnvironments: [],
|
172
174
|
show: false,
|
173
175
|
setIsOpen: null,
|
176
|
+
detailsPage: false,
|
174
177
|
};
|
175
178
|
|
176
179
|
export default RemoveCVVersionWizard;
|
data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVVersionDeleteFinish.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import React, { useState, useContext } from 'react';
|
2
2
|
import { useDispatch, useSelector } from 'react-redux';
|
3
|
+
import { Redirect } from 'react-router-dom';
|
3
4
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
4
5
|
import { STATUS } from 'foremanReact/constants';
|
5
6
|
import { translate as __ } from 'foremanReact/common/I18n';
|
@@ -13,7 +14,8 @@ const CVVersionDeleteFinish = () => {
|
|
13
14
|
cvId, versionIdToRemove, versionEnvironments,
|
14
15
|
setIsOpen, selectedEnvSet,
|
15
16
|
selectedCVForAK, selectedEnvForAK, selectedCVForHosts,
|
16
|
-
selectedEnvForHost, affectedActivationKeys, affectedHosts,
|
17
|
+
selectedEnvForHost, affectedActivationKeys, affectedHosts,
|
18
|
+
deleteFlow, removeDeletionFlow, detailsPage,
|
17
19
|
} = useContext(DeleteContext);
|
18
20
|
const removeCVVersionResponse = useSelector(state =>
|
19
21
|
selectRemoveCVVersionResponse(state, versionIdToRemove, versionEnvironments));
|
@@ -22,14 +24,20 @@ const CVVersionDeleteFinish = () => {
|
|
22
24
|
const removeResolved = removeCVVersionStatus === STATUS.RESOLVED;
|
23
25
|
const dispatch = useDispatch();
|
24
26
|
const [removeDispatched, setRemoveDispatched] = useState(false);
|
27
|
+
const [redirect, setRedirect] = useState(false);
|
25
28
|
const selectedEnv = versionEnvironments.filter(env => selectedEnvSet.has(env.id));
|
26
29
|
|
27
30
|
useDeepCompareEffect(() => {
|
28
31
|
if (removeResolved && removeCVVersionResponse && removeDispatched) {
|
29
|
-
setIsOpen(false);
|
30
32
|
dispatch(getContentViewVersions(cvId));
|
33
|
+
if (detailsPage) {
|
34
|
+
setRedirect(true);
|
35
|
+
} else {
|
36
|
+
setIsOpen(false);
|
37
|
+
}
|
31
38
|
}
|
32
|
-
}, [removeCVVersionResponse, removeResolved, setIsOpen,
|
39
|
+
}, [removeCVVersionResponse, removeResolved, setIsOpen,
|
40
|
+
dispatch, cvId, removeDispatched, detailsPage, setRedirect]);
|
33
41
|
|
34
42
|
/*
|
35
43
|
The remove version from environment API takes the following params :
|
@@ -88,6 +96,9 @@ const CVVersionDeleteFinish = () => {
|
|
88
96
|
selectedEnvForAK, selectedEnvForHost, selectedEnv,
|
89
97
|
removeCVVersionResponse, removeCVVersionStatus, removeDispatched]);
|
90
98
|
|
99
|
+
if (redirect) {
|
100
|
+
return (<Redirect to="/versions" />);
|
101
|
+
}
|
91
102
|
return <Loading loadingText={__('Please wait while the task starts..')} />;
|
92
103
|
};
|
93
104
|
|
data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetails.js
CHANGED
@@ -21,6 +21,7 @@ const ContentViewVersionDetails = ({ cvId, details }) => {
|
|
21
21
|
const { push } = useHistory();
|
22
22
|
const dispatch = useDispatch();
|
23
23
|
const [versionDetails, setVersionDetails] = useState({});
|
24
|
+
const [mounted, setMounted] = useState(true);
|
24
25
|
// Example urls expected:/versions/:id or /versions/:id/repositories.
|
25
26
|
const tab = pathname.split('/')[3];
|
26
27
|
const response = useSelector(state =>
|
@@ -31,10 +32,11 @@ const ContentViewVersionDetails = ({ cvId, details }) => {
|
|
31
32
|
const tableConfigs = getCVVersionTableConfigs({ cvId, versionId });
|
32
33
|
|
33
34
|
useEffect(() => {
|
34
|
-
if (isEmpty(response) && status === STATUS.PENDING) {
|
35
|
+
if (mounted || (isEmpty(response) && status === STATUS.PENDING)) {
|
35
36
|
dispatch(getContentViewVersionDetails(versionId, cvId));
|
36
37
|
}
|
37
|
-
|
38
|
+
return () => { setMounted(false); };
|
39
|
+
}, [dispatch, mounted, setMounted, versionId, cvId, response, status]);
|
38
40
|
|
39
41
|
useDeepCompareEffect(() => {
|
40
42
|
if (loaded) {
|
data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetailsHeader.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useState, useEffect } from 'react';
|
2
|
+
import { useDispatch } from 'react-redux';
|
2
3
|
import PropTypes from 'prop-types';
|
3
4
|
import {
|
4
5
|
GridItem,
|
@@ -10,52 +11,153 @@ import {
|
|
10
11
|
Label,
|
11
12
|
Flex,
|
12
13
|
FlexItem,
|
14
|
+
Dropdown,
|
15
|
+
DropdownItem,
|
16
|
+
DropdownToggle,
|
17
|
+
DropdownPosition,
|
13
18
|
} from '@patternfly/react-core';
|
14
19
|
import { translate as __ } from 'foremanReact/common/I18n';
|
15
20
|
import EditableTextInput from '../../../../../components/EditableTextInput';
|
16
21
|
import { hasPermission } from '../../../helpers';
|
22
|
+
import ContentViewVersionPromote from '../../Promote/ContentViewVersionPromote';
|
23
|
+
import getEnvironmentPaths from '../../../components/EnvironmentPaths/EnvironmentPathActions';
|
24
|
+
import RemoveCVVersionWizard from '../Delete/RemoveCVVersionWizard';
|
17
25
|
|
18
26
|
const ContentViewVersionDetailsHeader = ({
|
19
27
|
versionDetails: {
|
20
|
-
version, description, environments,
|
28
|
+
version, description, environments, content_view_id: cvId, id,
|
21
29
|
},
|
22
30
|
onEdit,
|
23
31
|
details: { permissions },
|
24
|
-
}) =>
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
)
|
32
|
+
}) => {
|
33
|
+
const dispatch = useDispatch();
|
34
|
+
useEffect(
|
35
|
+
() => {
|
36
|
+
dispatch(getEnvironmentPaths());
|
37
|
+
},
|
38
|
+
[dispatch],
|
39
|
+
);
|
40
|
+
const [dropdownOpen, setDropdownOpen] = useState(false);
|
41
|
+
const [promoting, setPromoting] = useState(false);
|
42
|
+
const [removingFromEnv, setRemovingFromEnv] = useState(false);
|
43
|
+
const [currentStep, setCurrentStep] = useState(1);
|
44
|
+
const [deleteVersion, setDeleteVersion] = useState(false);
|
45
|
+
const dropDownItems = [
|
46
|
+
<DropdownItem
|
47
|
+
key="promote"
|
48
|
+
onClick={() => {
|
49
|
+
setPromoting(true);
|
50
|
+
}}
|
51
|
+
>
|
52
|
+
{__('Promote')}
|
53
|
+
</DropdownItem>,
|
54
|
+
<DropdownItem
|
55
|
+
key="remove"
|
56
|
+
onClick={() => {
|
57
|
+
setRemovingFromEnv(true);
|
58
|
+
}}
|
59
|
+
>
|
60
|
+
{__('Remove from environment')}
|
61
|
+
</DropdownItem>,
|
62
|
+
<DropdownItem
|
63
|
+
key="delete"
|
64
|
+
onClick={() => {
|
65
|
+
setCurrentStep(1);
|
66
|
+
setDeleteVersion(true);
|
67
|
+
setRemovingFromEnv(true);
|
68
|
+
}}
|
69
|
+
>
|
70
|
+
{__('Delete')}
|
71
|
+
</DropdownItem>,
|
72
|
+
];
|
73
|
+
|
74
|
+
return (
|
75
|
+
<>
|
76
|
+
<GridItem span={10}>
|
77
|
+
<TextContent>
|
78
|
+
<Text component={TextVariants.h2}>{__('Version ')}{version}</Text>
|
79
|
+
</TextContent>
|
80
|
+
</GridItem>
|
81
|
+
<GridItem span={2} style={{ display: 'flex' }}>
|
82
|
+
<Dropdown
|
83
|
+
aria-label="version-action-dropdown"
|
84
|
+
position={DropdownPosition.right}
|
85
|
+
style={{ marginLeft: 'auto' }}
|
86
|
+
toggle={
|
87
|
+
<DropdownToggle
|
88
|
+
onToggle={setDropdownOpen}
|
89
|
+
id="toggle-id"
|
90
|
+
>
|
91
|
+
{__('Actions')}
|
92
|
+
</DropdownToggle>
|
93
|
+
}
|
94
|
+
isOpen={dropdownOpen}
|
95
|
+
dropdownItems={dropDownItems}
|
96
|
+
/>
|
97
|
+
</GridItem>
|
98
|
+
<GridItem className="content-view-header-content" span={12}>
|
99
|
+
<TextContent>
|
100
|
+
<TextList component={TextListVariants.dl}>
|
101
|
+
<EditableTextInput
|
102
|
+
key={description} // This fixes a render issue with the initial value
|
103
|
+
textArea
|
104
|
+
label={__('Description')}
|
105
|
+
attribute="description"
|
106
|
+
placeholder={__('No description')}
|
107
|
+
onEdit={onEdit}
|
108
|
+
disabled={!hasPermission(permissions, 'edit_content_views')}
|
109
|
+
value={description}
|
110
|
+
/>
|
111
|
+
</TextList>
|
112
|
+
</TextContent>
|
113
|
+
<Flex>
|
114
|
+
{environments?.map(({ name, id: envId }) =>
|
115
|
+
<FlexItem key={name}><Label isTruncated color="purple" href={`/lifecycle_environments/${envId}`}>{name}</Label></FlexItem>)}
|
116
|
+
</Flex>
|
117
|
+
</GridItem>
|
118
|
+
{promoting &&
|
119
|
+
<ContentViewVersionPromote
|
120
|
+
cvId={cvId}
|
121
|
+
versionIdToPromote={id}
|
122
|
+
versionNameToPromote={version}
|
123
|
+
versionEnvironments={environments}
|
124
|
+
setIsOpen={setPromoting}
|
125
|
+
detailsPage
|
126
|
+
aria-label="promote_content_view_modal"
|
127
|
+
/>
|
128
|
+
}
|
129
|
+
{removingFromEnv &&
|
130
|
+
<RemoveCVVersionWizard
|
131
|
+
cvId={cvId}
|
132
|
+
versionIdToRemove={id}
|
133
|
+
versionNameToRemove={version}
|
134
|
+
versionEnvironments={environments}
|
135
|
+
show={removingFromEnv}
|
136
|
+
setIsOpen={setRemovingFromEnv}
|
137
|
+
currentStep={currentStep}
|
138
|
+
setCurrentStep={setCurrentStep}
|
139
|
+
deleteWizard={deleteVersion}
|
140
|
+
detailsPage
|
141
|
+
aria-label="remove_content_view_version_modal"
|
142
|
+
/>
|
143
|
+
}
|
144
|
+
</>
|
145
|
+
);
|
146
|
+
};
|
53
147
|
|
54
148
|
ContentViewVersionDetailsHeader.propTypes = {
|
55
149
|
versionDetails: PropTypes.shape({
|
56
150
|
version: PropTypes.string,
|
57
151
|
environments: PropTypes.arrayOf(PropTypes.shape({})),
|
58
152
|
description: PropTypes.string,
|
153
|
+
content_view_id: PropTypes.oneOfType([
|
154
|
+
PropTypes.string,
|
155
|
+
PropTypes.number,
|
156
|
+
]),
|
157
|
+
id: PropTypes.oneOfType([
|
158
|
+
PropTypes.string,
|
159
|
+
PropTypes.number,
|
160
|
+
]),
|
59
161
|
}).isRequired,
|
60
162
|
onEdit: PropTypes.func.isRequired,
|
61
163
|
details: PropTypes.shape({
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
2
2
|
import { renderWithRedux, patientlyWaitFor } from 'react-testing-lib-wrapper';
|
3
3
|
import { Route } from 'react-router-dom';
|
4
4
|
import { head, last } from 'lodash';
|
5
|
-
import { nockInstance, assertNockRequest, mockSetting, mockAutocomplete } from '../../../../../../test-utils/nockWrapper';
|
5
|
+
import nock, { nockInstance, assertNockRequest, mockSetting, mockAutocomplete } from '../../../../../../test-utils/nockWrapper';
|
6
6
|
import api from '../../../../../../services/api';
|
7
7
|
import { cvVersionDetailsKey } from '../../../../ContentViewsConstants';
|
8
8
|
import ContentViewVersionDetails from '../ContentViewVersionDetails';
|
@@ -20,12 +20,15 @@ import ContentViewVersionModuleStreamsData from './ContentViewVersionModuleStrea
|
|
20
20
|
import ContentViewVersionDebPackagesData from './ContentViewVersionDebPackages.fixtures.json';
|
21
21
|
import ContentViewVersionAnsibleCollectionsData from './ContentViewVersionAnsibleCollections.fixtures.json';
|
22
22
|
import ContentViewVersionDockerTagsData from './ContentViewVersionDockerTags.fixtures.json';
|
23
|
+
import environmentPathsData from '../../Delete/__tests__/versionRemoveEnvPaths.fixtures';
|
23
24
|
|
24
25
|
// This changes the api count value so that only the specified tab will show.
|
25
26
|
const getTabSpecificData = key => ({
|
26
27
|
...ContentViewVersionDetailsData,
|
27
28
|
[key]: ContentViewVersionDetailsCounts[key],
|
28
29
|
});
|
30
|
+
let envScope;
|
31
|
+
const environmentPathsPath = api.getApiUrl('/organizations/1/environments/paths');
|
29
32
|
|
30
33
|
const withCVRoute = component => <Route path="/versions/:versionId([0-9]+)">{component}</Route>;
|
31
34
|
|
@@ -39,11 +42,23 @@ const renderOptions = {
|
|
39
42
|
},
|
40
43
|
};
|
41
44
|
|
45
|
+
beforeEach(() => {
|
46
|
+
envScope = nockInstance
|
47
|
+
.get(environmentPathsPath)
|
48
|
+
.query(true)
|
49
|
+
.reply(200, environmentPathsData);
|
50
|
+
});
|
51
|
+
|
52
|
+
afterEach(() => {
|
53
|
+
assertNockRequest(envScope);
|
54
|
+
nock.cleanAll();
|
55
|
+
});
|
42
56
|
// This is written separately, as the autocomplete/search scopes are not needed.
|
43
57
|
test('Can show versions details - Components Tab', async (done) => {
|
44
58
|
const { version } = ContentViewVersionDetailsData;
|
45
59
|
const scope = nockInstance
|
46
60
|
.get(cvVersions)
|
61
|
+
.times(2)
|
47
62
|
.query(true)
|
48
63
|
.reply(200, getTabSpecificData('component_view_count'));
|
49
64
|
|
@@ -72,6 +87,7 @@ test('Can show versions details - Components Tab', async (done) => {
|
|
72
87
|
.content_view.name)).toBeTruthy();
|
73
88
|
});
|
74
89
|
|
90
|
+
assertNockRequest(scope);
|
75
91
|
assertNockRequest(scope);
|
76
92
|
assertNockRequest(componentScope, done);
|
77
93
|
});
|
@@ -188,6 +204,7 @@ testConfig.forEach(({
|
|
188
204
|
|
189
205
|
const scope = nockInstance
|
190
206
|
.get(cvVersions)
|
207
|
+
.times(2)
|
191
208
|
.query(true)
|
192
209
|
.reply(200, getTabSpecificData(countKey));
|
193
210
|
|
@@ -219,6 +236,7 @@ testConfig.forEach(({
|
|
219
236
|
assertNockRequest(autocompleteScope);
|
220
237
|
assertNockRequest(searchDelayScope);
|
221
238
|
assertNockRequest(autoSearchScope);
|
239
|
+
assertNockRequest(scope);
|
222
240
|
assertNockRequest(tabScope);
|
223
241
|
assertNockRequest(scope, done);
|
224
242
|
}));
|
@@ -239,6 +257,7 @@ test('Can change repository selector', async (done) => {
|
|
239
257
|
const scope = nockInstance
|
240
258
|
.get(cvVersions)
|
241
259
|
.query(true)
|
260
|
+
.times(2)
|
242
261
|
.reply(200, {
|
243
262
|
...getTabSpecificData(countKey),
|
244
263
|
repositories: ContentViewVersionDetailsCounts.repositories,
|
@@ -284,6 +303,7 @@ test('Can change repository selector', async (done) => {
|
|
284
303
|
assertNockRequest(autocompleteScope);
|
285
304
|
assertNockRequest(searchDelayScope);
|
286
305
|
assertNockRequest(autoSearchScope);
|
306
|
+
assertNockRequest(scope);
|
287
307
|
assertNockRequest(tabScope);
|
288
308
|
assertNockRequest(scope, done);
|
289
309
|
});
|
@@ -2,16 +2,20 @@ import React from 'react';
|
|
2
2
|
import { renderWithRedux, patientlyWaitFor } from 'react-testing-lib-wrapper';
|
3
3
|
import { Route } from 'react-router-dom';
|
4
4
|
|
5
|
-
import { nockInstance, assertNockRequest } from '../../../../../../test-utils/nockWrapper';
|
5
|
+
import nock, { nockInstance, assertNockRequest } from '../../../../../../test-utils/nockWrapper';
|
6
6
|
import api from '../../../../../../services/api';
|
7
7
|
import { cvVersionDetailsKey } from '../../../../ContentViewsConstants';
|
8
8
|
import ContentViewVersionDetails from '../ContentViewVersionDetails';
|
9
9
|
import ContentViewVersionDetailsEmptyData from './ContentViewVersionDetails.fixtures.json';
|
10
10
|
import cvDetailData from '../../../../__tests__/mockDetails.fixtures.json';
|
11
|
+
import environmentPathsData from '../../Delete/__tests__/versionRemoveEnvPaths.fixtures';
|
11
12
|
|
12
13
|
const withCVRoute = component =>
|
13
14
|
<Route path="/versions/:versionId([0-9]+)">{component}</Route>;
|
14
15
|
const cvVersions = api.getApiUrl('/content_view_versions/73');
|
16
|
+
let envScope;
|
17
|
+
let versionScope;
|
18
|
+
const environmentPathsPath = api.getApiUrl('/organizations/1/environments/paths');
|
15
19
|
|
16
20
|
const renderOptions = {
|
17
21
|
apiNamespace: cvVersionDetailsKey(3, 73),
|
@@ -21,6 +25,23 @@ const renderOptions = {
|
|
21
25
|
},
|
22
26
|
};
|
23
27
|
|
28
|
+
beforeEach(() => {
|
29
|
+
envScope = nockInstance
|
30
|
+
.get(environmentPathsPath)
|
31
|
+
.query(true)
|
32
|
+
.reply(200, environmentPathsData);
|
33
|
+
versionScope = nockInstance
|
34
|
+
.get(cvVersions)
|
35
|
+
.query(true)
|
36
|
+
.reply(200, ContentViewVersionDetailsEmptyData);
|
37
|
+
});
|
38
|
+
|
39
|
+
afterEach(() => {
|
40
|
+
assertNockRequest(envScope);
|
41
|
+
assertNockRequest(versionScope);
|
42
|
+
nock.cleanAll();
|
43
|
+
});
|
44
|
+
|
24
45
|
test('Can show versions detail header', async (done) => {
|
25
46
|
const { version } = ContentViewVersionDetailsEmptyData;
|
26
47
|
const scope = nockInstance
|
@@ -31,12 +31,13 @@ export const startPollingTasks = (key, taskSearchParams = {}) =>
|
|
31
31
|
|
32
32
|
export const stopPollingTasks = key => stopInterval(bulkSearchKey(key));
|
33
33
|
|
34
|
-
const getTask = (key, task) => get({
|
34
|
+
const getTask = (key, task, handleSuccess) => get({
|
35
35
|
key,
|
36
36
|
url: `${foremanTasksApi.baseApiPath}/tasks/${task.id}`,
|
37
|
+
handleSuccess,
|
37
38
|
});
|
38
39
|
|
39
|
-
export const startPollingTask = (key, task) =>
|
40
|
-
withInterval(getTask(pollTaskKey(key), task));
|
40
|
+
export const startPollingTask = (key, task, handleSuccess) =>
|
41
|
+
withInterval(getTask(pollTaskKey(key), task, handleSuccess));
|
41
42
|
|
42
43
|
export const stopPollingTask = key => stopInterval(pollTaskKey(key));
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: katello
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.3.0.
|
4
|
+
version: 4.3.0.rc4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- N/A
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -1572,6 +1572,7 @@ files:
|
|
1572
1572
|
- app/views/dashboard/_subscription_status_widget.html.erb
|
1573
1573
|
- app/views/dashboard/_subscription_widget.html.erb
|
1574
1574
|
- app/views/dashboard/_sync_widget.html.erb
|
1575
|
+
- app/views/foreman/job_templates/change_content_source.erb
|
1575
1576
|
- app/views/foreman/job_templates/install_errata.erb
|
1576
1577
|
- app/views/foreman/job_templates/install_errata_-_katello_ansible_default.erb
|
1577
1578
|
- app/views/foreman/job_templates/install_group.erb
|