Skip to content

Commit a87ef79

Browse files
authored
fix(extensions): show error state when backend is missing (#783)
1 parent 49c61d3 commit a87ef79

5 files changed

Lines changed: 90 additions & 7 deletions

File tree

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+
show error state when backend is missing
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import React from 'react';
17+
import { renderInTestApp } from '@backstage/test-utils';
18+
import { MarketplaceCatalogContent } from './MarketplaceCatalogContent';
19+
import { useFilteredPlugins } from '../hooks/useFilteredPlugins';
20+
21+
const useFilteredPluginsMock = useFilteredPlugins as jest.Mock;
22+
23+
jest.mock('../hooks/useCollections', () => ({
24+
useCollections: jest.fn(),
25+
}));
26+
27+
jest.mock('../hooks/useFilteredPlugins', () => ({
28+
useFilteredPlugins: jest.fn(),
29+
}));
30+
31+
describe('MarketplaceCatalogContent', () => {
32+
it('should show empty state with no plugins', async () => {
33+
useFilteredPluginsMock.mockReturnValue({
34+
data: {
35+
totalItems: 0,
36+
},
37+
});
38+
39+
const { getByText } = await renderInTestApp(<MarketplaceCatalogContent />);
40+
expect(getByText('No plugins found')).toBeInTheDocument();
41+
});
42+
43+
it('should show empty state with no extensions backend found', async () => {
44+
useFilteredPluginsMock.mockReturnValue({
45+
error: {
46+
message: '404',
47+
},
48+
});
49+
50+
const { getByText } = await renderInTestApp(<MarketplaceCatalogContent />);
51+
expect(
52+
getByText('Must enable the Extensions backend plugin'),
53+
).toBeInTheDocument();
54+
});
55+
});

workspaces/marketplace/plugins/marketplace/src/components/MarketplaceCatalogContent.tsx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import Card from '@mui/material/Card';
2323
import Grid from '@mui/material/Grid';
2424
import Stack from '@mui/material/Stack';
2525
import Typography from '@mui/material/Typography';
26+
import Launch from '@mui/icons-material/Launch';
2627

2728
import { SearchTextField } from '../shared-components/SearchTextField';
2829

@@ -34,7 +35,7 @@ import { CollectionHorizontalScrollRow } from './CollectionHorizontalScrollRow';
3435

3536
import notFoundImag from '../assets/notfound.png';
3637

37-
const NoPluginsFound = () => (
38+
const EmptyState = ({ isError }: { isError?: boolean }) => (
3839
<Content>
3940
<Grid
4041
container
@@ -43,19 +44,35 @@ const NoPluginsFound = () => (
4344
>
4445
<Grid item xs={6}>
4546
<Stack gap={3} justifyContent="center">
46-
<Typography variant="h1">No plugins found</Typography>
47+
<Typography variant="h1">
48+
{isError
49+
? 'Must enable the Extensions backend plugin'
50+
: 'No plugins found'}
51+
</Typography>
4752
<Typography variant="body1">
48-
There was an error with loading plugins. Check your configuration or
49-
review plugin documentation to resolve. You can also explore other
50-
available plugins.
53+
{isError
54+
? "Configure the '@red-hat-developer-hub/backstage-plugin-marketplace-backend' plugin."
55+
: 'There was an error with loading plugins. Check your configuration or review plugin documentation to resolve. You can also explore other available plugins.'}
5156
</Typography>
5257
<Grid container spacing={2}>
58+
{!isError && (
59+
<Grid item>
60+
<LinkButton
61+
variant="contained"
62+
color="primary"
63+
to="https://developers.redhat.com/products/rhdh/plugins#communitypreinstalled"
64+
endIcon={<Launch />}
65+
>
66+
View all plugins
67+
</LinkButton>
68+
</Grid>
69+
)}
5370
<Grid item>
5471
<LinkButton
5572
variant="outlined"
5673
color="primary"
5774
to="https://docs.redhat.com/en/documentation/red_hat_developer_hub/"
58-
externalLinkIcon
75+
endIcon={<Launch />}
5976
>
6077
View documentation
6178
</LinkButton>
@@ -90,8 +107,12 @@ export const MarketplaceCatalogContent = () => {
90107
title += ` (${filteredPlugins.data.filteredItems})`;
91108
}
92109

110+
if (filteredPlugins.error?.message.includes('404')) {
111+
return <EmptyState isError />;
112+
}
113+
93114
if (filteredPlugins.data?.totalItems === 0) {
94-
return <NoPluginsFound />;
115+
return <EmptyState />;
95116
}
96117

97118
return (

workspaces/marketplace/plugins/marketplace/src/hooks/useCollections.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ export const useCollections = (request: GetEntitiesRequest) => {
2525
return useQuery({
2626
queryKey: ['marketplaceApi', 'getCollections', request],
2727
queryFn: () => marketplaceApi.getCollections(request),
28+
refetchOnWindowFocus: false,
2829
});
2930
};

workspaces/marketplace/plugins/marketplace/src/hooks/useFilteredPlugins.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const useFilteredPlugins = () => {
4242

4343
const marketplaceApi = useMarketplaceApi();
4444
return useQuery({
45+
refetchOnWindowFocus: false,
4546
queryKey: ['marketplaceApi', 'getPlugins', filteredPluginsRequest],
4647
queryFn: () => marketplaceApi.getPlugins(filteredPluginsRequest),
4748
select: data => {

0 commit comments

Comments
 (0)