permission-react: more restrictive typing for usePermission and PermissionedRoute
Signed-off-by: Mike Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
committed by
Joe Porpeglia
parent
9da18e4715
commit
5bdcb8c45d
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-permission-react': minor
|
||||
---
|
||||
|
||||
**BREAKING**: More restrictive typing for `usePermission` hook and `PermissionedRoute` component. It's no longer possible to pass a `resourceRef` unless the permission is of type `ResourcPermission`.
|
||||
@@ -12,6 +12,7 @@ import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import { ReactElement } from 'react';
|
||||
import { ResourcePermission } from '@backstage/plugin-permission-common';
|
||||
import { Route } from 'react-router';
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -44,17 +45,25 @@ export const permissionApiRef: ApiRef<PermissionApi>;
|
||||
// @public
|
||||
export const PermissionedRoute: (
|
||||
props: ComponentProps<typeof Route> & {
|
||||
permission: Permission;
|
||||
resourceRef?: string;
|
||||
errorComponent?: ReactElement | null;
|
||||
},
|
||||
} & (
|
||||
| {
|
||||
permission: Exclude<Permission, ResourcePermission>;
|
||||
resourceRef?: never;
|
||||
}
|
||||
| {
|
||||
permission: ResourcePermission;
|
||||
resourceRef: string | undefined;
|
||||
}
|
||||
),
|
||||
) => JSX.Element;
|
||||
|
||||
// @public
|
||||
export const usePermission: (
|
||||
permission: Permission,
|
||||
resourceRef?: string | undefined,
|
||||
) => AsyncPermissionResult;
|
||||
export function usePermission(
|
||||
...[permission, resourceRef]:
|
||||
| [ResourcePermission, string | undefined]
|
||||
| [Exclude<Permission, ResourcePermission>]
|
||||
): AsyncPermissionResult;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -18,7 +18,11 @@ import React, { ComponentProps, ReactElement } from 'react';
|
||||
import { Route } from 'react-router';
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
import { usePermission } from '../hooks';
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
isResourcePermission,
|
||||
Permission,
|
||||
ResourcePermission,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
|
||||
/**
|
||||
* Returns a React Router Route which only renders the element when authorized. If unauthorized, the Route will render a
|
||||
@@ -28,13 +32,25 @@ import { Permission } from '@backstage/plugin-permission-common';
|
||||
*/
|
||||
export const PermissionedRoute = (
|
||||
props: ComponentProps<typeof Route> & {
|
||||
permission: Permission;
|
||||
resourceRef?: string;
|
||||
errorComponent?: ReactElement | null;
|
||||
},
|
||||
} & (
|
||||
| {
|
||||
permission: Exclude<Permission, ResourcePermission>;
|
||||
resourceRef?: never;
|
||||
}
|
||||
| {
|
||||
permission: ResourcePermission;
|
||||
resourceRef: string | undefined;
|
||||
}
|
||||
),
|
||||
) => {
|
||||
const { permission, resourceRef, errorComponent, ...otherProps } = props;
|
||||
const permissionResult = usePermission(permission, resourceRef);
|
||||
|
||||
const permissionResult = usePermission(
|
||||
...(isResourcePermission(permission)
|
||||
? [permission, resourceRef]
|
||||
: [permission]),
|
||||
);
|
||||
const app = useApp();
|
||||
const { NotFoundErrorPage } = app.getComponents();
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { permissionApiRef } from '../apis';
|
||||
import {
|
||||
AuthorizeQuery,
|
||||
AuthorizeResult,
|
||||
isResourcePermission,
|
||||
Permission,
|
||||
ResourcePermission,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import useSWR from 'swr';
|
||||
|
||||
@@ -30,25 +33,40 @@ export type AsyncPermissionResult = {
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* React hook utility for authorization. Given either a non-resource
|
||||
* {@link @backstage/plugin-permission-common#Permission} or a
|
||||
* {@link @backstage/plugin-permission-common#ResourcePermission} 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.
|
||||
*
|
||||
* The resourceRef parameter is optional to allow calling this hook with an
|
||||
* entity that might be loading asynchronously, but when resourceRef is not
|
||||
* supplied, the value of `allowed` will always be false.
|
||||
*
|
||||
* 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 = (
|
||||
permission: Permission,
|
||||
resourceRef?: string,
|
||||
): AsyncPermissionResult => {
|
||||
export function usePermission(
|
||||
...[permission, resourceRef]:
|
||||
| [ResourcePermission, string | undefined]
|
||||
| [Exclude<Permission, ResourcePermission>]
|
||||
): AsyncPermissionResult {
|
||||
const permissionApi = useApi(permissionApiRef);
|
||||
const { data, error } = useSWR({ permission, resourceRef }, async args => {
|
||||
const { result } = await permissionApi.authorize(args);
|
||||
// We could make the resourceRef parameter required to avoid this check, but
|
||||
// it would make using this hook difficult in situations where the entity
|
||||
// must be asynchronously loaded, so instead we short-circuit to a deny when
|
||||
// no resourceRef is supplied, on the assumption that the resourceRef is
|
||||
// still loading outside the hook.
|
||||
if (isResourcePermission(args.permission) && !args.resourceRef) {
|
||||
return AuthorizeResult.DENY;
|
||||
}
|
||||
|
||||
const { result } = await permissionApi.authorize(args as AuthorizeQuery);
|
||||
return result;
|
||||
});
|
||||
|
||||
@@ -59,4 +77,4 @@ export const usePermission = (
|
||||
return { loading: true, allowed: false };
|
||||
}
|
||||
return { loading: false, allowed: data === AuthorizeResult.ALLOW };
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user