Remove all useState from EntityAirbrakeWidget.tsx

Signed-off-by: Karan Shah <karan.shah@simplybusiness.co.uk>
This commit is contained in:
Karan Shah
2022-03-03 23:48:10 +00:00
parent 4239b8dba6
commit c51587701b
4 changed files with 37 additions and 50 deletions
+1
View File
@@ -27,6 +27,7 @@
"@backstage/core-components": "^0.8.10",
"@backstage/core-plugin-api": "^0.7.0",
"@backstage/dev-utils": "^0.2.23",
"@backstage/errors": "^0.2.2",
"@backstage/plugin-catalog-react": "^0.7.0",
"@backstage/test-utils": "^0.2.6",
"@backstage/theme": "^0.2.15",
+2 -1
View File
@@ -16,6 +16,7 @@
import { Groups } from './airbrakeGroups';
import { createApiRef } from '@backstage/core-plugin-api';
import { CustomErrorBase } from '@backstage/errors';
export const airbrakeApiRef = createApiRef<AirbrakeApi>({
id: 'plugin.airbrake.service',
@@ -25,7 +26,7 @@ export interface AirbrakeApi {
fetchGroups(projectId: string): Promise<Groups>;
}
export class NoProjectIdError extends Error {
export class NoProjectIdError extends CustomErrorBase {
constructor() {
super('Project ID is not present');
}
@@ -18,7 +18,6 @@ import React from 'react';
import { EntityAirbrakeWidget } from './EntityAirbrakeWidget';
import exampleData from '../../api/mock/airbrakeGroupsApiMock.json';
import {
MockErrorApi,
renderInTestApp,
setupRequestMockHandlers,
TestApiProvider,
@@ -30,7 +29,6 @@ import {
MockAirbrakeApi,
ProductionAirbrakeApi,
} from '../../api';
import { errorApiRef } from '@backstage/core-plugin-api';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
@@ -53,14 +51,9 @@ describe('EntityAirbrakeWidget', () => {
});
it('states that the annotation is missing if no project ID annotation is provided but does not error', async () => {
const mockErrorApi = new MockErrorApi({ collect: true });
const widget = await renderInTestApp(
<TestApiProvider
apis={[
[airbrakeApiRef, new ProductionAirbrakeApi(localDiscoveryApi)],
[errorApiRef, mockErrorApi],
]}
apis={[[airbrakeApiRef, new ProductionAirbrakeApi(localDiscoveryApi)]]}
>
<EntityAirbrakeWidget entity={createEntity()} />
</TestApiProvider>,
@@ -68,7 +61,9 @@ describe('EntityAirbrakeWidget', () => {
await expect(
widget.findByText('Missing Annotation'),
).resolves.toBeInTheDocument();
expect(mockErrorApi.getErrors().length).toBe(0);
expect(
widget.queryByText(/.*Failed fetching Airbrake groups.*/),
).not.toBeInTheDocument();
});
it('states that an error occurred if the API call fails', async () => {
@@ -80,14 +75,10 @@ describe('EntityAirbrakeWidget', () => {
},
),
);
const mockErrorApi = new MockErrorApi({ collect: true });
const widget = await renderInTestApp(
<TestApiProvider
apis={[
[airbrakeApiRef, new ProductionAirbrakeApi(localDiscoveryApi)],
[errorApiRef, mockErrorApi],
]}
apis={[[airbrakeApiRef, new ProductionAirbrakeApi(localDiscoveryApi)]]}
>
<EntityAirbrakeWidget entity={createEntity(123)} />
</TestApiProvider>,
@@ -96,9 +87,10 @@ describe('EntityAirbrakeWidget', () => {
await expect(
widget.findByText(/.*there was an issue communicating with Airbrake.*/),
).resolves.toBeInTheDocument();
expect(mockErrorApi.getErrors().length).toBe(1);
expect(mockErrorApi.getErrors()[0].error.message).toStrictEqual(
'Failed fetching Airbrake groups',
);
expect(
widget.getByRole('heading', {
name: /.*Failed fetching Airbrake groups.*/,
}),
).toBeInTheDocument();
});
});
@@ -14,10 +14,11 @@
* limitations under the License.
*/
import { Entity } from '@backstage/catalog-model';
import React, { useEffect, useState } from 'react';
import React from 'react';
import { Grid, Typography } from '@material-ui/core';
import {
EmptyState,
ErrorPanel,
InfoCard,
MissingAnnotationEmptyState,
Progress,
@@ -25,7 +26,7 @@ import {
import hash from 'object-hash';
import { makeStyles } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
import { ErrorApi, errorApiRef, useApi } from '@backstage/core-plugin-api';
import { useApi } from '@backstage/core-plugin-api';
import { airbrakeApiRef } from '../../api';
import useAsync from 'react-use/lib/useAsync';
import { AIRBRAKE_PROJECT_ID_ANNOTATION, useProjectId } from '../useProjectId';
@@ -47,33 +48,22 @@ export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => {
const classes = useStyles();
const projectId = useProjectId(entity);
const errorApi = useApi<ErrorApi>(errorApiRef);
const airbrakeApi = useApi(airbrakeApiRef);
const [componentState, setComponentState] = useState<ComponentState>(
ComponentState.Loading,
);
let componentState = ComponentState.Loading;
const { loading, value } = useAsync(async () => {
try {
const result = await airbrakeApi.fetchGroups(projectId);
setComponentState(ComponentState.Loaded);
return result;
} catch (e) {
if (!projectId) {
setComponentState(ComponentState.NoProjectId);
} else {
setComponentState(ComponentState.Error);
errorApi.post(e);
}
throw e;
}
}, [airbrakeApi, errorApi, projectId]);
const { loading, value, error } = useAsync(() => {
return airbrakeApi.fetchGroups(projectId);
}, [airbrakeApi, projectId]);
useEffect(() => {
if (loading) {
setComponentState(ComponentState.Loading);
}
}, [loading]);
if (loading) {
componentState = ComponentState.Loading;
} else if (!projectId) {
componentState = ComponentState.NoProjectId;
} else if (error) {
componentState = ComponentState.Error;
} else if (value) {
componentState = ComponentState.Loaded;
}
switch (componentState) {
case ComponentState.Loaded:
@@ -103,11 +93,14 @@ export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => {
case ComponentState.Error:
default:
return (
<EmptyState
missing="info"
title="No information to display"
description={`There is no Airbrake project with id '${projectId}' or there was an issue communicating with Airbrake.`}
/>
<>
<ErrorPanel error={error as Error} />
<EmptyState
missing="info"
title="No information to display"
description={`There is no Airbrake project with id '${projectId}' or there was an issue communicating with Airbrake.`}
/>
</>
);
}
};