Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions workspaces/extensions/.changeset/popular-pumpkins-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@red-hat-developer-hub/backstage-plugin-extensions': patch
---

show error in the UI when toggle action fails
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { TabPanel } from './TabPanel';
import { useInstallationContext } from './InstallationContext';
import { useTranslation } from '../hooks/useTranslation';
import { ExtensionsStatus, getPluginActionTooltipMessage } from '../utils';
import { InstallationWarning } from './InstallationWarning';

interface TabItem {
label: string;
Expand Down Expand Up @@ -197,6 +198,13 @@ export const ExtensionsPackageEditContent = ({

const showRightCard = hasPackageExamples;

const showEditWarning =
(pkgConfig.data as any)?.error?.message &&
(pkgConfig.data as any)?.error?.reason !==
ExtensionsStatus.INSTALLATION_DISABLED &&
(pkgConfig.data as any)?.error?.reason !==
ExtensionsStatus.INSTALLATION_DISABLED_IN_PRODUCTION;

const handleSave = async () => {
try {
setSaveError(null);
Expand Down Expand Up @@ -279,6 +287,12 @@ export const ExtensionsPackageEditContent = ({

return (
<>
{showEditWarning && <InstallationWarning configData={pkgConfig.data} />}
{saveError && (
<Alert severity="error" sx={{ mb: '1rem' }}>
{saveError}
</Alert>
)}
{!pkg.spec?.dynamicArtifact && (
<Alert severity="error" sx={{ mb: '1rem' }}>
<AlertTitle>{t('alert.missingDynamicArtifactTitle')}</AlertTitle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { Query, QueryResult } from '@material-table/core';
import { useQuery } from '@tanstack/react-query';

import Box from '@mui/material/Box';
import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
import {
ExtensionsPackage,
ExtensionsPackageInstallStatus,
Expand Down Expand Up @@ -58,6 +60,8 @@ import {

export const InstalledPackagesTable = () => {
const { t } = useTranslation();
const [rowActionError, setRowActionError] = useState<string | null>(null);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const extensionsConfig = useExtensionsConfiguration();
const { installedPackages } = useInstallationContext();
const nodeEnvironment = useNodeEnvironment();
Expand Down Expand Up @@ -187,11 +191,19 @@ export const InstalledPackagesTable = () => {
<DownloadPackageYaml
pkg={row}
isProductionEnv={isProductionEnvironment}
onError={(err: string) => {
setRowActionError(err);
setSnackbarOpen(true);
}}
/>
<TogglePackage
pkg={row}
isProductionEnv={isProductionEnvironment}
isInstallationEnabled={extensionsConfig.data?.enabled ?? false}
onError={(err: string) => {
setRowActionError(err);
setSnackbarOpen(true);
}}
/>
</Box>
);
Expand Down Expand Up @@ -359,6 +371,31 @@ export const InstalledPackagesTable = () => {
onClose={setOpenInstalledPackagesDialog}
showPackages
/>
<Snackbar
open={snackbarOpen}
autoHideDuration={6000}
onClose={() => {
setSnackbarOpen(false);
setRowActionError(null);
}}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
sx={{
top: '80px !important',
}}
>
<Alert
onClose={() => {
setSnackbarOpen(false);
setRowActionError(null);
}}
severity="error"
sx={{ width: '100%' }}
>
{Array.isArray(rowActionError)
? rowActionError.map((err, idx) => <div key={idx}>{err}</div>)
: rowActionError}
</Alert>
</Snackbar>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { ExtensionsPackageInstallStatus } from '@red-hat-developer-hub/backstage
import { useTranslation } from '../../hooks/useTranslation';
import { packageInstallRouteRef, packageRouteRef } from '../../routes';
import { usePackageConfig } from '../../hooks/usePackageConfig';
import { usePackage } from '../../hooks/usePackage';
import { downloadPackageYAML } from '../../utils/downloadPackageYaml';
import { usePluginConfigurationPermissions } from '../../hooks/usePluginConfigurationPermissions';
import { useEnablePlugin } from '../../hooks/useEnablePlugin';
Expand All @@ -53,12 +54,15 @@ export type InstalledPackageRow = {
export const DownloadPackageYaml = ({
pkg,
isProductionEnv,
onError,
}: {
pkg: InstalledPackageRow;
isProductionEnv: boolean;
onError?: (error: string) => void;
}) => {
const { t } = useTranslation();
const pkgConfig = usePackageConfig(pkg.namespace!, pkg.name!);
const packageEntity = usePackage(pkg.namespace!, pkg.name!);
const packageConfigPermission = usePluginConfigurationPermissions(
pkg.namespace!,
pkg.parentPlugin ?? '',
Expand Down Expand Up @@ -108,10 +112,31 @@ export const DownloadPackageYaml = ({
size="small"
sx={{ color: theme => theme.palette.text.primary }}
onClick={async () => {
await downloadPackageYAML(
pkgConfig.data?.configYaml ?? '',
pkg.name!,
);
try {
const configYaml = pkgConfig.data?.configYaml ?? '';
if (!configYaml) {
const dynamicArtifact =
packageEntity.data?.spec?.dynamicArtifact ??
`./dynamic-plugins/dist/${pkg.packageName}`;
const minimalYaml = `plugins:
- package: ${JSON.stringify(dynamicArtifact)}
disabled: false
`;
// eslint-disable-next-line no-console
console.info(
`No configuration found for package ${pkg.name}, downloaded a minimal YAML`,
);
await downloadPackageYAML(minimalYaml, pkg.name!);
return;
}
await downloadPackageYAML(configYaml, pkg.name!);
} catch (err: any) {
const errorMessage =
err?.message || err?.toString() || 'Unknown error occurred';
onError?.(
`Failed to download package ${pkg.name}: ${errorMessage}`,
);
}
}}
>
<FileDownloadOutlinedIcon />
Expand Down Expand Up @@ -235,10 +260,12 @@ export const TogglePackage = ({
pkg,
isProductionEnv,
isInstallationEnabled,
onError,
}: {
pkg: InstalledPackageRow;
isProductionEnv: boolean;
isInstallationEnabled: boolean;
onError?: (error: string) => void;
}) => {
const { t } = useTranslation();
const { installedPackages, setInstalledPackages } = useInstallationContext();
Expand Down Expand Up @@ -323,17 +350,20 @@ export const TogglePackage = ({
};
setInstalledPackages(updated);
} else {
// eslint-disable-next-line no-console
console.warn(
`[Package Toggle] Package toggle responded with non-OK status:`,
(res as any)?.error?.message ?? res,
const errorMessage =
(res as any)?.error?.message ?? res?.toString() ?? 'Unknown error';
onError?.(
`Failed to ${isPackageEnabled ? 'disable' : 'enable'} package ${pkg.name}: ${errorMessage}`,
);
}
} catch (err: any) {
// eslint-disable-next-line no-console
console.error(
`[Package Toggle] Failed to toggle package}:`,
err?.error?.message ?? err,
const errorMessage =
err?.error?.message ??
err?.message ??
err?.toString() ??
'Unknown error';
onError?.(
`Failed to ${isPackageEnabled ? 'disable' : 'enable'} package ${pkg.name}: ${errorMessage}`,
);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const PackageEditHeader = () => {

const pkg = usePackage(params.namespace, params.name);

const displayName = pkg.data?.metadata.title ?? params.name;
const displayName = pkg.data?.metadata?.title ?? params.name;
const title =
location?.state?.viewOnly || !pkg.data?.spec?.dynamicArtifact
? displayName
Expand Down