Merge pull request #8456 from Oriflame/feature/ownershipcard-configurable-kind-and-type-7806
Ownershipcard filter configurable
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Added `entityFilterKind` property for `EntityOwnershipCard`
|
||||
@@ -126,6 +126,8 @@ import {
|
||||
} from '@roadiehq/backstage-plugin-travis-ci';
|
||||
import React, { ReactNode, useMemo, useState } from 'react';
|
||||
|
||||
const customEntityFilterKind = ['Component', 'API', 'System'];
|
||||
|
||||
const EntityLayoutWrapper = (props: { children?: ReactNode }) => {
|
||||
const [badgesDialogOpen, setBadgesDialogOpen] = useState(false);
|
||||
|
||||
@@ -523,7 +525,10 @@ const userPage = (
|
||||
<EntityUserProfileCard variant="gridItem" />
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<EntityOwnershipCard variant="gridItem" />
|
||||
<EntityOwnershipCard
|
||||
variant="gridItem"
|
||||
entityFilterKind={customEntityFilterKind}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</EntityLayout.Route>
|
||||
@@ -539,7 +544,10 @@ const groupPage = (
|
||||
<EntityGroupProfileCard variant="gridItem" />
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<EntityOwnershipCard variant="gridItem" />
|
||||
<EntityOwnershipCard
|
||||
variant="gridItem"
|
||||
entityFilterKind={customEntityFilterKind}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<EntityMembersListCard />
|
||||
|
||||
@@ -33,9 +33,11 @@ export const EntityMembersListCard: (_props: {
|
||||
// @public (undocumented)
|
||||
export const EntityOwnershipCard: ({
|
||||
variant,
|
||||
entityFilterKind,
|
||||
}: {
|
||||
entity?: Entity | undefined;
|
||||
variant?: InfoCardVariants | undefined;
|
||||
entityFilterKind?: string[] | undefined;
|
||||
}) => JSX.Element;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "EntityUserProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
@@ -75,9 +77,11 @@ export { orgPlugin as plugin };
|
||||
// @public (undocumented)
|
||||
export const OwnershipCard: ({
|
||||
variant,
|
||||
entityFilterKind,
|
||||
}: {
|
||||
entity?: Entity | undefined;
|
||||
variant?: InfoCardVariants | undefined;
|
||||
entityFilterKind?: string[] | undefined;
|
||||
}) => JSX.Element;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "UserProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.10.1",
|
||||
"@backstage/core-app-api": "^0.2.0",
|
||||
"@backstage/catalog-client": "^0.5.2",
|
||||
"@backstage/dev-utils": "^0.2.14",
|
||||
"@backstage/test-utils": "^0.1.24",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import {
|
||||
CatalogEntitiesRequest,
|
||||
CatalogListResponse,
|
||||
} from '@backstage/catalog-client';
|
||||
import { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import {
|
||||
CatalogApi,
|
||||
catalogApiRef,
|
||||
@@ -26,6 +30,97 @@ import { queryByText } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { OwnershipCard } from './OwnershipCard';
|
||||
|
||||
const items = [
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'API',
|
||||
metadata: {
|
||||
name: 'my-api',
|
||||
},
|
||||
spec: {
|
||||
type: 'openapi',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
name: 'my-team',
|
||||
namespace: 'default',
|
||||
kind: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'my-service',
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
name: 'my-team',
|
||||
namespace: 'default',
|
||||
kind: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'my-library',
|
||||
namespace: 'other-namespace',
|
||||
},
|
||||
spec: {
|
||||
type: 'library',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
name: 'my-team',
|
||||
namespace: 'default',
|
||||
kind: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'System',
|
||||
metadata: {
|
||||
name: 'my-system',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
name: 'my-team',
|
||||
namespace: 'default',
|
||||
kind: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
] as Entity[];
|
||||
|
||||
const getEntitiesMock = (
|
||||
request?: CatalogEntitiesRequest,
|
||||
): Promise<CatalogListResponse<Entity>> => {
|
||||
const filterKinds =
|
||||
!Array.isArray(request?.filter) && Array.isArray(request?.filter?.kind)
|
||||
? request?.filter?.kind ?? []
|
||||
: []; // we expect the request to be like { filter: { kind: ['API','System'], .... }. If changed in OwnerShipCard, let's change in also here
|
||||
return Promise.resolve({
|
||||
items: items.filter(item => filterKinds.find(k => k === item.kind)),
|
||||
} as CatalogListResponse<Entity>);
|
||||
};
|
||||
|
||||
describe('OwnershipCard', () => {
|
||||
const groupEntity: GroupEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
@@ -49,75 +144,12 @@ describe('OwnershipCard', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const items = [
|
||||
{
|
||||
kind: 'API',
|
||||
metadata: {
|
||||
name: 'my-api',
|
||||
},
|
||||
spec: {
|
||||
type: 'openapi',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
name: 'my-team',
|
||||
namespace: 'default',
|
||||
kind: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'my-service',
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
name: 'my-team',
|
||||
namespace: 'default',
|
||||
kind: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'my-library',
|
||||
namespace: 'other-namespace',
|
||||
},
|
||||
spec: {
|
||||
type: 'library',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
name: 'my-team',
|
||||
namespace: 'default',
|
||||
kind: 'Group',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
] as any;
|
||||
|
||||
it('displays entity counts', async () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getEntities: jest.fn(),
|
||||
} as any;
|
||||
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
items,
|
||||
});
|
||||
catalogApi.getEntities.mockImplementation(getEntitiesMock);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
@@ -132,6 +164,17 @@ describe('OwnershipCard', () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(catalogApi.getEntities).toHaveBeenCalledWith({
|
||||
filter: { kind: ['Component', 'API'] },
|
||||
fields: [
|
||||
'kind',
|
||||
'metadata.name',
|
||||
'metadata.namespace',
|
||||
'spec.type',
|
||||
'relations',
|
||||
],
|
||||
});
|
||||
|
||||
expect(getByText('OPENAPI')).toBeInTheDocument();
|
||||
expect(
|
||||
queryByText(getByText('OPENAPI').parentElement!, '1'),
|
||||
@@ -144,6 +187,38 @@ describe('OwnershipCard', () => {
|
||||
expect(
|
||||
queryByText(getByText('LIBRARY').parentElement!, '1'),
|
||||
).toBeInTheDocument();
|
||||
expect(() => getByText('SYSTEM')).toThrowError();
|
||||
});
|
||||
|
||||
it('applies CustomFilterDefinition', async () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getEntities: jest.fn(),
|
||||
} as any;
|
||||
|
||||
catalogApi.getEntities.mockImplementation(getEntitiesMock);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<EntityProvider entity={groupEntity}>
|
||||
<OwnershipCard entityFilterKind={['API', 'System']} />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/create': catalogRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(getByText('SYSTEM')).toBeInTheDocument();
|
||||
expect(
|
||||
queryByText(getByText('SYSTEM').parentElement!, '1'),
|
||||
).toBeInTheDocument();
|
||||
expect(getByText('OPENAPI')).toBeInTheDocument();
|
||||
expect(
|
||||
queryByText(getByText('OPENAPI').parentElement!, '1'),
|
||||
).toBeInTheDocument();
|
||||
expect(() => getByText('LIBRARY')).toThrowError();
|
||||
});
|
||||
|
||||
it('links to the catalog with the group filter', async () => {
|
||||
@@ -151,9 +226,7 @@ describe('OwnershipCard', () => {
|
||||
getEntities: jest.fn(),
|
||||
} as any;
|
||||
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
items,
|
||||
});
|
||||
catalogApi.getEntities.mockImplementation(getEntitiesMock);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
@@ -199,9 +272,7 @@ describe('OwnershipCard', () => {
|
||||
getEntities: jest.fn(),
|
||||
} as any;
|
||||
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
items,
|
||||
});
|
||||
catalogApi.getEntities.mockImplementation(getEntitiesMock);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
|
||||
@@ -128,10 +128,12 @@ const getQueryParams = (
|
||||
|
||||
export const OwnershipCard = ({
|
||||
variant,
|
||||
entityFilterKind,
|
||||
}: {
|
||||
/** @deprecated The entity is now grabbed from context instead */
|
||||
entity?: Entity;
|
||||
variant?: InfoCardVariants;
|
||||
entityFilterKind?: string[];
|
||||
}) => {
|
||||
const { entity } = useEntity();
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
@@ -142,7 +144,7 @@ export const OwnershipCard = ({
|
||||
error,
|
||||
value: componentsWithCounters,
|
||||
} = useAsync(async () => {
|
||||
const kinds = ['Component', 'API'];
|
||||
const kinds = entityFilterKind ?? ['Component', 'API'];
|
||||
const entitiesList = await catalogApi.getEntities({
|
||||
filter: {
|
||||
kind: kinds,
|
||||
@@ -162,17 +164,17 @@ export const OwnershipCard = ({
|
||||
|
||||
const counts = ownedEntitiesList.reduce(
|
||||
(acc: EntityTypeProps[], ownedEntity) => {
|
||||
if (typeof ownedEntity.spec?.type !== 'string') return acc;
|
||||
|
||||
const match = acc.find(
|
||||
x => x.kind === ownedEntity.kind && x.type === ownedEntity.spec?.type,
|
||||
x =>
|
||||
x.kind === ownedEntity.kind &&
|
||||
x.type === (ownedEntity.spec?.type ?? ownedEntity.kind),
|
||||
);
|
||||
if (match) {
|
||||
match.count += 1;
|
||||
} else {
|
||||
acc.push({
|
||||
kind: ownedEntity.kind,
|
||||
type: ownedEntity.spec?.type,
|
||||
type: ownedEntity.spec?.type?.toString() ?? ownedEntity.kind,
|
||||
count: 1,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user