HMS-4024: Update repositories step
This commit is contained in:
parent
e85789f58d
commit
5dc4ecb63f
14 changed files with 785 additions and 706 deletions
|
|
@ -47,6 +47,7 @@ import {
|
|||
RH_ICON_SIZE,
|
||||
} from '../../../../constants';
|
||||
import {
|
||||
ApiRepositoryResponseRead,
|
||||
useCreateRepositoryMutation,
|
||||
useListRepositoriesQuery,
|
||||
useSearchRpmMutation,
|
||||
|
|
@ -151,7 +152,8 @@ const Packages = () => {
|
|||
},
|
||||
] = useSearchRpmMutation();
|
||||
|
||||
const [createRepository] = useCreateRepositoryMutation();
|
||||
const [createRepository, { isLoading: createLoading }] =
|
||||
useCreateRepositoryMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedSearchTerm.length > 1 && isSuccessDistroRepositories) {
|
||||
|
|
@ -202,6 +204,10 @@ const Packages = () => {
|
|||
toggleSourceRepos,
|
||||
searchRecommendedRpms,
|
||||
epelRepoUrlByDistribution,
|
||||
isSuccessDistroRepositories,
|
||||
searchDistroRpms,
|
||||
distroRepositories,
|
||||
arch,
|
||||
]);
|
||||
|
||||
const EmptySearch = () => {
|
||||
|
|
@ -441,6 +447,8 @@ const Packages = () => {
|
|||
<Button
|
||||
key="add"
|
||||
variant="primary"
|
||||
isLoading={createLoading}
|
||||
isDisabled={createLoading}
|
||||
onClick={handleConfirmModalToggle}
|
||||
>
|
||||
Add listed repositories
|
||||
|
|
@ -723,16 +731,22 @@ const Packages = () => {
|
|||
`There was an error while adding the recommended repository.`
|
||||
);
|
||||
}
|
||||
|
||||
if (epelRepo.data.length === 0) {
|
||||
await createRepository({
|
||||
const result = await createRepository({
|
||||
apiRepositoryRequest: distribution.startsWith('rhel-8')
|
||||
? EPEL_8_REPO_DEFINITION
|
||||
: EPEL_9_REPO_DEFINITION,
|
||||
});
|
||||
dispatch(
|
||||
addRecommendedRepository(
|
||||
(result as { data: ApiRepositoryResponseRead }).data
|
||||
)
|
||||
);
|
||||
} else {
|
||||
dispatch(addRecommendedRepository(epelRepo.data[0]));
|
||||
}
|
||||
|
||||
dispatch(addPackage(isSelectingPackage!));
|
||||
dispatch(addRecommendedRepository(epelRepo.data[0]));
|
||||
setIsRepoModalOpen(!isRepoModalOpen);
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,39 +4,36 @@ import { Alert, Button } from '@patternfly/react-core';
|
|||
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||
|
||||
import { useGetEnvironment } from '../../../../Utilities/useGetEnvironment';
|
||||
import { useCheckRepositoriesAvailability } from '../../utilities/checkRepositoriesAvailability';
|
||||
|
||||
const RepositoryUnavailable = () => {
|
||||
const RepositoryUnavailable = ({ quantity }: { quantity: number }) => {
|
||||
const { isBeta } = useGetEnvironment();
|
||||
|
||||
if (useCheckRepositoriesAvailability()) {
|
||||
return (
|
||||
<Alert
|
||||
variant="warning"
|
||||
title="Previously added custom repository unavailable"
|
||||
return (
|
||||
<Alert
|
||||
variant="warning"
|
||||
title="Previously added custom repository unavailable"
|
||||
isInline
|
||||
>
|
||||
{quantity > 1
|
||||
? `${quantity} repositories that were used to build this image previously are not available.`
|
||||
: 'One repository that was used to build this image previously is not available. '}
|
||||
Address the error found in the last introspection and validate that the
|
||||
repository is still accessible.
|
||||
<br />
|
||||
<br />
|
||||
<Button
|
||||
component="a"
|
||||
target="_blank"
|
||||
variant="link"
|
||||
iconPosition="right"
|
||||
isInline
|
||||
icon={<ExternalLinkAltIcon />}
|
||||
href={isBeta() ? '/preview/settings/content' : '/settings/content'}
|
||||
>
|
||||
A repository that was used to build this image previously is not
|
||||
available. Address the error found in the last introspection and
|
||||
validate that the repository is still accessible.
|
||||
<br />
|
||||
<br />
|
||||
<Button
|
||||
component="a"
|
||||
target="_blank"
|
||||
variant="link"
|
||||
iconPosition="right"
|
||||
isInline
|
||||
icon={<ExternalLinkAltIcon />}
|
||||
href={isBeta() ? '/preview/settings/content' : '/settings/content'}
|
||||
>
|
||||
Go to Repositories
|
||||
</Button>
|
||||
</Alert>
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
Go to Repositories
|
||||
</Button>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export default RepositoryUnavailable;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownToggle,
|
||||
DropdownToggleCheckbox,
|
||||
} from '@patternfly/react-core/deprecated';
|
||||
|
||||
import { ApiRepositoryResponseRead } from '../../../../../store/contentSourcesApi';
|
||||
|
||||
interface BulkSelectProps {
|
||||
selected: Set<string>;
|
||||
contentList: ApiRepositoryResponseRead[];
|
||||
deselectAll: () => void;
|
||||
perPage: number;
|
||||
handleAddRemove: (
|
||||
repo: ApiRepositoryResponseRead | ApiRepositoryResponseRead[],
|
||||
selected: boolean
|
||||
) => void;
|
||||
isDisabled: boolean;
|
||||
}
|
||||
|
||||
export function BulkSelect({
|
||||
selected,
|
||||
contentList,
|
||||
deselectAll,
|
||||
perPage,
|
||||
handleAddRemove,
|
||||
isDisabled,
|
||||
}: BulkSelectProps) {
|
||||
const [dropdownIsOpen, setDropdownIsOpen] = useState(false);
|
||||
|
||||
const allChecked = !contentList.some(({ url }) => !selected.has(url!));
|
||||
|
||||
const someChecked =
|
||||
allChecked || contentList.some(({ url }) => selected.has(url!));
|
||||
|
||||
const toggleDropdown = () => setDropdownIsOpen(!dropdownIsOpen);
|
||||
|
||||
const handleSelectPage = () => handleAddRemove(contentList, !allChecked);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
toggle={
|
||||
<DropdownToggle
|
||||
id="stacked-example-toggle"
|
||||
isDisabled={isDisabled}
|
||||
splitButtonItems={[
|
||||
<DropdownToggleCheckbox
|
||||
id="example-checkbox-1"
|
||||
key="split-checkbox"
|
||||
aria-label="Select all"
|
||||
isChecked={allChecked || someChecked ? null : false}
|
||||
onClick={handleSelectPage}
|
||||
/>,
|
||||
]}
|
||||
onToggle={toggleDropdown}
|
||||
>
|
||||
{someChecked ? `${selected.size} selected` : null}
|
||||
</DropdownToggle>
|
||||
}
|
||||
isOpen={dropdownIsOpen}
|
||||
dropdownItems={[
|
||||
<DropdownItem
|
||||
key="none"
|
||||
isDisabled={!selected.size}
|
||||
onClick={() => {
|
||||
deselectAll();
|
||||
toggleDropdown();
|
||||
}}
|
||||
>{`Clear all (${selected.size} items)`}</DropdownItem>,
|
||||
<DropdownItem
|
||||
key="page"
|
||||
isDisabled={!contentList.length}
|
||||
onClick={() => {
|
||||
handleSelectPage();
|
||||
toggleDropdown();
|
||||
}}
|
||||
>{`${allChecked ? 'Remove' : 'Select'} page (${
|
||||
perPage > contentList.length ? contentList.length : perPage
|
||||
} items)`}</DropdownItem>,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
EmptyState,
|
||||
EmptyStateVariant,
|
||||
EmptyStateHeader,
|
||||
EmptyStateIcon,
|
||||
EmptyStateBody,
|
||||
EmptyStateFooter,
|
||||
Button,
|
||||
} from '@patternfly/react-core';
|
||||
import { RepositoryIcon } from '@patternfly/react-icons';
|
||||
|
||||
import { useGetEnvironment } from '../../../../../Utilities/useGetEnvironment';
|
||||
|
||||
type EmptyProps = {
|
||||
refetch: () => void;
|
||||
hasFilterValue: boolean;
|
||||
};
|
||||
|
||||
export default function Empty({ hasFilterValue, refetch }: EmptyProps) {
|
||||
const { isBeta } = useGetEnvironment();
|
||||
return (
|
||||
<EmptyState variant={EmptyStateVariant.lg} data-testid="empty-state">
|
||||
<EmptyStateHeader
|
||||
titleText={
|
||||
hasFilterValue
|
||||
? 'No matching repositories found'
|
||||
: 'No Custom Repositories'
|
||||
}
|
||||
icon={<EmptyStateIcon icon={RepositoryIcon} />}
|
||||
headingLevel="h4"
|
||||
/>
|
||||
<EmptyStateBody>
|
||||
{hasFilterValue
|
||||
? 'Try another search query or clear the current search value'
|
||||
: `Repositories can be added in the "Repositories" area of the
|
||||
console. Once added, refresh this page to see them.`}
|
||||
</EmptyStateBody>
|
||||
<EmptyStateFooter>
|
||||
<Button
|
||||
variant="primary"
|
||||
component="a"
|
||||
target="_blank"
|
||||
href={isBeta() ? '/preview/settings/content' : '/settings/content'}
|
||||
className="pf-u-mr-sm"
|
||||
>
|
||||
Go to repositories
|
||||
</Button>
|
||||
<Button variant="secondary" isInline onClick={() => refetch()}>
|
||||
Refresh
|
||||
</Button>
|
||||
</EmptyStateFooter>
|
||||
</EmptyState>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Alert } from '@patternfly/react-core';
|
||||
|
||||
export const Error = () => {
|
||||
return (
|
||||
<Alert title="Repositories unavailable" variant="danger" isPlain isInline>
|
||||
Repositories cannot be reached, try again later.
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
EmptyState,
|
||||
EmptyStateIcon,
|
||||
Spinner,
|
||||
EmptyStateHeader,
|
||||
Bullseye,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
export const Loading = () => {
|
||||
return (
|
||||
<Bullseye>
|
||||
<EmptyState>
|
||||
<EmptyStateHeader
|
||||
titleText="Loading"
|
||||
icon={<EmptyStateIcon icon={Spinner} />}
|
||||
headingLevel="h4"
|
||||
/>
|
||||
</EmptyState>
|
||||
</Bullseye>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { ApiRepositoryResponseRead } from '../../../../../store/contentSourcesApi';
|
||||
import {
|
||||
CustomRepository,
|
||||
Repository,
|
||||
} from '../../../../../store/imageBuilderApi';
|
||||
|
||||
// Utility function to convert from Content Sources to Image Builder custom repo API schema
|
||||
export const convertSchemaToIBCustomRepo = (
|
||||
repo: ApiRepositoryResponseRead
|
||||
) => {
|
||||
const imageBuilderRepo: CustomRepository = {
|
||||
id: repo.uuid!,
|
||||
name: repo.name,
|
||||
baseurl: [repo.url!],
|
||||
check_gpg: false,
|
||||
};
|
||||
// only include the flag if enabled
|
||||
if (repo.module_hotfixes) {
|
||||
imageBuilderRepo.module_hotfixes = repo.module_hotfixes;
|
||||
}
|
||||
if (repo.gpg_key) {
|
||||
imageBuilderRepo.gpgkey = [repo.gpg_key];
|
||||
imageBuilderRepo.check_gpg = true;
|
||||
imageBuilderRepo.check_repo_gpg = repo.metadata_verification;
|
||||
}
|
||||
|
||||
return imageBuilderRepo;
|
||||
};
|
||||
|
||||
// Utility function to convert from Content Sources to Image Builder payload repo API schema
|
||||
export const convertSchemaToIBPayloadRepo = (
|
||||
repo: ApiRepositoryResponseRead
|
||||
) => {
|
||||
const imageBuilderRepo: Repository = {
|
||||
baseurl: repo.url,
|
||||
rhsm: false,
|
||||
check_gpg: false,
|
||||
};
|
||||
// only include the flag if enabled
|
||||
if (repo.module_hotfixes) {
|
||||
imageBuilderRepo.module_hotfixes = repo.module_hotfixes;
|
||||
}
|
||||
if (repo.gpg_key) {
|
||||
imageBuilderRepo.gpgkey = repo.gpg_key;
|
||||
imageBuilderRepo.check_gpg = true;
|
||||
imageBuilderRepo.check_repo_gpg = repo.metadata_verification;
|
||||
}
|
||||
|
||||
return imageBuilderRepo;
|
||||
};
|
||||
|
|
@ -44,7 +44,7 @@ const RepositoriesStep = () => {
|
|||
<br />
|
||||
<ManageRepositoriesButton />
|
||||
</Text>
|
||||
{recommendedRepos.length > 0 && (
|
||||
{packages.length && recommendedRepos.length ? (
|
||||
<Alert
|
||||
title="Why can't I remove a selected repository?"
|
||||
variant="info"
|
||||
|
|
@ -55,6 +55,8 @@ const RepositoriesStep = () => {
|
|||
following packages on the Packages step:{' '}
|
||||
{packages.map((pkg) => pkg.name).join(', ')}
|
||||
</Alert>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<Repositories />
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ import {
|
|||
import {
|
||||
convertSchemaToIBCustomRepo,
|
||||
convertSchemaToIBPayloadRepo,
|
||||
} from '../steps/Repositories/Repositories';
|
||||
} from '../steps/Repositories/components/Utilities';
|
||||
import { GcpAccountType } from '../steps/TargetEnvironment/Gcp';
|
||||
|
||||
type ServerStore = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue