Skip to content

Commit b6c9d5c

Browse files
fix(extensions): disable row actions in prod env (#1665)
Co-authored-by: Mitesh Kumar <itsmiteshkumar98@gmail.com>
1 parent 7a65562 commit b6c9d5c

File tree

10 files changed

+160
-26
lines changed

10 files changed

+160
-26
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-marketplace': patch
3+
---
4+
5+
Disabled row actions in the "Installed packages" tab for the production environment

workspaces/marketplace/plugins/marketplace/report-alpha.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ readonly "installedPackages.table.columns.version": string;
109109
readonly "installedPackages.table.columns.role": string;
110110
readonly "installedPackages.table.columns.name": string;
111111
readonly "installedPackages.table.columns.actions": string;
112+
readonly "installedPackages.table.tooltips.packageProductionDisabled": string;
112113
readonly "installedPackages.table.tooltips.enableActions": string;
113114
readonly "installedPackages.table.tooltips.noDownloadPermissions": string;
114115
readonly "installedPackages.table.tooltips.noEditPermissions": string;

workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.test.tsx

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,27 @@ import { BrowserRouter } from 'react-router-dom';
1818
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
1919
import { mockApis, MockErrorApi, TestApiProvider } from '@backstage/test-utils';
2020
import { QueryClientProvider } from '@tanstack/react-query';
21+
import { errorApiRef } from '@backstage/core-plugin-api';
22+
import { translationApiRef } from '@backstage/core-plugin-api/alpha';
2123

2224
import { queryClient } from '../../queryclient';
2325
import { marketplaceApiRef } from '../../api';
2426
import { dynamicPluginsInfoApiRef } from '../../api';
2527
import { InstalledPackagesTable } from './InstalledPackagesTable';
26-
import { errorApiRef } from '@backstage/core-plugin-api';
27-
import { translationApiRef } from '@backstage/core-plugin-api/alpha';
28+
import { useNodeEnvironment } from '../../hooks/useNodeEnvironment';
2829

2930
jest.mock('@backstage/core-plugin-api', () => ({
3031
...jest.requireActual('@backstage/core-plugin-api'),
3132
useRouteRef: () => (params: any) =>
3233
`/packages/${params.namespace}/${params.name}`,
3334
}));
3435

36+
jest.mock('../../hooks/useNodeEnvironment', () => ({
37+
useNodeEnvironment: jest.fn(),
38+
}));
39+
40+
const useNodeEnvironmentMock = useNodeEnvironment as jest.Mock;
41+
3542
describe('InstalledPackagesTable', () => {
3643
const renderWithProviders = (apis: any) =>
3744
render(
@@ -156,6 +163,60 @@ describe('InstalledPackagesTable', () => {
156163
expect(disabledButtons.length).toBeGreaterThanOrEqual(3);
157164
});
158165

166+
it('disables actions in the production env', async () => {
167+
useNodeEnvironmentMock.mockReturnValue({
168+
data: {
169+
nodeEnv: 'production',
170+
},
171+
});
172+
const dynamicPlugins = [
173+
{
174+
name: '@scope/pkg-a-dynamic',
175+
version: '1.0.0',
176+
role: 'frontend-plugin',
177+
platform: 'fe',
178+
},
179+
];
180+
181+
const entities = {
182+
items: [
183+
{
184+
apiVersion: 'extensions.backstage.io/v1alpha1',
185+
kind: 'Package',
186+
metadata: {
187+
namespace: 'rhdh',
188+
name: 'scope-pkg-a',
189+
title: 'Package A',
190+
},
191+
spec: { packageName: '@scope/pkg-a', version: '1.0.0' },
192+
},
193+
],
194+
totalItems: 1,
195+
pageInfo: {},
196+
};
197+
198+
const apis = [
199+
[
200+
dynamicPluginsInfoApiRef,
201+
{ listLoadedPlugins: jest.fn().mockResolvedValue(dynamicPlugins) },
202+
],
203+
[
204+
marketplaceApiRef,
205+
{ getPackages: jest.fn().mockResolvedValue(entities) },
206+
],
207+
] as const;
208+
209+
renderWithProviders(apis);
210+
211+
await waitFor(() =>
212+
expect(screen.getByText('Installed packages (1)')).toBeInTheDocument(),
213+
);
214+
215+
const disabledButtons = screen.getAllByRole('button', { hidden: true });
216+
// There are three disabled action buttons rendered wrapped in span
217+
expect(disabledButtons.length).toBeGreaterThanOrEqual(3);
218+
});
219+
159220
it('filters by search query', async () => {
160221
const dynamicPlugins = [
161222
{

workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,17 @@ export const InstalledPackagesTable = () => {
171171
render: (row: InstalledPackageRow) => {
172172
return (
173173
<Box display="flex" gap={1}>
174-
<EditPackage pkg={row} />
174+
<EditPackage pkg={row} isProductionEnv={isProductionEnvironment} />
175175
{/* Show it when uninstall functionality is implemented */}
176176
{showUninstall && <UninstallPackage pkg={row} />}
177-
<DownloadPackageYaml pkg={row} />
178-
<TogglePackage pkg={row} />
177+
<DownloadPackageYaml
178+
pkg={row}
179+
isProductionEnv={isProductionEnvironment}
180+
/>
181+
<TogglePackage
182+
pkg={row}
183+
isProductionEnv={isProductionEnvironment}
184+
/>
179185
</Box>
180186
);
181187
},

workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/RowActions.tsx

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,41 @@ export type InstalledPackageRow = {
4949
name?: string;
5050
};
5151

52-
export const DownloadPackageYaml = ({ pkg }: { pkg: InstalledPackageRow }) => {
52+
export const DownloadPackageYaml = ({
53+
pkg,
54+
isProductionEnv,
55+
}: {
56+
pkg: InstalledPackageRow;
57+
isProductionEnv: boolean;
58+
}) => {
5359
const { t } = useTranslation();
5460
const pkgConfig = usePackageConfig(pkg.namespace!, pkg.name!);
5561
const packageConfigPermission = usePluginConfigurationPermissions(
5662
pkg.namespace!,
5763
pkg.parentPlugin ?? '',
5864
);
65+
66+
const disabledIcon = (
67+
<Box component="span" display="inline-flex">
68+
<IconButton
69+
size="small"
70+
disabled
71+
sx={{ color: theme => theme.palette.action.disabled }}
72+
>
73+
<FileDownloadOutlinedIcon />
74+
</IconButton>
75+
</Box>
76+
);
77+
78+
if (isProductionEnv) {
79+
return (
80+
<Tooltip
81+
title={t('installedPackages.table.tooltips.packageProductionDisabled')}
82+
>
83+
{disabledIcon}
84+
</Tooltip>
85+
);
86+
}
5987
const disabled =
6088
!pkg.hasEntity || packageConfigPermission.data?.read !== 'ALLOW';
6189

@@ -68,15 +96,7 @@ export const DownloadPackageYaml = ({ pkg }: { pkg: InstalledPackageRow }) => {
6896
: t('installedPackages.table.tooltips.enableActions')
6997
}
7098
>
71-
<Box component="span" display="inline-flex">
72-
<IconButton
73-
size="small"
74-
disabled
75-
sx={{ color: theme => theme.palette.action.disabled }}
76-
>
77-
<FileDownloadOutlinedIcon />
78-
</IconButton>
79-
</Box>
99+
{disabledIcon}
80100
</Tooltip>
81101
);
82102
}
@@ -99,7 +119,13 @@ export const DownloadPackageYaml = ({ pkg }: { pkg: InstalledPackageRow }) => {
99119
);
100120
};
101121

102-
export const EditPackage = ({ pkg }: { pkg: InstalledPackageRow }) => {
122+
export const EditPackage = ({
123+
pkg,
124+
isProductionEnv,
125+
}: {
126+
pkg: InstalledPackageRow;
127+
isProductionEnv: boolean;
128+
}) => {
103129
const { t } = useTranslation();
104130
const navigate = useNavigate();
105131
const getPackagePath = useRouteRef(packageInstallRouteRef);
@@ -121,6 +147,16 @@ export const EditPackage = ({ pkg }: { pkg: InstalledPackageRow }) => {
121147
</Box>
122148
);
123149

150+
if (isProductionEnv) {
151+
return (
152+
<Tooltip
153+
title={t('installedPackages.table.tooltips.packageProductionDisabled')}
154+
>
155+
{disabledEditIcon}
156+
</Tooltip>
157+
);
158+
}
159+
124160
if (!pkg.hasEntity) {
125161
return (
126162
<Tooltip title={t('installedPackages.table.tooltips.enableActions')}>
@@ -158,7 +194,13 @@ export const EditPackage = ({ pkg }: { pkg: InstalledPackageRow }) => {
158194
);
159195
};
160196

161-
export const TogglePackage = ({ pkg }: { pkg: InstalledPackageRow }) => {
197+
export const TogglePackage = ({
198+
pkg,
199+
isProductionEnv,
200+
}: {
201+
pkg: InstalledPackageRow;
202+
isProductionEnv: boolean;
203+
}) => {
162204
const { t } = useTranslation();
163205
const { installedPackages, setInstalledPackages } = useInstallationContext();
164206
const { mutateAsync: enablePlugin } = useEnablePlugin(true);
@@ -170,6 +212,23 @@ export const TogglePackage = ({ pkg }: { pkg: InstalledPackageRow }) => {
170212
pkg.installStatus === MarketplacePackageInstallStatus.Installed ||
171213
pkg.installStatus === MarketplacePackageInstallStatus.UpdateAvailable,
172214
);
215+
const disabledIcon = (
216+
<Box component="span" display="inline-flex">
217+
<IconButton size="small" disabled>
218+
{isPackageEnabled ? <Switch checked disabled /> : <Switch disabled />}
219+
</IconButton>
220+
</Box>
221+
);
222+
223+
if (isProductionEnv) {
224+
return (
225+
<Tooltip
226+
title={t('installedPackages.table.tooltips.packageProductionDisabled')}
227+
>
228+
{disabledIcon}
229+
</Tooltip>
230+
);
231+
}
173232

174233
if (!pkg.hasEntity || packageConfigPermission.data?.write !== 'ALLOW') {
175234
return (
@@ -180,15 +239,7 @@ export const TogglePackage = ({ pkg }: { pkg: InstalledPackageRow }) => {
180239
: t('installedPackages.table.tooltips.enableActions')
181240
}
182241
>
183-
<Box component="span" display="inline-flex">
184-
<IconButton size="small" disabled>
185-
{isPackageEnabled ? (
186-
<Switch checked disabled />
187-
) : (
188-
<Switch disabled />
189-
)}
190-
</IconButton>
191-
</Box>
242+
{disabledIcon}
192243
</Tooltip>
193244
);
194245
}

workspaces/marketplace/plugins/marketplace/src/translations/de.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ const marketplaceTranslationDe = createTranslationMessages({
166166
'installedPackages.table.columns.role': 'Rolle',
167167
'installedPackages.table.columns.version': 'Version',
168168
'installedPackages.table.columns.actions': 'Aktionen',
169+
'installedPackages.table.tooltips.packageProductionDisabled':
170+
'Das Paket kann in der Produktionsumgebung nicht verwaltet werden.',
169171
'installedPackages.table.tooltips.enableActions':
170172
'Um Aktionen zu aktivieren, fügen Sie eine Katalogeintrag für dieses Paket hinzu',
171173
'installedPackages.table.tooltips.noDownloadPermissions':

workspaces/marketplace/plugins/marketplace/src/translations/es.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ const marketplaceTranslationEs = createTranslationMessages({
166166
'installedPackages.table.columns.role': 'Rol',
167167
'installedPackages.table.columns.version': 'Versión',
168168
'installedPackages.table.columns.actions': 'Acciones',
169+
'installedPackages.table.tooltips.packageProductionDisabled':
170+
'El paquete no puede ser gestionado en el entorno de producción.',
169171
'installedPackages.table.tooltips.enableActions':
170172
'Para habilitar acciones, agregue una entidad de catálogo para este paquete',
171173
'installedPackages.table.tooltips.noDownloadPermissions':

workspaces/marketplace/plugins/marketplace/src/translations/fr.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ const marketplaceTranslationFr = createTranslationMessages({
187187
'Aucun résultat trouvé. Essayez un autre terme de recherche.',
188188
'installedPackages.table.searchPlaceholder': 'Rechercher',
189189
'installedPackages.table.title': 'Paquets installés ({{count}})',
190+
'installedPackages.table.tooltips.packageProductionDisabled':
191+
"Le paquet ne peut pas être géré dans l'environnement de production.",
190192
'installedPackages.table.tooltips.enableActions':
191193
'Pour activer les actions, ajoutez une entité de catalogue pour ce package',
192194
'installedPackages.table.tooltips.noDownloadPermissions':

workspaces/marketplace/plugins/marketplace/src/translations/it.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ const marketplaceTranslationIt = createTranslationMessages({
165165
'installedPackages.table.columns.role': 'Ruolo',
166166
'installedPackages.table.columns.version': 'Versione',
167167
'installedPackages.table.columns.actions': 'Azioni',
168+
'installedPackages.table.tooltips.packageProductionDisabled':
169+
"Il pacchetto non può essere gestito nell'ambiente di produzione.",
168170
'installedPackages.table.tooltips.enableActions':
169171
"Per abilitare le azioni, aggiungi un'entità del catalogo per questo pacchetto",
170172
'installedPackages.table.tooltips.noDownloadPermissions':

workspaces/marketplace/plugins/marketplace/src/translations/ref.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ export const marketplaceMessages = {
192192
actions: 'Actions',
193193
},
194194
tooltips: {
195+
packageProductionDisabled:
196+
'Package cannot be managed in the production environment.',
195197
enableActions:
196198
'To enable actions, add a catalog entity for this package',
197199
noDownloadPermissions:

0 commit comments

Comments
 (0)