Remove all useState from EntityAirbrakeWidget.tsx
Signed-off-by: Karan Shah <karan.shah@simplybusiness.co.uk>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
+10
-18
@@ -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.`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user