Handle request errors properly and display them in the results list.

Signed-off-by: Dominik Henneke <dominik.henneke@sda-se.com>
This commit is contained in:
Dominik Henneke
2021-07-14 09:52:04 +02:00
parent 474cb76c88
commit 078d4973e3
6 changed files with 44 additions and 21 deletions
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search': patch
---
Handle request errors properly and display them in the results list.
+1
View File
@@ -33,6 +33,7 @@
"@backstage/config": "^0.1.5",
"@backstage/core-components": "^0.1.5",
"@backstage/core-plugin-api": "^0.1.3",
"@backstage/errors": "^0.1.1",
"@backstage/plugin-catalog-react": "^0.2.6",
"@backstage/search-common": "^0.1.2",
"@backstage/theme": "^0.2.8",
+1 -1
View File
@@ -44,7 +44,7 @@ describe('apis', () => {
const json = jest.fn();
const originalFetch = window.fetch;
window.fetch = jest.fn().mockResolvedValue({ json });
window.fetch = jest.fn().mockResolvedValue({ json, ok: true });
afterAll(() => {
window.fetch = originalFetch;
+8 -2
View File
@@ -14,13 +14,14 @@
* limitations under the License.
*/
import { SearchQuery, SearchResultSet } from '@backstage/search-common';
import qs from 'qs';
import {
createApiRef,
DiscoveryApi,
IdentityApi,
} from '@backstage/core-plugin-api';
import { ResponseError } from '@backstage/errors';
import { SearchQuery, SearchResultSet } from '@backstage/search-common';
import qs from 'qs';
export const searchApiRef = createApiRef<SearchApi>({
id: 'plugin.search.queryservice',
@@ -52,6 +53,11 @@ export class SearchClient implements SearchApi {
const response = await fetch(url, {
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
if (!response.ok) {
throw await ResponseError.fromResponse(response);
}
return response.json();
}
}
@@ -14,11 +14,11 @@
* limitations under the License.
*/
import { renderInTestApp } from '@backstage/test-utils';
import { waitFor } from '@testing-library/react';
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { SearchResult } from './SearchResult';
import { useSearch } from '../SearchContext';
import { SearchResult } from './SearchResult';
jest.mock('../SearchContext', () => ({
...jest.requireActual('../SearchContext'),
@@ -33,7 +33,9 @@ describe('SearchResult', () => {
result: { loading: true },
});
const { getByRole } = render(<SearchResult>{() => <></>}</SearchResult>);
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(getByRole('progressbar')).toBeInTheDocument();
@@ -41,16 +43,18 @@ describe('SearchResult', () => {
});
it('Alert rendered on Error state', async () => {
const error = 'error';
const error = new Error('some error');
(useSearch as jest.Mock).mockReturnValueOnce({
result: { loading: false, error },
});
const { getByRole } = render(<SearchResult>{() => <></>}</SearchResult>);
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(getByRole('alert')).toHaveTextContent(
`Error encountered while fetching search results. ${error}`,
new RegExp(`Error encountered while fetching search results.*${error}`),
);
});
});
@@ -60,7 +64,9 @@ describe('SearchResult', () => {
result: { loading: false, error: '', value: undefined },
});
const { getByRole } = render(<SearchResult>{() => <></>}</SearchResult>);
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(
@@ -74,7 +80,9 @@ describe('SearchResult', () => {
result: { loading: false, error: '', value: { results: [] } },
});
const { getByRole } = render(<SearchResult>{() => <></>}</SearchResult>);
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(
@@ -83,12 +91,12 @@ describe('SearchResult', () => {
});
});
it('Calls children with results set to result.value', () => {
it('Calls children with results set to result.value', async () => {
(useSearch as jest.Mock).mockReturnValueOnce({
result: { loading: false, error: '', value: { results: [] } },
});
render(
await renderInTestApp(
<SearchResult>
{({ results }) => {
expect(results).toEqual([]);
@@ -14,13 +14,15 @@
* limitations under the License.
*/
import React from 'react';
import {
EmptyState,
Progress,
ResponseErrorPanel,
} from '@backstage/core-components';
import { SearchResult } from '@backstage/search-common';
import { Alert } from '@material-ui/lab';
import React from 'react';
import { useSearch } from '../SearchContext';
import { EmptyState, Progress } from '@backstage/core-components';
type Props = {
children: (results: { results: SearchResult[] }) => JSX.Element;
};
@@ -35,9 +37,10 @@ const SearchResultComponent = ({ children }: Props) => {
}
if (error) {
return (
<Alert severity="error">
Error encountered while fetching search results. {error.toString()}
</Alert>
<ResponseErrorPanel
title="Error encountered while fetching search results"
error={error}
/>
);
}