Merge pull request #24518 from Zaperex/add-additional-scaffolder-permissions
Add additional scaffolder permissions
This commit is contained in:
@@ -54,6 +54,7 @@
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/plugin-permission-react": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-common": "workspace:^",
|
||||
"@backstage/theme": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
@@ -87,13 +88,15 @@
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/plugin-catalog": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/dom": "^10.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^15.0.0",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/humanize-duration": "^3.18.1",
|
||||
"@types/luxon": "^3.0.0"
|
||||
"@types/luxon": "^3.0.0",
|
||||
"swr": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
|
||||
@@ -14,7 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentType, ElementType, FormEvent, ReactNode, Ref } from 'react';
|
||||
import {
|
||||
ComponentType,
|
||||
ElementType,
|
||||
FormEvent,
|
||||
HTMLAttributes,
|
||||
ReactNode,
|
||||
Ref,
|
||||
} from 'react';
|
||||
import {
|
||||
ErrorSchema,
|
||||
FormContextType,
|
||||
@@ -32,7 +39,6 @@ import {
|
||||
Experimental_DefaultFormStateBehavior,
|
||||
ErrorTransformer,
|
||||
} from '@rjsf/utils';
|
||||
import { HTMLAttributes } from 'react';
|
||||
import Form, { IChangeEvent } from '@rjsf/core';
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
starredEntitiesApiRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
MockPermissionApi,
|
||||
MockStorageApi,
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
@@ -28,6 +29,9 @@ import React from 'react';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { permissionApiRef } from '@backstage/plugin-permission-react';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { SWRConfig } from 'swr';
|
||||
|
||||
describe('TemplateCard', () => {
|
||||
it('should render the card title', async () => {
|
||||
@@ -50,6 +54,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
@@ -79,6 +84,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
@@ -110,6 +116,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
@@ -139,6 +146,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
@@ -174,6 +182,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
@@ -213,6 +222,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
@@ -257,6 +267,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} additionalLinks={[]} />
|
||||
@@ -305,6 +316,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} additionalLinks={[]} />
|
||||
@@ -347,6 +359,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
@@ -386,6 +399,7 @@ describe('TemplateCard', () => {
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi()],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} onSelected={mockOnSelected} />
|
||||
@@ -403,4 +417,44 @@ describe('TemplateCard', () => {
|
||||
|
||||
expect(mockOnSelected).toHaveBeenCalledWith(mockTemplate);
|
||||
});
|
||||
it('should not render the choose button when user has insufficient permissions', async () => {
|
||||
const mockTemplate: TemplateEntityV1beta3 = {
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
kind: 'Template',
|
||||
metadata: { name: 'bob', tags: ['cpp', 'react'] },
|
||||
spec: {
|
||||
steps: [],
|
||||
type: 'service',
|
||||
},
|
||||
};
|
||||
const mockOnSelected = jest.fn();
|
||||
const mockAuthorize = jest
|
||||
.fn()
|
||||
.mockImplementation(async () => ({ result: AuthorizeResult.DENY }));
|
||||
// SWR used by the usePermission hook needs cache to be reset for each test
|
||||
const { queryByText } = await renderInTestApp(
|
||||
<SWRConfig value={{ provider: () => new Map() }}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
[permissionApiRef, new MockPermissionApi(mockAuthorize)],
|
||||
]}
|
||||
>
|
||||
<TemplateCard template={mockTemplate} onSelected={mockOnSelected} />
|
||||
</TestApiProvider>
|
||||
</SWRConfig>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:kind/:namespace/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(queryByText('Choose')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,6 +39,8 @@ import LanguageIcon from '@material-ui/icons/Language';
|
||||
import React, { useCallback } from 'react';
|
||||
import { CardHeader } from './CardHeader';
|
||||
import { CardLink } from './CardLink';
|
||||
import { usePermission } from '@backstage/plugin-permission-react';
|
||||
import { taskCreatePermission } from '@backstage/plugin-scaffolder-common/alpha';
|
||||
|
||||
const useStyles = makeStyles<Theme>(theme => ({
|
||||
box: {
|
||||
@@ -108,6 +110,9 @@ export const TemplateCard = (props: TemplateCardProps) => {
|
||||
!!props.additionalLinks?.length || !!template.metadata.links?.length;
|
||||
const displayDefaultDivider = !hasTags && !hasLinks;
|
||||
|
||||
const { allowed: canCreateTask } = usePermission({
|
||||
permission: taskCreatePermission,
|
||||
});
|
||||
const handleChoose = useCallback(() => {
|
||||
analytics.captureEvent('click', `Template has been opened`);
|
||||
onSelected?.(template);
|
||||
@@ -196,14 +201,16 @@ export const TemplateCard = (props: TemplateCardProps) => {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={handleChoose}
|
||||
>
|
||||
Choose
|
||||
</Button>
|
||||
{canCreateTask ? (
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={handleChoose}
|
||||
>
|
||||
Choose
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</CardActions>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user