diff --git a/.changeset/org-ownershipcard-filteradded.md b/.changeset/org-ownershipcard-filteradded.md
new file mode 100644
index 0000000000..cbd1475bb1
--- /dev/null
+++ b/.changeset/org-ownershipcard-filteradded.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-org': patch
+---
+
+Added `entityFilterKind` property for `EntityOwnershipCard`
diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index 9810f49386..a7de64e89a 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -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 = (
-
+
@@ -539,7 +544,10 @@ const groupPage = (
-
+
diff --git a/plugins/org/api-report.md b/plugins/org/api-report.md
index 88ce00fd52..60caa45134 100644
--- a/plugins/org/api-report.md
+++ b/plugins/org/api-report.md
@@ -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)
diff --git a/plugins/org/package.json b/plugins/org/package.json
index 4e7f556547..8df865f386 100644
--- a/plugins/org/package.json
+++ b/plugins/org/package.json
@@ -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",
diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx
index 8d6fc1a002..439365e583 100644
--- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx
+++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx
@@ -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> => {
+ 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);
+};
+
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 = {
getEntities: jest.fn(),
} as any;
- catalogApi.getEntities.mockResolvedValue({
- items,
- });
+ catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByText } = await renderInTestApp(
@@ -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 = {
+ getEntities: jest.fn(),
+ } as any;
+
+ catalogApi.getEntities.mockImplementation(getEntitiesMock);
+
+ const { getByText } = await renderInTestApp(
+
+
+
+
+ ,
+ {
+ 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(
@@ -199,9 +272,7 @@ describe('OwnershipCard', () => {
getEntities: jest.fn(),
} as any;
- catalogApi.getEntities.mockResolvedValue({
- items,
- });
+ catalogApi.getEntities.mockImplementation(getEntitiesMock);
const { getByText } = await renderInTestApp(
diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx
index fe23191779..1e2d514359 100644
--- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx
+++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx
@@ -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,
});
}