From 1f2757bb07d1c142fc857a2f84fd14522d49d1cb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 15 Mar 2022 11:25:19 +0100 Subject: [PATCH] catalog-react: remove deprecated loading state of useEntity Signed-off-by: Patrik Oldsberg --- .changeset/thin-candles-run.md | 5 ++++ plugins/catalog-react/api-report.md | 3 --- .../src/hooks/useEntity.test.tsx | 27 +++++++++---------- plugins/catalog-react/src/hooks/useEntity.tsx | 18 +++---------- .../src/hooks/useEntityPermission.test.tsx | 6 ++--- .../src/hooks/useEntityPermission.ts | 8 ++++-- 6 files changed, 30 insertions(+), 37 deletions(-) create mode 100644 .changeset/thin-candles-run.md diff --git a/.changeset/thin-candles-run.md b/.changeset/thin-candles-run.md new file mode 100644 index 0000000000..2776373569 --- /dev/null +++ b/.changeset/thin-candles-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': minor +--- + +**BREAKING**: The `useEntity` hook no longer returns loading or error states, and will throw an error if the entity is not immediately available. In practice this means that `useEntity` can only be used in contexts where the entity is guaranteed to have been loaded, for example inside an `EntityLayout`. To access the loading state of the entity, use `useAsyncEntity` instead. diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 7859e1d2d8..265fce417d 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -472,9 +472,6 @@ export function useAsyncEntity< // @public export function useEntity(): { entity: TEntity; - loading: boolean; - error?: Error; - refresh?: VoidFunction; }; // @public diff --git a/plugins/catalog-react/src/hooks/useEntity.test.tsx b/plugins/catalog-react/src/hooks/useEntity.test.tsx index f61283f2cf..5e86a94180 100644 --- a/plugins/catalog-react/src/hooks/useEntity.test.tsx +++ b/plugins/catalog-react/src/hooks/useEntity.test.tsx @@ -16,19 +16,21 @@ import React from 'react'; import { renderHook } from '@testing-library/react-hooks'; -import { useEntity, EntityProvider, AsyncEntityProvider } from './useEntity'; +import { + useEntity, + useAsyncEntity, + EntityProvider, + AsyncEntityProvider, +} from './useEntity'; import { Entity } from '@backstage/catalog-model'; -describe('EntityProvider', () => { - it('should provide no entity', async () => { +describe('useEntity', () => { + it('should throw if no entity is provided', async () => { const { result } = renderHook(() => useEntity(), { wrapper: ({ children }) => , }); - expect(result.current.entity).toBe(undefined); - expect(result.current.loading).toBe(true); - expect(result.current.error).toBe(undefined); - expect(result.current.refresh).toBe(undefined); + expect(result.error?.message).toMatch(/entity has not been loaded/); }); it('should provide an entity', async () => { @@ -40,15 +42,12 @@ describe('EntityProvider', () => { }); expect(result.current.entity).toBe(entity); - expect(result.current.loading).toBe(false); - expect(result.current.error).toBe(undefined); - expect(result.current.refresh).toBe(undefined); }); }); -describe('AsyncEntityProvider', () => { +describe('useAsyncEntity', () => { it('should provide no entity', async () => { - const { result } = renderHook(() => useEntity(), { + const { result } = renderHook(() => useAsyncEntity(), { wrapper: ({ children }) => ( ), @@ -63,7 +62,7 @@ describe('AsyncEntityProvider', () => { it('should provide an entity', async () => { const entity = { kind: 'MyEntity' } as Entity; const refresh = () => {}; - const { result } = renderHook(() => useEntity(), { + const { result } = renderHook(() => useAsyncEntity(), { wrapper: ({ children }) => ( { it('should provide an error', async () => { const error = new Error('oh no'); - const { result } = renderHook(() => useEntity(), { + const { result } = renderHook(() => useAsyncEntity(), { wrapper: ({ children }) => ( ( */ export function useEntity(): { entity: TEntity; - /** @deprecated use {@link useAsyncEntity} instead */ - loading: boolean; - /** @deprecated use {@link useAsyncEntity} instead */ - error?: Error; - /** @deprecated use {@link useAsyncEntity} instead */ - refresh?: VoidFunction; } { const versionedHolder = useVersionedContext<{ 1: EntityLoadingStatus }>('entity-context'); @@ -123,18 +117,12 @@ export function useEntity(): { } if (!value.entity) { - // Once we have removed the additional fields from being returned we can drop this deprecation - // and move to the error instead. - // throw new Error('useEntity hook is being called outside of an EntityLayout where the entity has not been loaded. If this is intentional, please use useAsyncEntity instead.'); - - // eslint-disable-next-line no-console - console.warn( - 'DEPRECATION: useEntity hook is being called outside of an EntityLayout where the entity has not been loaded. If this is intentional, please use useAsyncEntity instead. This warning will be replaced with an error in future releases.', + throw new Error( + 'useEntity hook is being called outside of an EntityLayout where the entity has not been loaded. If this is intentional, please use useAsyncEntity instead.', ); } - const { entity, loading, error, refresh } = value; - return { entity: entity as TEntity, loading, error, refresh }; + return { entity: value.entity as TEntity }; } /** diff --git a/plugins/catalog-react/src/hooks/useEntityPermission.test.tsx b/plugins/catalog-react/src/hooks/useEntityPermission.test.tsx index 553d999b61..766b587da3 100644 --- a/plugins/catalog-react/src/hooks/useEntityPermission.test.tsx +++ b/plugins/catalog-react/src/hooks/useEntityPermission.test.tsx @@ -17,18 +17,18 @@ import { catalogEntityDeletePermission } from '@backstage/plugin-catalog-common'; import { renderHook } from '@testing-library/react-hooks'; import { useEntityPermission } from './useEntityPermission'; -import { useEntity } from './useEntity'; +import { useAsyncEntity } from './useEntity'; import { usePermission } from '@backstage/plugin-permission-react'; jest.mock('./useEntity', () => ({ ...jest.requireActual('./useEntity'), - useEntity: jest.fn(), + useAsyncEntity: jest.fn(), })); jest.mock('@backstage/plugin-permission-react', () => ({ ...jest.requireActual('@backstage/plugin-permission-react'), usePermission: jest.fn(), })); -const useEntityMock = useEntity as jest.Mock; +const useEntityMock = useAsyncEntity as jest.Mock; const usePermissionMock = usePermission as jest.Mock; describe('useEntityPermission', () => { diff --git a/plugins/catalog-react/src/hooks/useEntityPermission.ts b/plugins/catalog-react/src/hooks/useEntityPermission.ts index 092114936a..55d863726e 100644 --- a/plugins/catalog-react/src/hooks/useEntityPermission.ts +++ b/plugins/catalog-react/src/hooks/useEntityPermission.ts @@ -17,7 +17,7 @@ import { stringifyEntityRef } from '@backstage/catalog-model'; import { Permission } from '@backstage/plugin-permission-common'; import { usePermission } from '@backstage/plugin-permission-react'; -import { useEntity } from './useEntity'; +import { useAsyncEntity } from './useEntity'; /** * A thin wrapper around the @@ -35,7 +35,11 @@ export function useEntityPermission(permission: Permission): { allowed: boolean; error?: Error; } { - const { entity, loading: loadingEntity, error: entityError } = useEntity(); + const { + entity, + loading: loadingEntity, + error: entityError, + } = useAsyncEntity(); const { allowed, loading: loadingPermission,