HMS-4024: Update repositories step

This commit is contained in:
Andrew Dewar 2024-05-31 09:37:16 -06:00 committed by Klara Simickova
parent e85789f58d
commit 5dc4ecb63f
14 changed files with 785 additions and 706 deletions

View file

@ -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);
};

View file

@ -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;

View file

@ -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>,
]}
/>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
};

View file

@ -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>
);
};

View file

@ -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;
};

View file

@ -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>

View file

@ -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 = {