katello 4.5.0.rc1 → 4.5.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/assets/javascripts/katello/hosts/activation_key_edit.js +9 -2
- data/app/controllers/katello/api/registry/registry_proxies_controller.rb +3 -0
- data/app/controllers/katello/api/v2/alternate_content_sources_bulk_actions_controller.rb +44 -0
- data/app/controllers/katello/api/v2/alternate_content_sources_controller.rb +29 -6
- data/app/controllers/katello/api/v2/content_view_components_controller.rb +1 -1
- data/app/controllers/katello/api/v2/content_view_repositories_controller.rb +1 -1
- data/app/controllers/katello/api/v2/repositories_controller.rb +2 -8
- data/app/lib/actions/katello/alternate_content_source/refresh.rb +27 -0
- data/app/lib/actions/katello/cdn_configuration/update.rb +1 -1
- data/app/lib/actions/katello/content_view/publish.rb +1 -1
- data/app/lib/actions/katello/organization/manifest_refresh.rb +1 -1
- data/app/lib/actions/pulp3/alternate_content_source/delete.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/delete_remote.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/refresh.rb +23 -0
- data/app/lib/actions/pulp3/alternate_content_source/update.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/update_remote.rb +2 -2
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/create.rb +0 -2
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/refresh.rb +15 -0
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/update.rb +0 -2
- data/app/lib/actions/pulp3/repository/refresh_distribution.rb +1 -4
- data/app/lib/actions/pulp3/repository/save_artifact.rb +1 -1
- data/app/lib/actions/pulp3/repository/save_distribution_references.rb +0 -2
- data/app/models/katello/alternate_content_source.rb +5 -0
- data/app/services/katello/pulp3/alternate_content_source.rb +6 -0
- data/app/services/katello/pulp3/content_view_version/metadata_map.rb +1 -1
- data/app/services/katello/pulp3/repository.rb +29 -1
- data/app/views/katello/api/v2/alternate_content_sources/base.json.rabl +10 -1
- data/app/views/katello/api/v2/content_facet/show.json.rabl +12 -0
- data/app/views/katello/api/v2/repository_sets/show.json.rabl +4 -0
- data/config/routes/api/v2.rb +16 -4
- data/db/migrate/20220303160220_remove_duplicate_errata.rb +1 -1
- data/db/migrate/20220428203334_add_last_refreshed_to_katello_alternate_content_sources.rb +5 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +1 -1
- data/lib/katello/permission_creator.rb +4 -2
- data/lib/katello/tasks/refresh_alternate_content_sources.rake +10 -0
- data/lib/katello/version.rb +1 -1
- data/webpack/components/Bookmark/index.js +22 -14
- data/webpack/components/Search/Search.js +4 -0
- data/webpack/components/Table/MainTable.scss +5 -1
- data/webpack/components/Table/TableWrapper.js +5 -1
- data/webpack/components/TypeAhead/TypeAhead.js +4 -0
- data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.js +2 -0
- data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ChangeHostCVModal.js +2 -8
- data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ContentViewDetailsCard.js +41 -11
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsActions.js +2 -2
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsCard.js +32 -13
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/__tests__/hostCollectionsCard.test.js +8 -0
- data/webpack/components/extensions/HostDetails/DetailsTabCards/RecentCommunicationCardExtensions.js +37 -0
- data/webpack/components/extensions/HostDetails/HostDetailsActions.js +11 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/SecondaryTabsRoutes.js +4 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/constants.js +2 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/index.js +6 -1
- data/webpack/components/extensions/HostDetails/Tabs/ErrataTab/ErrataTab.js +120 -51
- data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsTab.js +71 -37
- data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackageInstallModal.js +4 -3
- data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +117 -40
- data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionActions.js +25 -3
- data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionHooks.js +85 -0
- data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsTab.js +87 -33
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/EnableTracerModal.js +14 -7
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/HostTracesActions.js +2 -1
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/TracesEnabler.js +104 -0
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/TracesTab.js +92 -51
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/errataTab.test.js +13 -23
- data/webpack/components/extensions/HostDetails/Tabs/{ModuleStreamsTab/__tests__/modules.fixtures.json → __tests__/moduleStreams.fixtures.json} +0 -0
- data/webpack/components/extensions/HostDetails/Tabs/{ModuleStreamsTab/__tests__ → __tests__}/moduleStreamsTab.test.js +13 -6
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/packageInstallModal.test.js +21 -15
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +8 -0
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySets.fixtures.json +4 -1
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySetsTab.test.js +26 -0
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/tracesTab.test.js +7 -4
- data/webpack/components/extensions/HostDetails/hostDetailsHelpers.js +18 -0
- data/webpack/global_index.js +2 -2
- data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -1
- data/webpack/scenes/AlternateContentSources/ACSActions.js +13 -1
- data/webpack/scenes/AlternateContentSources/ACSConstants.js +14 -0
- data/webpack/scenes/AlternateContentSources/ACSSelectors.js +10 -1
- data/webpack/scenes/AlternateContentSources/Create/ACSCreateContext.js +4 -0
- data/webpack/scenes/AlternateContentSources/Create/ACSCreateWizard.js +160 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSCreateFinish.js +79 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSCredentials.js +199 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSReview.js +104 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSSmartProxies.js +41 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/AcsUrlPaths.js +71 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/NameACS.js +57 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/SelectSource.js +77 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreate.test.js +149 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreateData.fixtures.json +3 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/contentCredentials.fixtures.json +69 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/smartProxy.fixtures.json +65 -0
- data/webpack/scenes/AlternateContentSources/MainTable/ACSTable.js +33 -23
- data/webpack/scenes/ContentCredentials/ContentCredentialSelectors.js +4 -1
- data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +2 -2
- data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +1 -1
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +1 -1
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewBulkAddModal.js +2 -2
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +4 -4
- data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +1 -1
- data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +4 -4
- data/webpack/scenes/ContentViews/components/ContentViewIcon.js +1 -1
- data/webpack/scenes/ContentViews/components/ContentViewsCounter.js +1 -1
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +1 -1
- data/webpack/scenes/ContentViews/expansions/DetailsExpansion.js +2 -2
- data/webpack/scenes/ContentViews/expansions/RelatedContentViewComponentsModal.js +2 -2
- data/webpack/scenes/ContentViews/expansions/__tests__/contentViewComponentsModal.test.js +1 -1
- data/webpack/scenes/RedHatRepositories/components/Search.js +4 -4
- data/webpack/scenes/SmartProxy/SmartProxyContentActions.js +9 -2
- data/webpack/scenes/SmartProxy/SmartProxyContentConstants.js +1 -1
- data/webpack/scenes/SmartProxy/SmartProxyContentSelectors.js +10 -1
- data/webpack/scenes/Tasks/helpers.js +30 -3
- metadata +34 -14
- data/db/seeds.d/107-enable_dynflow.rb +0 -8
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/EnableTracerEmptyState.js +0 -42
@@ -0,0 +1,149 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import * as reactRedux from 'react-redux';
|
3
|
+
import { renderWithRedux, patientlyWaitFor, fireEvent } from 'react-testing-lib-wrapper';
|
4
|
+
import api, { foremanApi } from '../../../../services/api';
|
5
|
+
import nock, { nockInstance, assertNockRequest, mockAutocomplete, mockSetting } from '../../../../test-utils/nockWrapper';
|
6
|
+
import ACSTable from '../../MainTable/ACSTable';
|
7
|
+
import contentCredentialResult from './contentCredentials.fixtures';
|
8
|
+
import smartProxyResult from './smartProxy.fixtures';
|
9
|
+
|
10
|
+
const ACSIndexPath = api.getApiUrl('/alternate_content_sources');
|
11
|
+
const ACSCreatePath = api.getApiUrl('/alternate_content_sources');
|
12
|
+
const contentCredentialPath = api.getApiUrl('/content_credentials');
|
13
|
+
const smartProxyPath = foremanApi.getApiUrl('/smart_proxies');
|
14
|
+
const autocompleteUrl = '/alternate_content_sources/auto_complete_search';
|
15
|
+
|
16
|
+
const createACSDetails = {
|
17
|
+
upstream_username: 'username',
|
18
|
+
upstream_password: 'password',
|
19
|
+
name: 'acs_test',
|
20
|
+
description: '',
|
21
|
+
base_url: 'https://test_url.com/',
|
22
|
+
subpaths: ['test/repo1/', 'test/repo2/'],
|
23
|
+
smart_proxy_names: ['centos7-katello-devel-stable.example.com'],
|
24
|
+
content_type: 'yum',
|
25
|
+
alternate_content_source_type: 'custom',
|
26
|
+
verify_ssl: false,
|
27
|
+
ssl_ca_cert_id: '',
|
28
|
+
};
|
29
|
+
|
30
|
+
const noResults = {
|
31
|
+
total: 0,
|
32
|
+
subtotal: 0,
|
33
|
+
page: 1,
|
34
|
+
per_page: 20,
|
35
|
+
results: [],
|
36
|
+
};
|
37
|
+
|
38
|
+
let searchDelayScope;
|
39
|
+
let autoSearchScope;
|
40
|
+
beforeEach(() => {
|
41
|
+
searchDelayScope = mockSetting(nockInstance, 'autosearch_delay', 0);
|
42
|
+
autoSearchScope = mockSetting(nockInstance, 'autosearch_while_typing');
|
43
|
+
});
|
44
|
+
|
45
|
+
afterEach(() => {
|
46
|
+
nock.cleanAll();
|
47
|
+
assertNockRequest(searchDelayScope);
|
48
|
+
assertNockRequest(autoSearchScope);
|
49
|
+
});
|
50
|
+
|
51
|
+
test('Can show add ACS button', async (done) => {
|
52
|
+
const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl);
|
53
|
+
const scope = nockInstance
|
54
|
+
.get(ACSIndexPath)
|
55
|
+
.query(true)
|
56
|
+
.reply(200, noResults);
|
57
|
+
|
58
|
+
const { queryByText } = renderWithRedux(<ACSTable />);
|
59
|
+
|
60
|
+
expect(queryByText("You currently don't have any alternate content sources.")).toBeNull();
|
61
|
+
await patientlyWaitFor(() => expect(queryByText("You currently don't have any alternate content sources.")).toBeInTheDocument());
|
62
|
+
expect(queryByText('Add source')).toBeInTheDocument();
|
63
|
+
assertNockRequest(autocompleteScope);
|
64
|
+
assertNockRequest(scope, done);
|
65
|
+
});
|
66
|
+
|
67
|
+
test('Can display create wizard and create ACS', async (done) => {
|
68
|
+
const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl);
|
69
|
+
const scope = nockInstance
|
70
|
+
.get(ACSIndexPath)
|
71
|
+
.query(true)
|
72
|
+
.reply(200, noResults);
|
73
|
+
|
74
|
+
const contentCredentialScope = nockInstance
|
75
|
+
.get(contentCredentialPath)
|
76
|
+
.query(true)
|
77
|
+
.reply(200, contentCredentialResult);
|
78
|
+
|
79
|
+
const smartProxyScope = nockInstance
|
80
|
+
.get(smartProxyPath)
|
81
|
+
.query(true)
|
82
|
+
.reply(200, smartProxyResult);
|
83
|
+
|
84
|
+
const createScope = nockInstance
|
85
|
+
.post(ACSCreatePath, createACSDetails)
|
86
|
+
.reply(201, { id: 22 });
|
87
|
+
|
88
|
+
const {
|
89
|
+
getByLabelText, getByText, getAllByRole, queryByText,
|
90
|
+
} = renderWithRedux(<ACSTable />);
|
91
|
+
|
92
|
+
expect(queryByText("You currently don't have any alternate content sources.")).toBeNull();
|
93
|
+
await patientlyWaitFor(() => expect(queryByText("You currently don't have any alternate content sources.")).toBeInTheDocument());
|
94
|
+
expect(queryByText('Add source')).toBeInTheDocument();
|
95
|
+
fireEvent.click(getByText('Add source'));
|
96
|
+
|
97
|
+
// First step: Select source
|
98
|
+
await patientlyWaitFor(() => {
|
99
|
+
expect(getByText('Add an alternate content source')).toBeInTheDocument();
|
100
|
+
expect(queryByText('Indicate the source type.')).toBeInTheDocument();
|
101
|
+
});
|
102
|
+
|
103
|
+
// Choose ACS type, content_type defaults to yum
|
104
|
+
fireEvent.click(getByText('Custom'));
|
105
|
+
|
106
|
+
// Go to next step: Name source
|
107
|
+
fireEvent.click(getByText('Next'));
|
108
|
+
|
109
|
+
await patientlyWaitFor(() => {
|
110
|
+
expect(getByText('Enter a name for your source.')).toBeInTheDocument();
|
111
|
+
});
|
112
|
+
// Enter Name
|
113
|
+
fireEvent.change(getByLabelText('acs_name_field'), { target: { value: 'acs_test' } });
|
114
|
+
|
115
|
+
// Mock smart proxy selector to go to next page
|
116
|
+
const useSelectorMock = jest.spyOn(reactRedux, 'useSelector');
|
117
|
+
useSelectorMock.mockReturnValue(smartProxyResult);
|
118
|
+
fireEvent.click(getByText('Next'));
|
119
|
+
fireEvent.click(getByLabelText('Add all'));
|
120
|
+
|
121
|
+
// Go to URL and subpath step
|
122
|
+
fireEvent.click(getByText('Next'));
|
123
|
+
|
124
|
+
fireEvent.change(getByLabelText('acs_base_url_field'), { target: { value: 'https://test_url.com/' } });
|
125
|
+
expect(getByLabelText('acs_base_url_field')).toHaveAttribute('value', 'https://test_url.com/');
|
126
|
+
fireEvent.change(getByLabelText('acs_subpath_field'), { target: { value: 'test/repo1/,test/repo2/' } });
|
127
|
+
|
128
|
+
// Mock content credential data
|
129
|
+
useSelectorMock.mockReturnValue(contentCredentialResult.results);
|
130
|
+
fireEvent.click(getByText('Next'));
|
131
|
+
const manualAuthRadio = getAllByRole('radio', { name: 'Manual authentication' })[0];
|
132
|
+
fireEvent.click(manualAuthRadio);
|
133
|
+
await patientlyWaitFor(() => {
|
134
|
+
expect(getByText('Username')).toBeInTheDocument();
|
135
|
+
expect(getByText('Password')).toBeInTheDocument();
|
136
|
+
});
|
137
|
+
fireEvent.change(getByLabelText('acs_username_field'), { target: { value: 'username' } });
|
138
|
+
fireEvent.change(getByLabelText('acs_password_field'), { target: { value: 'password' } });
|
139
|
+
|
140
|
+
fireEvent.click(getByText('Next'));
|
141
|
+
fireEvent.click(getByText('Add'));
|
142
|
+
|
143
|
+
useSelectorMock.mockClear();
|
144
|
+
assertNockRequest(autocompleteScope);
|
145
|
+
assertNockRequest(scope);
|
146
|
+
assertNockRequest(contentCredentialScope);
|
147
|
+
assertNockRequest(smartProxyScope);
|
148
|
+
assertNockRequest(createScope, done);
|
149
|
+
});
|
@@ -0,0 +1,69 @@
|
|
1
|
+
{
|
2
|
+
"total": 2,
|
3
|
+
"subtotal": 2,
|
4
|
+
"selectable": 2,
|
5
|
+
"page": 1,
|
6
|
+
"per_page": 20,
|
7
|
+
"error": null,
|
8
|
+
"search": null,
|
9
|
+
"sort": {
|
10
|
+
"by": "name",
|
11
|
+
"order": "asc"
|
12
|
+
},
|
13
|
+
"results": [
|
14
|
+
{
|
15
|
+
"name": "test",
|
16
|
+
"content_type": "cert",
|
17
|
+
"content": "abcdef",
|
18
|
+
"id": 1,
|
19
|
+
"organization_id": 1,
|
20
|
+
"organization": {
|
21
|
+
"name": "Default Organization",
|
22
|
+
"label": "Default_Organization",
|
23
|
+
"id": 1
|
24
|
+
},
|
25
|
+
"created_at": "2022-05-02 16:21:58 -0400",
|
26
|
+
"updated_at": "2022-05-02 16:21:58 -0400",
|
27
|
+
"gpg_key_products": [],
|
28
|
+
"gpg_key_repos": [],
|
29
|
+
"ssl_ca_products": [],
|
30
|
+
"ssl_ca_root_repos": [],
|
31
|
+
"ssl_client_products": [],
|
32
|
+
"ssl_client_root_repos": [],
|
33
|
+
"ssl_key_products": [],
|
34
|
+
"ssl_key_root_repos": [],
|
35
|
+
"permissions": {
|
36
|
+
"view_content_credenials": true,
|
37
|
+
"edit_content_credenials": true,
|
38
|
+
"destroy_content_credenials": true
|
39
|
+
}
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"name": "test1",
|
43
|
+
"content_type": "gpg_key",
|
44
|
+
"content": "abcdef",
|
45
|
+
"id": 2,
|
46
|
+
"organization_id": 1,
|
47
|
+
"organization": {
|
48
|
+
"name": "Default Organization",
|
49
|
+
"label": "Default_Organization",
|
50
|
+
"id": 1
|
51
|
+
},
|
52
|
+
"created_at": "2022-05-02 16:22:30 -0400",
|
53
|
+
"updated_at": "2022-05-02 16:22:30 -0400",
|
54
|
+
"gpg_key_products": [],
|
55
|
+
"gpg_key_repos": [],
|
56
|
+
"ssl_ca_products": [],
|
57
|
+
"ssl_ca_root_repos": [],
|
58
|
+
"ssl_client_products": [],
|
59
|
+
"ssl_client_root_repos": [],
|
60
|
+
"ssl_key_products": [],
|
61
|
+
"ssl_key_root_repos": [],
|
62
|
+
"permissions": {
|
63
|
+
"view_content_credenials": true,
|
64
|
+
"edit_content_credenials": true,
|
65
|
+
"destroy_content_credenials": true
|
66
|
+
}
|
67
|
+
}
|
68
|
+
]
|
69
|
+
}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
{
|
2
|
+
"total": 1,
|
3
|
+
"subtotal": 1,
|
4
|
+
"page": 1,
|
5
|
+
"per_page": 20,
|
6
|
+
"search": null,
|
7
|
+
"sort": {
|
8
|
+
"by": null,
|
9
|
+
"order": null
|
10
|
+
},
|
11
|
+
"results": [
|
12
|
+
{
|
13
|
+
"created_at": "2022-05-02 10:17:57 -0400",
|
14
|
+
"updated_at": "2022-05-02 10:24:21 -0400",
|
15
|
+
"hosts_count": 0,
|
16
|
+
"name": "centos7-katello-devel-stable.example.com",
|
17
|
+
"id": 1,
|
18
|
+
"url": "https://centos7-katello-devel-stable.example.com:9090",
|
19
|
+
"remote_execution_pubkey": "foreman-proxy@centos7-katello-devel-stable.example.com",
|
20
|
+
"download_policy": "on_demand",
|
21
|
+
"supported_pulp_types": [
|
22
|
+
"ansible_collection",
|
23
|
+
"deb",
|
24
|
+
"docker",
|
25
|
+
"file",
|
26
|
+
"python",
|
27
|
+
"yum"
|
28
|
+
],
|
29
|
+
"features": [
|
30
|
+
{
|
31
|
+
"capabilities": [
|
32
|
+
"single",
|
33
|
+
"ssh"
|
34
|
+
],
|
35
|
+
"name": "Dynflow",
|
36
|
+
"id": 17
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"capabilities": [],
|
40
|
+
"name": "SSH",
|
41
|
+
"id": 18
|
42
|
+
},
|
43
|
+
{
|
44
|
+
"capabilities": [
|
45
|
+
"ansible",
|
46
|
+
"certguard",
|
47
|
+
"container",
|
48
|
+
"core",
|
49
|
+
"deb",
|
50
|
+
"file",
|
51
|
+
"python",
|
52
|
+
"rpm"
|
53
|
+
],
|
54
|
+
"name": "Pulpcore",
|
55
|
+
"id": 3
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"capabilities": [],
|
59
|
+
"name": "Logs",
|
60
|
+
"id": 13
|
61
|
+
}
|
62
|
+
]
|
63
|
+
}
|
64
|
+
]
|
65
|
+
}
|
@@ -9,19 +9,22 @@ import {
|
|
9
9
|
selectAlternateContentSourcesStatus,
|
10
10
|
} from '../ACSSelectors';
|
11
11
|
import { useTableSort } from '../../../components/Table/TableHooks';
|
12
|
-
import getAlternateContentSources, { deleteACS } from '../ACSActions';
|
12
|
+
import getAlternateContentSources, { deleteACS, refreshACS } from '../ACSActions';
|
13
|
+
import ACSCreateWizard from '../Create/ACSCreateWizard';
|
14
|
+
import LastSync from '../../ContentViews/Details/Repositories/LastSync';
|
13
15
|
|
14
16
|
const ACSTable = () => {
|
15
17
|
const response = useSelector(selectAlternateContentSources);
|
16
18
|
const status = useSelector(selectAlternateContentSourcesStatus);
|
17
19
|
const error = useSelector(selectAlternateContentSourcesError);
|
18
20
|
const [searchQuery, updateSearchQuery] = useState('');
|
21
|
+
const [isCreateWizardOpen, setIsCreateWizardOpen] = useState(false);
|
19
22
|
const dispatch = useDispatch();
|
20
23
|
const { results, ...metadata } = response;
|
21
24
|
const columnHeaders = [
|
22
25
|
__('Name'),
|
23
26
|
__('Type'),
|
24
|
-
__('
|
27
|
+
__('Last Refresh'),
|
25
28
|
];
|
26
29
|
|
27
30
|
const COLUMNS_TO_SORT_PARAMS = {
|
@@ -47,37 +50,35 @@ const ACSTable = () => {
|
|
47
50
|
[apiSortParams],
|
48
51
|
);
|
49
52
|
|
50
|
-
// const createButtonOnclick = () => {
|
51
|
-
// let params = {
|
52
|
-
// name: `test_acs-${Math.random()}`,
|
53
|
-
// label: `test_acs-${Math.random()}`,
|
54
|
-
// base_url: "https://fixtures.pulpproject.org/",
|
55
|
-
// subpaths: ["file/", "package/"],
|
56
|
-
// smart_proxy_ids:[1],
|
57
|
-
// content_type:"yum",
|
58
|
-
// alternate_content_source_type:"custom"
|
59
|
-
// };
|
60
|
-
// dispatch(createACS(params));
|
61
|
-
// };
|
62
|
-
|
63
53
|
const onDelete = (id) => {
|
64
54
|
dispatch(deleteACS(id, () =>
|
65
55
|
dispatch(getAlternateContentSources())));
|
66
56
|
};
|
67
57
|
|
58
|
+
const onRefresh = (id) => {
|
59
|
+
dispatch(refreshACS(id, () =>
|
60
|
+
dispatch(getAlternateContentSources())));
|
61
|
+
};
|
62
|
+
|
68
63
|
const createButtonOnclick = () => {
|
69
|
-
|
70
|
-
console.log('Dispatch create!');
|
64
|
+
setIsCreateWizardOpen(true);
|
71
65
|
};
|
72
66
|
|
73
67
|
const rowDropdownItems = ({ id }) => [
|
74
68
|
{
|
75
|
-
title: 'Delete',
|
69
|
+
title: __('Delete'),
|
76
70
|
ouiaId: `remove-acs-${id}`,
|
77
71
|
onClick: () => {
|
78
72
|
onDelete(id);
|
79
73
|
},
|
80
74
|
},
|
75
|
+
{
|
76
|
+
title: __('Refresh'),
|
77
|
+
ouiaId: `remove-acs-${id}`,
|
78
|
+
onClick: () => {
|
79
|
+
onRefresh(id);
|
80
|
+
},
|
81
|
+
},
|
81
82
|
];
|
82
83
|
|
83
84
|
const emptyContentTitle = __("You currently don't have any alternate content sources.");
|
@@ -104,9 +105,17 @@ const ACSTable = () => {
|
|
104
105
|
additionalListeners={[activeSortColumn, activeSortDirection]}
|
105
106
|
autocompleteEndpoint="/alternate_content_sources/auto_complete_search"
|
106
107
|
actionButtons={
|
107
|
-
|
108
|
-
{
|
109
|
-
|
108
|
+
<>
|
109
|
+
<Button ouiaId="create-acs" onClick={createButtonOnclick} variant="primary" aria-label="create_acs">
|
110
|
+
{__('Add source')}
|
111
|
+
</Button>
|
112
|
+
{isCreateWizardOpen &&
|
113
|
+
<ACSCreateWizard
|
114
|
+
show={isCreateWizardOpen}
|
115
|
+
setIsOpen={setIsCreateWizardOpen}
|
116
|
+
/>
|
117
|
+
}
|
118
|
+
</>
|
110
119
|
}
|
111
120
|
>
|
112
121
|
<Thead>
|
@@ -124,15 +133,16 @@ const ACSTable = () => {
|
|
124
133
|
<Tbody>
|
125
134
|
{results?.map((acs, index) => {
|
126
135
|
const {
|
127
|
-
id,
|
128
136
|
name,
|
129
137
|
alternate_content_source_type: acsType,
|
138
|
+
last_refresh: lastTask,
|
130
139
|
} = acs;
|
140
|
+
const { last_refresh_words: lastRefreshWords, started_at: startedAt } = lastTask ?? {};
|
131
141
|
return (
|
132
142
|
<Tr key={index}>
|
133
143
|
<Td>{name}</Td>
|
134
144
|
<Td>{acsType}</Td>
|
135
|
-
<Td
|
145
|
+
<Td><LastSync startedAt={startedAt} lastSync={lastTask} lastSyncWords={lastRefreshWords} emptyMessage="N/A" /></Td>
|
136
146
|
<Td
|
137
147
|
actions={{
|
138
148
|
items: rowDropdownItems(acs),
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import {
|
2
|
+
selectAPIStatus,
|
2
3
|
selectAPIResponse,
|
3
4
|
} from 'foremanReact/redux/API/APISelectors';
|
5
|
+
import { STATUS } from 'foremanReact/constants';
|
4
6
|
|
5
7
|
import { GET_CONTENT_CREDENTIALS_KEY } from './ContentCredentialConstants';
|
6
8
|
|
@@ -9,4 +11,5 @@ export const selectContentCredentials = (state) => {
|
|
9
11
|
return response.results;
|
10
12
|
};
|
11
13
|
|
12
|
-
export
|
14
|
+
export const selectContentCredentialsStatus = state =>
|
15
|
+
selectAPIStatus(state, GET_CONTENT_CREDENTIALS_KEY) || STATUS.PENDING;
|
@@ -115,7 +115,7 @@ const CreateContentViewForm = ({ setModalOpen }) => {
|
|
115
115
|
aria-label="component_tile"
|
116
116
|
icon={<ContentViewIcon composite={false} />}
|
117
117
|
id="component"
|
118
|
-
title={__('
|
118
|
+
title={__('Content view')}
|
119
119
|
onClick={() => { setComponent(true); setComposite(false); }}
|
120
120
|
isSelected={component}
|
121
121
|
>
|
@@ -133,7 +133,7 @@ const CreateContentViewForm = ({ setModalOpen }) => {
|
|
133
133
|
onClick={() => { setComposite(true); setComponent(false); }}
|
134
134
|
isSelected={composite}
|
135
135
|
>
|
136
|
-
{__('Consisting of multiple
|
136
|
+
{__('Consisting of multiple content views')}
|
137
137
|
</Tile>
|
138
138
|
</GridItem>
|
139
139
|
</Grid>
|
@@ -68,7 +68,7 @@ test('Displays dependent fields correctly', () => {
|
|
68
68
|
expect(getByText('Name')).toBeInTheDocument();
|
69
69
|
expect(getByText('Label')).toBeInTheDocument();
|
70
70
|
expect(getByText('Composite content view')).toBeInTheDocument();
|
71
|
-
expect(getByText('
|
71
|
+
expect(getByText('Content view')).toBeInTheDocument();
|
72
72
|
expect(getByText('Solve dependencies')).toBeInTheDocument();
|
73
73
|
expect(queryByText('Auto publish')).not.toBeInTheDocument();
|
74
74
|
expect(getByText('Import only')).toBeInTheDocument();
|
data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js
CHANGED
@@ -87,7 +87,7 @@ const ComponentContentViewAddModal = ({
|
|
87
87
|
|
88
88
|
return (
|
89
89
|
<Modal
|
90
|
-
title={componentId ? __('Update version') : __('Add
|
90
|
+
title={componentId ? __('Update version') : __('Add content view')}
|
91
91
|
variant={ModalVariant.small}
|
92
92
|
isOpen={show}
|
93
93
|
description={__(`Select available version of ${cvName} to use`)}
|
data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewBulkAddModal.js
CHANGED
@@ -51,10 +51,10 @@ const ComponentContentViewBulkAddModal = ({ cvId, rowsToAdd, onClose }) => {
|
|
51
51
|
|
52
52
|
return (
|
53
53
|
<Modal
|
54
|
-
title={__('Add
|
54
|
+
title={__('Add content views')}
|
55
55
|
variant={ModalVariant.large}
|
56
56
|
isOpen
|
57
|
-
description={__('Select available version of
|
57
|
+
description={__('Select available version of content views to use')}
|
58
58
|
onClose={onClose}
|
59
59
|
appendTo={document.body}
|
60
60
|
>
|
@@ -212,7 +212,7 @@ test('Can add published component views to content view with modal', async (done
|
|
212
212
|
});
|
213
213
|
fireEvent.click(getByText('Add'));
|
214
214
|
await patientlyWaitFor(() => {
|
215
|
-
expect(getByText('Add
|
215
|
+
expect(getByText('Add content view')).toBeInTheDocument();
|
216
216
|
});
|
217
217
|
fireEvent.click(getByLabelText('add_component'));
|
218
218
|
await patientlyWaitFor(() => {
|
@@ -317,7 +317,7 @@ test('Can bulk add component views to content view with modal', async (done) =>
|
|
317
317
|
.reply(200, {});
|
318
318
|
|
319
319
|
const {
|
320
|
-
|
320
|
+
getAllByText, getByLabelText, queryByText,
|
321
321
|
} = renderWithRedux(
|
322
322
|
<ContentViewComponents cvId={4} details={cvDetails} />,
|
323
323
|
renderOptions,
|
@@ -333,14 +333,14 @@ test('Can bulk add component views to content view with modal', async (done) =>
|
|
333
333
|
});
|
334
334
|
fireEvent.click(getByLabelText('bulk_add_components'));
|
335
335
|
await patientlyWaitFor(() => {
|
336
|
-
expect(
|
336
|
+
expect(getAllByText('Add content views')[1]).toBeInTheDocument();
|
337
337
|
});
|
338
338
|
fireEvent.click(getByLabelText('version-select-cv-10'));
|
339
339
|
fireEvent.click(getByLabelText('cv-10-3.0'));
|
340
340
|
|
341
341
|
fireEvent.click(getByLabelText('add_components'));
|
342
342
|
await patientlyWaitFor(() => {
|
343
|
-
expect(queryByText('
|
343
|
+
expect(queryByText('Select available version of content views to use')).not.toBeInTheDocument();
|
344
344
|
expect(getByLabelText('bulk_add_components')).toHaveAttribute('aria-disabled', 'false');
|
345
345
|
});
|
346
346
|
|
@@ -71,7 +71,7 @@ const ContentViewInfo = ({ cvId, details }) => {
|
|
71
71
|
<TextListItem component={TextListItemVariants.dd} className="foreman-spaced-list">
|
72
72
|
<Flex>
|
73
73
|
<FlexItem spacer={{ default: 'spacerXs' }}>
|
74
|
-
<ContentViewIcon composite={composite} description={composite ? __('Composite') : __('
|
74
|
+
<ContentViewIcon composite={composite} description={composite ? __('Composite') : __('Content view')} />
|
75
75
|
</FlexItem>
|
76
76
|
</Flex>
|
77
77
|
</TextListItem>
|
@@ -43,14 +43,14 @@ test('Can call API for CVs and show on screen on page load', async (done) => {
|
|
43
43
|
.query(true)
|
44
44
|
.reply(200, cvIndexData);
|
45
45
|
|
46
|
-
const { queryByText } = renderWithRedux(<ContentViewsPage />, renderOptions);
|
46
|
+
const { queryByText, queryAllByText } = renderWithRedux(<ContentViewsPage />, renderOptions);
|
47
47
|
|
48
48
|
expect(queryByText(firstCV.name)).toBeNull();
|
49
49
|
|
50
50
|
// Assert that the CV name is now showing on the screen, but wait for it to appear.
|
51
51
|
await patientlyWaitFor(() => {
|
52
52
|
expect(queryByText(firstCV.name)).toBeInTheDocument();
|
53
|
-
expect(
|
53
|
+
expect(queryAllByText('Content views')[0]).toBeInTheDocument();
|
54
54
|
expect(queryByText('Composite content views')).toBeInTheDocument();
|
55
55
|
});
|
56
56
|
|
@@ -354,7 +354,7 @@ test('Displays Create Content View and opens modal with Form', async () => {
|
|
354
354
|
expect(queryByText('Name')).not.toBeInTheDocument();
|
355
355
|
expect(queryByText('Label')).not.toBeInTheDocument();
|
356
356
|
expect(queryByText('Composite content view')).not.toBeInTheDocument();
|
357
|
-
expect(queryByText('
|
357
|
+
expect(queryByText('Content view')).not.toBeInTheDocument();
|
358
358
|
expect(queryByText('Solve dependencies')).not.toBeInTheDocument();
|
359
359
|
expect(queryByText('Auto publish')).not.toBeInTheDocument();
|
360
360
|
expect(queryByText('Import only')).not.toBeInTheDocument();
|
@@ -365,7 +365,7 @@ test('Displays Create Content View and opens modal with Form', async () => {
|
|
365
365
|
expect(getByText('Name')).toBeInTheDocument();
|
366
366
|
expect(getByText('Label')).toBeInTheDocument();
|
367
367
|
expect(getByText('Composite content view')).toBeInTheDocument();
|
368
|
-
expect(getByText('
|
368
|
+
expect(getByText('Content view')).toBeInTheDocument();
|
369
369
|
expect(getByText('Solve dependencies')).toBeInTheDocument();
|
370
370
|
expect(queryByText('Auto publish')).not.toBeInTheDocument();
|
371
371
|
expect(getByText('Import only')).toBeInTheDocument();
|
@@ -16,7 +16,7 @@ const ContentViewIcon = ({
|
|
16
16
|
position="auto"
|
17
17
|
enableFlip
|
18
18
|
entryDelay={400}
|
19
|
-
content={composite ? __('Composite content view') : __('
|
19
|
+
content={composite ? __('Composite content view') : __('Content view')}
|
20
20
|
{...toolTipProps}
|
21
21
|
>
|
22
22
|
{composite ? <RegistryIcon size="md" {...props} /> : <EnterpriseIcon size="sm" {...props} />}
|
@@ -18,7 +18,7 @@ const ContentViewsCounter = () => {
|
|
18
18
|
<b>
|
19
19
|
<Flex>
|
20
20
|
<FlexItem spacer={{ default: 'spacerXs' }}>
|
21
|
-
<ContentViewIcon composite={false} description={__('
|
21
|
+
<ContentViewIcon composite={false} description={__('Content views')} count={(component || component === 0) ? component : <InProgressIcon />} />
|
22
22
|
</FlexItem>
|
23
23
|
<FlexItem>
|
24
24
|
<Tooltip
|
@@ -14,14 +14,14 @@ const DetailsExpansion = ({
|
|
14
14
|
if (cvComposite) {
|
15
15
|
return (
|
16
16
|
<>
|
17
|
-
{__('Related
|
17
|
+
{__('Related content views: ')}
|
18
18
|
<RelatedContentViewComponentsModal key="cvId" {...{ cvName, cvId, relatedCVCount }} />
|
19
19
|
</>
|
20
20
|
);
|
21
21
|
}
|
22
22
|
return (
|
23
23
|
<>
|
24
|
-
{__('Related composite
|
24
|
+
{__('Related composite content views: ')}
|
25
25
|
<RelatedCompositeContentViewsModal
|
26
26
|
key={cvId}
|
27
27
|
{...{
|
@@ -29,7 +29,7 @@ const RelatedContentViewsModal = ({ cvName, cvId, relatedCVCount }) => {
|
|
29
29
|
<FlexItem>
|
30
30
|
<RegistryIcon />
|
31
31
|
<b>{` ${cvName}`}</b>
|
32
|
-
{__(' content view is used in listed
|
32
|
+
{__(' content view is used in listed content views. For more information, ')}
|
33
33
|
<Link to={urlBuilder(`content_views/${cvId}#/contentviews`, '')}>
|
34
34
|
{__('view content view tabs.')}
|
35
35
|
</Link>
|
@@ -49,7 +49,7 @@ const RelatedContentViewsModal = ({ cvName, cvId, relatedCVCount }) => {
|
|
49
49
|
<Grid>
|
50
50
|
<GridItem span={12}>
|
51
51
|
<Modal
|
52
|
-
title={__('Related
|
52
|
+
title={__('Related content views')}
|
53
53
|
variant={ModalVariant.medium}
|
54
54
|
isOpen={isOpen}
|
55
55
|
description={description()}
|
@@ -30,7 +30,7 @@ test('Can call API and show Related Content Views Components Modal', async (done
|
|
30
30
|
|
31
31
|
await patientlyWaitFor(() => expect(getByLabelText(`button_${cvId}`)).toBeInTheDocument());
|
32
32
|
fireEvent.click(getByLabelText(`button_${cvId}`));
|
33
|
-
await patientlyWaitFor(() => expect(getByText('Related
|
33
|
+
await patientlyWaitFor(() => expect(getByText('Related content views')).toBeInTheDocument());
|
34
34
|
|
35
35
|
assertNockRequest(scope, done);
|
36
36
|
});
|
@@ -2,7 +2,7 @@
|
|
2
2
|
import React, { Component } from 'react';
|
3
3
|
import { DropdownButton, MenuItem } from 'patternfly-react';
|
4
4
|
import PropTypes from 'prop-types';
|
5
|
-
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
6
|
import '../index.scss';
|
7
7
|
import Search from '../../../components/Search/index';
|
8
8
|
import { orgId } from '../../../services/api';
|
@@ -14,17 +14,17 @@ class RepositorySearch extends Component {
|
|
14
14
|
{
|
15
15
|
key: 'available',
|
16
16
|
endpoint: 'repository_sets',
|
17
|
-
title: 'Available',
|
17
|
+
title: __('Available'),
|
18
18
|
},
|
19
19
|
{
|
20
20
|
key: 'enabled',
|
21
21
|
endpoint: 'enabled_repositories',
|
22
|
-
title: 'Enabled',
|
22
|
+
title: __('Enabled'),
|
23
23
|
},
|
24
24
|
{
|
25
25
|
key: 'both',
|
26
26
|
endpoint: false,
|
27
|
-
title: 'Both',
|
27
|
+
title: __('Both'),
|
28
28
|
},
|
29
29
|
];
|
30
30
|
this.state = { searchList: this.dropDownItems[0] };
|