diff --git a/.changeset/little-games-fail.md b/.changeset/little-games-fail.md
new file mode 100644
index 0000000000..199f983be8
--- /dev/null
+++ b/.changeset/little-games-fail.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-org': patch
+---
+
+Added `relationType` property to EntityMembersListCard component that allows for display users related to a group via some other relationship aside from `memberOf`.
+
+Also, as a side effect, the `relationsType` property has been deprecated in favor of a more accurately named `relationAggregation` property.
diff --git a/plugins/org/api-report.md b/plugins/org/api-report.md
index a6926d71f2..e958e8e29a 100644
--- a/plugins/org/api-report.md
+++ b/plugins/org/api-report.md
@@ -23,7 +23,9 @@ export const EntityMembersListCard: (props: {
memberDisplayTitle?: string | undefined;
pageSize?: number | undefined;
showAggregateMembersToggle?: boolean | undefined;
+ relationType?: string | undefined;
relationsType?: EntityRelationAggregation | undefined;
+ relationAggregation?: EntityRelationAggregation | undefined;
}) => JSX_2.Element;
// @public (undocumented)
@@ -32,6 +34,7 @@ export const EntityOwnershipCard: (props: {
entityFilterKind?: string[] | undefined;
hideRelationsToggle?: boolean | undefined;
relationsType?: EntityRelationAggregation | undefined;
+ relationAggregation?: EntityRelationAggregation | undefined;
entityLimit?: number | undefined;
}) => JSX_2.Element;
@@ -55,7 +58,9 @@ export const MembersListCard: (props: {
memberDisplayTitle?: string;
pageSize?: number;
showAggregateMembersToggle?: boolean;
+ relationType?: string;
relationsType?: EntityRelationAggregation;
+ relationAggregation?: EntityRelationAggregation;
}) => React_2.JSX.Element;
// @public
@@ -82,6 +87,7 @@ export const OwnershipCard: (props: {
entityFilterKind?: string[];
hideRelationsToggle?: boolean;
relationsType?: EntityRelationAggregation;
+ relationAggregation?: EntityRelationAggregation;
entityLimit?: number;
}) => React_2.JSX.Element;
diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx
index 793ffe274b..69d6090687 100644
--- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx
+++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx
@@ -99,6 +99,7 @@ describe('MemberTab Test', () => {
] as Entity[],
}),
};
+ const getEntitiesSpy = jest.spyOn(catalogApi, 'getEntities');
it('Display Profile Card', async () => {
await renderInTestApp(
@@ -115,6 +116,12 @@ describe('MemberTab Test', () => {
},
},
);
+ expect(getEntitiesSpy).toHaveBeenCalledWith({
+ filter: {
+ kind: 'User',
+ 'relations.memberof': ['group:default/team-d'],
+ },
+ });
expect(screen.getByAltText('Tara MacGovern')).toHaveAttribute(
'src',
@@ -149,6 +156,29 @@ describe('MemberTab Test', () => {
expect(screen.getByText('Testers (1)')).toBeInTheDocument();
});
+ it('Can query a different relationship', async () => {
+ await renderInTestApp(
+
+
+
+
+ ,
+ {
+ mountedRoutes: {
+ '/catalog/:namespace/:kind/:name': entityRouteRef,
+ '/catalog': rootRouteRef,
+ },
+ },
+ );
+
+ expect(getEntitiesSpy).toHaveBeenCalledWith({
+ filter: {
+ kind: 'User',
+ 'relations.leaderof': ['group:default/team-d'],
+ },
+ });
+ });
+
describe('Aggregate members toggle', () => {
it('Does not show the aggregate members toggle if the showAggregateMembersToggle prop is undefined', async () => {
await renderInTestApp(
@@ -346,7 +376,7 @@ describe('MemberTab Test', () => {
@@ -384,7 +414,7 @@ describe('MemberTab Test', () => {
-
+
diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
index 418a83f5b2..f27b071114 100644
--- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
+++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
@@ -138,14 +138,19 @@ export const MembersListCard = (props: {
memberDisplayTitle?: string;
pageSize?: number;
showAggregateMembersToggle?: boolean;
+ relationType?: string;
+ /** @deprecated Please use `relationAggregation` instead */
relationsType?: EntityRelationAggregation;
+ relationAggregation?: EntityRelationAggregation;
}) => {
const {
memberDisplayTitle = 'Members',
pageSize = 50,
showAggregateMembersToggle,
- relationsType = 'direct',
+ relationType = 'memberof',
} = props;
+ const relationAggregation =
+ props.relationAggregation ?? props.relationsType ?? 'direct';
const classes = useListStyles();
const { entity: groupEntity } = useEntity();
@@ -165,7 +170,7 @@ export const MembersListCard = (props: {
};
const [showAggregateMembers, setShowAggregateMembers] = useState(
- relationsType === 'aggregated',
+ relationAggregation === 'aggregated',
);
const { loading: loadingDescendantMembers, value: descendantMembers } =
@@ -177,6 +182,7 @@ export const MembersListCard = (props: {
return await getAllDesendantMembersForGroupEntity(
groupEntity,
catalogApi,
+ relationType,
);
}, [catalogApi, groupEntity, showAggregateMembers]);
const {
@@ -187,7 +193,7 @@ export const MembersListCard = (props: {
const membersList = await catalogApi.getEntities({
filter: {
kind: 'User',
- 'relations.memberof': [
+ [`relations.${relationType.toLocaleLowerCase('en-US')}`]: [
stringifyEntityRef({
kind: 'group',
namespace: groupNamespace.toLocaleLowerCase('en-US'),
diff --git a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx
index 69ad5bf8bb..a7decfb592 100644
--- a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx
+++ b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx
@@ -114,19 +114,27 @@ export const ComponentsGrid = ({
className,
entity,
relationsType,
+ relationAggregation,
entityFilterKind,
entityLimit = 6,
}: {
className?: string;
entity: Entity;
- relationsType: EntityRelationAggregation;
+ /** @deprecated Please use relationAggregation instead */
+ relationsType?: EntityRelationAggregation;
+ relationAggregation?: EntityRelationAggregation;
entityFilterKind?: string[];
entityLimit?: number;
}) => {
const catalogLink = useRouteRef(catalogIndexRouteRef);
+ if (!relationsType && !relationAggregation) {
+ throw new Error(
+ 'The relationAggregation property must be set as an EntityRelationAggregation type.',
+ );
+ }
const { componentsWithCounters, loading, error } = useGetEntities(
entity,
- relationsType,
+ (relationAggregation ?? relationsType)!, // we can safely use the non-null assertion here because of the run-time check above
entityFilterKind,
entityLimit,
);
diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx
index f2b949f768..004fb8b964 100644
--- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx
+++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx
@@ -288,7 +288,7 @@ describe('OwnershipCard', () => {
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 a5b0b7bb19..fce47845bc 100644
--- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx
+++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx
@@ -67,31 +67,34 @@ export const OwnershipCard = (props: {
variant?: InfoCardVariants;
entityFilterKind?: string[];
hideRelationsToggle?: boolean;
+ /** @deprecated Please use relationAggregation instead */
relationsType?: EntityRelationAggregation;
+ relationAggregation?: EntityRelationAggregation;
entityLimit?: number;
}) => {
const {
variant,
entityFilterKind,
hideRelationsToggle,
- relationsType,
entityLimit = 6,
} = props;
+ const relationAggregation = props.relationAggregation ?? props.relationsType;
const relationsToggle =
hideRelationsToggle === undefined ? false : hideRelationsToggle;
const classes = useStyles();
const { entity } = useEntity();
- const defaultRelationsType = entity.kind === 'User' ? 'aggregated' : 'direct';
- const [getRelationsType, setRelationsType] = useState(
- relationsType ?? defaultRelationsType,
+ const defaultRelationAggregation =
+ entity.kind === 'User' ? 'aggregated' : 'direct';
+ const [getRelationAggregation, setRelationAggregation] = useState(
+ relationAggregation ?? defaultRelationAggregation,
);
useEffect(() => {
- if (!relationsType) {
- setRelationsType(defaultRelationsType);
+ if (!relationAggregation) {
+ setRelationAggregation(defaultRelationAggregation);
}
- }, [setRelationsType, defaultRelationsType, relationsType]);
+ }, [setRelationAggregation, defaultRelationAggregation, relationAggregation]);
return (
{
- const updatedRelationsType =
- getRelationsType === 'direct' ? 'aggregated' : 'direct';
- setRelationsType(updatedRelationsType);
+ const updatedRelationAggregation =
+ getRelationAggregation === 'direct'
+ ? 'aggregated'
+ : 'direct';
+ setRelationAggregation(updatedRelationAggregation);
}}
name="pin"
inputProps={{ 'aria-label': 'Ownership Type Switch' }}
@@ -136,7 +141,7 @@ export const OwnershipCard = (props: {
className={classes.grid}
entity={entity}
entityLimit={entityLimit}
- relationsType={getRelationsType}
+ relationAggregation={getRelationAggregation}
entityFilterKind={entityFilterKind}
/>
diff --git a/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.test.ts b/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.test.ts
index eac5a171ba..c6cec557c1 100644
--- a/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.test.ts
+++ b/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.test.ts
@@ -64,7 +64,7 @@ describe('useGetEntities', () => {
]),
});
- describe('given aggregated relationsType', () => {
+ describe('given aggregated relationAggregation', () => {
const whenHookIsCalledWith = async (_entity: Entity) => {
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'aggregated'),
@@ -205,7 +205,7 @@ describe('useGetEntities', () => {
});
});
- describe('given direct relationsType', () => {
+ describe('given direct relationAggregation', () => {
const whenHookIsCalledWith = async (_entity: Entity) => {
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'direct'),
diff --git a/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.ts b/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.ts
index 5a708bcfee..d806bbfef2 100644
--- a/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.ts
+++ b/plugins/org/src/components/Cards/OwnershipCard/useGetEntities.ts
@@ -125,11 +125,11 @@ const getChildOwnershipEntityRefs = async (
const getOwners = async (
entity: Entity,
- relations: EntityRelationAggregation,
+ relationAggregation: EntityRelationAggregation,
catalogApi: CatalogApi,
): Promise => {
const isGroup = entity.kind === 'Group';
- const isAggregated = relations === 'aggregated';
+ const isAggregated = relationAggregation === 'aggregated';
const isUserEntity = entity.kind === 'User';
if (isAggregated && isGroup) {
@@ -166,7 +166,7 @@ const getOwnedEntitiesByOwners = (
export function useGetEntities(
entity: Entity,
- relations: EntityRelationAggregation,
+ relationAggregation: EntityRelationAggregation,
entityFilterKind?: string[],
entityLimit = 6,
): {
@@ -189,7 +189,7 @@ export function useGetEntities(
error,
value: componentsWithCounters,
} = useAsync(async () => {
- const owners = await getOwners(entity, relations, catalogApi);
+ const owners = await getOwners(entity, relationAggregation, catalogApi);
const ownedEntitiesList = await getOwnedEntitiesByOwners(
owners,
@@ -230,7 +230,7 @@ export function useGetEntities(
kind: string;
queryParams: string;
}>;
- }, [catalogApi, entity, relations]);
+ }, [catalogApi, entity, relationAggregation]);
return {
componentsWithCounters,
diff --git a/plugins/org/src/helpers/helpers.ts b/plugins/org/src/helpers/helpers.ts
index 7da4ee32d8..465f89afa1 100644
--- a/plugins/org/src/helpers/helpers.ts
+++ b/plugins/org/src/helpers/helpers.ts
@@ -31,6 +31,7 @@ import {
export const getMembersFromGroups = async (
groups: CompoundEntityRef[],
catalogApi: CatalogApi,
+ relationship = 'memberof',
) => {
const membersList =
groups.length === 0
@@ -38,13 +39,14 @@ export const getMembersFromGroups = async (
: await catalogApi.getEntities({
filter: {
kind: 'User',
- 'relations.memberof': groups.map(group =>
- stringifyEntityRef({
- kind: 'group',
- namespace: group.namespace.toLocaleLowerCase('en-US'),
- name: group.name.toLocaleLowerCase('en-US'),
- }),
- ),
+ [`relations.${relationship.toLocaleLowerCase('en-US')}`]:
+ groups.map(group =>
+ stringifyEntityRef({
+ kind: 'group',
+ namespace: group.namespace.toLocaleLowerCase('en-US'),
+ name: group.name.toLocaleLowerCase('en-US'),
+ }),
+ ),
},
});
@@ -99,10 +101,12 @@ export const getDescendantGroupsFromGroup = async (
export const getAllDesendantMembersForGroupEntity = async (
groupEntity: GroupEntity,
catalogApi: CatalogApi,
+ relationship = 'memberof',
) =>
getMembersFromGroups(
await getDescendantGroupsFromGroup(groupEntity, catalogApi),
catalogApi,
+ relationship,
);
export const removeDuplicateEntitiesFrom = (entityArray: Entity[]) => {