Add caching to the useEntityPermission hook (#9064)
* Add caching to the useEntityPermission hook Signed-off-by: Joon Park <joonp@spotify.com> * Remove useRef Signed-off-by: Joon Park <joonp@spotify.com> * Use useSWR hook Signed-off-by: Joon Park <joonp@spotify.com>
This commit is contained in:
@@ -32,7 +32,8 @@
|
||||
"@backstage/plugin-permission-common": "^0.4.0",
|
||||
"cross-fetch": "^3.0.6",
|
||||
"react-router": "6.0.0-beta.0",
|
||||
"react-use": "^17.2.4"
|
||||
"react-use": "^17.2.4",
|
||||
"swr": "^1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.13.1 || ^17.0.0",
|
||||
|
||||
@@ -19,9 +19,8 @@ import { render } from '@testing-library/react';
|
||||
import { usePermission } from './usePermission';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { permissionApiRef } from '../apis';
|
||||
|
||||
const mockAuthorize = jest.fn();
|
||||
import { PermissionApi, permissionApiRef } from '../apis';
|
||||
import { SWRConfig } from 'swr';
|
||||
|
||||
const permission = {
|
||||
name: 'access.something',
|
||||
@@ -39,49 +38,48 @@ const TestComponent: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
describe('usePermission', () => {
|
||||
it('Returns loading when permissionApi has not yet responded.', () => {
|
||||
mockAuthorize.mockReturnValueOnce(new Promise(() => {}));
|
||||
|
||||
const { getByText } = render(
|
||||
<TestApiProvider
|
||||
apis={[[permissionApiRef, { authorize: mockAuthorize }]]}
|
||||
>
|
||||
function renderComponent(mockApi: PermissionApi) {
|
||||
return render(
|
||||
<SWRConfig value={{ provider: () => new Map() }}>
|
||||
<TestApiProvider apis={[[permissionApiRef, mockApi]]}>
|
||||
<TestComponent />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
</TestApiProvider>
|
||||
,
|
||||
</SWRConfig>,
|
||||
);
|
||||
}
|
||||
|
||||
expect(mockAuthorize).toHaveBeenCalledWith({ permission });
|
||||
describe('usePermission', () => {
|
||||
const mockPermissionApi = { authorize: jest.fn() };
|
||||
|
||||
it('Returns loading when permissionApi has not yet responded.', () => {
|
||||
mockPermissionApi.authorize.mockReturnValueOnce(new Promise(() => {}));
|
||||
|
||||
const { getByText } = renderComponent(mockPermissionApi);
|
||||
|
||||
expect(mockPermissionApi.authorize).toHaveBeenCalledWith({ permission });
|
||||
expect(getByText('loading')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Returns allowed when permissionApi allows authorization.', async () => {
|
||||
mockAuthorize.mockResolvedValueOnce({ result: AuthorizeResult.ALLOW });
|
||||
mockPermissionApi.authorize.mockResolvedValueOnce({
|
||||
result: AuthorizeResult.ALLOW,
|
||||
});
|
||||
|
||||
const { findByText } = render(
|
||||
<TestApiProvider
|
||||
apis={[[permissionApiRef, { authorize: mockAuthorize }]]}
|
||||
>
|
||||
<TestComponent />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
const { findByText } = renderComponent(mockPermissionApi);
|
||||
|
||||
expect(mockAuthorize).toHaveBeenCalledWith({ permission });
|
||||
expect(mockPermissionApi.authorize).toHaveBeenCalledWith({ permission });
|
||||
expect(await findByText('content')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Returns not allowed when permissionApi denies authorization.', async () => {
|
||||
mockAuthorize.mockResolvedValueOnce({ result: AuthorizeResult.DENY });
|
||||
mockPermissionApi.authorize.mockResolvedValueOnce({
|
||||
result: AuthorizeResult.DENY,
|
||||
});
|
||||
|
||||
const { findByText } = render(
|
||||
<TestApiProvider
|
||||
apis={[[permissionApiRef, { authorize: mockAuthorize }]]}
|
||||
>
|
||||
<TestComponent />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
const { findByText } = renderComponent(mockPermissionApi);
|
||||
|
||||
expect(mockAuthorize).toHaveBeenCalledWith({ permission });
|
||||
expect(mockPermissionApi.authorize).toHaveBeenCalledWith({ permission });
|
||||
await expect(findByText('content')).rejects.toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { permissionApiRef } from '../apis';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
Permission,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import useSWR from 'swr';
|
||||
|
||||
/** @public */
|
||||
export type AsyncPermissionResult = {
|
||||
@@ -30,9 +30,16 @@ export type AsyncPermissionResult = {
|
||||
};
|
||||
|
||||
/**
|
||||
* React hook utlity for authorization. Given a {@link @backstage/plugin-permission-common#Permission} and an optional
|
||||
* resourceRef, it will return whether or not access is allowed (for the given resource, if resourceRef is provided). See
|
||||
* {@link @backstage/plugin-permission-common/PermissionClient#authorize} for more details.
|
||||
* React hook utility for authorization. Given a
|
||||
* {@link @backstage/plugin-permission-common#Permission} and an optional
|
||||
* resourceRef, it will return whether or not access is allowed (for the given
|
||||
* resource, if resourceRef is provided). See
|
||||
* {@link @backstage/plugin-permission-common/PermissionClient#authorize} for
|
||||
* more details.
|
||||
*
|
||||
* Note: This hook uses stale-while-revalidate to help avoid flicker in UI
|
||||
* elements that would be conditionally rendered based on the `allowed` result
|
||||
* of this hook.
|
||||
* @public
|
||||
*/
|
||||
export const usePermission = (
|
||||
@@ -40,21 +47,16 @@ export const usePermission = (
|
||||
resourceRef?: string,
|
||||
): AsyncPermissionResult => {
|
||||
const permissionApi = useApi(permissionApiRef);
|
||||
|
||||
const { loading, error, value } = useAsync(async () => {
|
||||
const { result } = await permissionApi.authorize({
|
||||
permission,
|
||||
resourceRef,
|
||||
});
|
||||
|
||||
const { data, error } = useSWR({ permission, resourceRef }, async args => {
|
||||
const { result } = await permissionApi.authorize(args);
|
||||
return result;
|
||||
}, [permissionApi, permission, resourceRef]);
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return { loading: true, allowed: false };
|
||||
}
|
||||
if (error) {
|
||||
return { error, loading: false, allowed: false };
|
||||
}
|
||||
return { loading: false, allowed: value === AuthorizeResult.ALLOW };
|
||||
if (data === undefined) {
|
||||
return { loading: true, allowed: false };
|
||||
}
|
||||
return { loading: false, allowed: data === AuthorizeResult.ALLOW };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user