diff --git a/.changeset/giant-bottles-eat.md b/.changeset/giant-bottles-eat.md new file mode 100644 index 0000000000..d37343bfe4 --- /dev/null +++ b/.changeset/giant-bottles-eat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-airbrake': patch +--- + +Fix a bug where API calls were being made and errors were being added to the snack bar when no project ID was present. This is a common use case for components that haven't added the Airbrake plugin annotation to their `catalog-info.yaml`. diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index ee14a17f9a..e8d09b5e65 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -18,23 +18,21 @@ import React from 'react'; import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; import exampleData from '../../api/mock/airbrakeGroupsApiMock.json'; import { - MockErrorApi, renderInTestApp, setupRequestMockHandlers, TestApiProvider, } from '@backstage/test-utils'; -import { createEntity } from '../../api'; import { airbrakeApiRef, + createEntity, localDiscoveryApi, MockAirbrakeApi, ProductionAirbrakeApi, } from '../../api'; -import { errorApiRef } from '@backstage/core-plugin-api'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; -describe('EntityAirbrakeContent', () => { +describe('EntityAirbrakeWidget', () => { const worker = setupServer(); setupRequestMockHandlers(worker); @@ -52,15 +50,20 @@ describe('EntityAirbrakeContent', () => { } }); - it('states that the annotation is missing if no project ID annotation is provided', async () => { + it('states that the annotation is missing if no project ID annotation is provided but does not error', async () => { const widget = await renderInTestApp( - + , ); await expect( widget.findByText('Missing Annotation'), ).resolves.toBeInTheDocument(); + expect( + widget.queryByText(/.*Failed fetching Airbrake groups.*/), + ).not.toBeInTheDocument(); }); it('states that an error occurred if the API call fails', async () => { @@ -72,14 +75,10 @@ describe('EntityAirbrakeContent', () => { }, ), ); - const mockErrorApi = new MockErrorApi({ collect: true }); const widget = await renderInTestApp( , @@ -88,9 +87,10 @@ describe('EntityAirbrakeContent', () => { 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(); }); }); diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx index b11116aeef..6c2e4b8686 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx @@ -14,10 +14,11 @@ * limitations under the License. */ import { Entity } from '@backstage/catalog-model'; -import React, { useEffect } 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'; @@ -40,55 +41,50 @@ export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => { const classes = useStyles(); const projectId = useProjectId(entity); - const errorApi = useApi(errorApiRef); const airbrakeApi = useApi(airbrakeApiRef); - const { loading, value, error } = useAsync( - () => airbrakeApi.fetchGroups(projectId), - [airbrakeApi, projectId], - ); - - useEffect(() => { - if (error) { - errorApi.post(error); + const { loading, value, error } = useAsync(() => { + if (!projectId) { + return Promise.resolve(undefined); } - }, [error, errorApi]); - if (loading || !projectId || error) { + return airbrakeApi.fetchGroups(projectId); + }, [airbrakeApi, projectId]); + + if (!projectId) { return ( - - {loading && } - - {!loading && !projectId && ( - - )} - - {!loading && error && ( - - )} - + + ); + } else if (loading) { + return ; + } else if (value) { + return ( + + {value.groups?.map(group => ( + + {group.errors?.map(groupError => ( + + + {groupError.message} + + + ))} + + ))} + ); } return ( - - {value?.groups?.map(group => ( - - {group.errors?.map(groupError => ( - - - {groupError.message} - - - ))} - - ))} - + <> + {error && } + + ); };