feat: add permission to catalog create and refresh buttion
Signed-off-by: rui ma <ruima@alauda.io>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Add permission check to catalog create and refresh button
|
||||
@@ -56,6 +56,7 @@
|
||||
"@backstage/integration-react": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/plugin-permission-react": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-common": "workspace:^",
|
||||
"@backstage/plugin-search-common": "workspace:^",
|
||||
"@backstage/plugin-search-react": "workspace:^",
|
||||
@@ -83,7 +84,7 @@
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/dev-utils": "workspace:^",
|
||||
"@backstage/plugin-permission-react": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/dom": "^9.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
|
||||
@@ -33,6 +33,12 @@ import { RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { permissionApiRef } from '@backstage/plugin-permission-react';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
|
||||
const mockAuthorize = jest.fn();
|
||||
|
||||
const mockPermissionApi = { authorize: mockAuthorize };
|
||||
|
||||
describe('<AboutCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
@@ -87,6 +93,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -143,6 +150,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -198,6 +206,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -240,6 +249,7 @@ describe('<AboutCard />', () => {
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -276,6 +286,10 @@ describe('<AboutCard />', () => {
|
||||
},
|
||||
};
|
||||
|
||||
mockAuthorize.mockImplementation(async () => ({
|
||||
result: AuthorizeResult.ALLOW,
|
||||
}));
|
||||
|
||||
await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
@@ -284,6 +298,7 @@ describe('<AboutCard />', () => {
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, mockPermissionApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -308,6 +323,55 @@ describe('<AboutCard />', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render refresh button if the permission is DENY', async () => {
|
||||
const entity = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
annotations: {
|
||||
'backstage.io/managed-by-location':
|
||||
'url:https://backstage.io/catalog-info.yaml',
|
||||
},
|
||||
name: 'software-deny',
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
|
||||
mockAuthorize.mockImplementation(async () => ({
|
||||
result: AuthorizeResult.DENY,
|
||||
}));
|
||||
|
||||
await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, mockPermissionApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByTitle('Schedule entity refresh'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render refresh button if the location is not an url or file', async () => {
|
||||
const entity = {
|
||||
apiVersion: 'v1',
|
||||
@@ -330,6 +394,7 @@ describe('<AboutCard />', () => {
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -384,6 +449,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -440,6 +506,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -493,6 +560,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -546,6 +614,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -592,6 +661,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -659,6 +729,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
@@ -707,6 +778,7 @@ describe('<AboutCard />', () => {
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
[permissionApiRef, {}],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
|
||||
@@ -60,6 +60,8 @@ import DocsIcon from '@material-ui/icons/Description';
|
||||
import EditIcon from '@material-ui/icons/Edit';
|
||||
import { isTemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { parseEntityRef } from '@backstage/catalog-model';
|
||||
import { useEntityPermission } from '@backstage/plugin-catalog-react/alpha';
|
||||
import { catalogEntityRefreshPermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
|
||||
const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
|
||||
|
||||
@@ -108,6 +110,9 @@ export function AboutCard(props: AboutCardProps) {
|
||||
const errorApi = useApi(errorApiRef);
|
||||
const viewTechdocLink = useRouteRef(viewTechDocRouteRef);
|
||||
const templateRoute = useRouteRef(createFromTemplateRouteRef);
|
||||
const { allowed: canRefresh } = useEntityPermission(
|
||||
catalogEntityRefreshPermission,
|
||||
);
|
||||
|
||||
const entitySourceLocation = getEntitySourceLocation(
|
||||
entity,
|
||||
@@ -215,7 +220,7 @@ export function AboutCard(props: AboutCardProps) {
|
||||
title="About"
|
||||
action={
|
||||
<>
|
||||
{allowRefresh && (
|
||||
{allowRefresh && canRefresh && (
|
||||
<IconButton
|
||||
aria-label="Refresh"
|
||||
title="Schedule entity refresh"
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { mockBreakpoint } from '@backstage/core-components/testUtils';
|
||||
import {
|
||||
MockPermissionApi,
|
||||
MockStorageApi,
|
||||
TestApiProvider,
|
||||
renderInTestApp,
|
||||
@@ -46,6 +47,7 @@ import { CatalogTableRow } from '../CatalogTable';
|
||||
import { DefaultCatalogPage } from './DefaultCatalogPage';
|
||||
|
||||
import { CatalogTableColumnsFunc } from '../CatalogTable/types';
|
||||
import { permissionApiRef } from '@backstage/plugin-permission-react';
|
||||
|
||||
describe('DefaultCatalogPage', () => {
|
||||
const origReplaceState = window.history.replaceState;
|
||||
@@ -168,6 +170,7 @@ describe('DefaultCatalogPage', () => {
|
||||
[identityApiRef, identityApi],
|
||||
[storageApiRef, storageApi],
|
||||
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -45,6 +45,8 @@ import { catalogTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
import { CatalogTableColumnsFunc } from '../CatalogTable/types';
|
||||
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
import { usePermission } from '@backstage/plugin-permission-react';
|
||||
|
||||
/** @internal */
|
||||
export type BaseCatalogPageProps = {
|
||||
@@ -60,15 +62,20 @@ export function BaseCatalogPage(props: BaseCatalogPageProps) {
|
||||
useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage';
|
||||
const createComponentLink = useRouteRef(createComponentRouteRef);
|
||||
const { t } = useTranslationRef(catalogTranslationRef);
|
||||
const { allowed } = usePermission({
|
||||
permission: catalogEntityCreatePermission,
|
||||
});
|
||||
|
||||
return (
|
||||
<PageWithHeader title={t('indexPage.title', { orgName })} themeId="home">
|
||||
<Content>
|
||||
<ContentHeader title="">
|
||||
<CreateButton
|
||||
title={t('indexPage.createButtonTitle')}
|
||||
to={createComponentLink && createComponentLink()}
|
||||
/>
|
||||
{allowed && (
|
||||
<CreateButton
|
||||
title={t('indexPage.createButtonTitle')}
|
||||
to={createComponentLink && createComponentLink()}
|
||||
/>
|
||||
)}
|
||||
<SupportButton>All your software catalog entities</SupportButton>
|
||||
</ContentHeader>
|
||||
<EntityListProvider pagination={pagination}>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/theme": "workspace:^",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
|
||||
@@ -54,6 +54,8 @@ import EditIcon from '@material-ui/icons/Edit';
|
||||
import EmailIcon from '@material-ui/icons/Email';
|
||||
import GroupIcon from '@material-ui/icons/Group';
|
||||
import { LinksGroup } from '../../Meta';
|
||||
import { useEntityPermission } from '@backstage/plugin-catalog-react/alpha';
|
||||
import { catalogEntityRefreshPermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
|
||||
const CardTitle = (props: { title: string }) => (
|
||||
<Box display="flex" alignItems="center">
|
||||
@@ -70,6 +72,9 @@ export const GroupProfileCard = (props: {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const alertApi = useApi(alertApiRef);
|
||||
const { entity: group } = useEntity<GroupEntity>();
|
||||
const { allowed: canRefresh } = useEntityPermission(
|
||||
catalogEntityRefreshPermission,
|
||||
);
|
||||
|
||||
const refreshEntity = useCallback(async () => {
|
||||
await catalogApi.refreshEntity(stringifyEntityRef(group));
|
||||
@@ -127,7 +132,7 @@ export const GroupProfileCard = (props: {
|
||||
variant={props.variant}
|
||||
action={
|
||||
<>
|
||||
{allowRefresh && (
|
||||
{allowRefresh && canRefresh && (
|
||||
<IconButton
|
||||
aria-label="Refresh"
|
||||
title="Schedule entity refresh"
|
||||
|
||||
@@ -6049,6 +6049,7 @@ __metadata:
|
||||
"@backstage/integration-react": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-react": "workspace:^"
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
"@backstage/plugin-permission-react": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-common": "workspace:^"
|
||||
"@backstage/plugin-search-common": "workspace:^"
|
||||
@@ -8149,6 +8150,7 @@ __metadata:
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/dev-utils": "workspace:^"
|
||||
"@backstage/plugin-catalog": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-react": "workspace:^"
|
||||
"@backstage/plugin-permission-react": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user