From 2b71db211668479ae337446d838aac0f59ad7759 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Tue, 8 Dec 2020 18:55:17 +0100 Subject: [PATCH] Consider group memberships in the OwnershipCard --- .changeset/witty-rabbits-smell.md | 5 ++ .../Cards/OwnershipCard/OwnershipCard.tsx | 13 ++--- .../org/src/components/getEntityRelations.ts | 42 ++++++++++++++ plugins/org/src/components/isOwnerOf.ts | 55 +++++++++++++++++++ 4 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 .changeset/witty-rabbits-smell.md create mode 100644 plugins/org/src/components/getEntityRelations.ts create mode 100644 plugins/org/src/components/isOwnerOf.ts diff --git a/.changeset/witty-rabbits-smell.md b/.changeset/witty-rabbits-smell.md new file mode 100644 index 0000000000..fbe2234b45 --- /dev/null +++ b/.changeset/witty-rabbits-smell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-org': patch +--- + +Support transitive ownerships of users and groups. diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx index 7c4ad7a56a..16a833cf56 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { InfoCard, useApi, Progress } from '@backstage/core'; -import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { Entity } from '@backstage/catalog-model'; import { catalogApiRef } from '@backstage/plugin-catalog'; import { useAsync } from 'react-use'; import Alert from '@material-ui/lab/Alert'; @@ -28,6 +29,7 @@ import { Typography, } from '@material-ui/core'; import { pageTheme } from '@backstage/theme'; +import { isOwnerOf } from '../../isOwnerOf'; type EntitiesKinds = 'Component' | 'API'; type EntitiesTypes = @@ -118,9 +120,6 @@ export const OwnershipCard = ({ entity: Entity; variant: string; }) => { - const { - metadata: { name: groupName }, - } = entity; const catalogApi = useApi(catalogApiRef); const { loading, @@ -129,10 +128,8 @@ export const OwnershipCard = ({ } = useAsync(async () => { const entitiesList = await catalogApi.getEntities(); const ownedEntitiesList = entitiesList.items.filter(component => - component?.relations?.some( - r => r.type === RELATION_OWNED_BY && r.target.name === groupName, - ), - ) as Array; + isOwnerOf(entity, component), + ); return [ { diff --git a/plugins/org/src/components/getEntityRelations.ts b/plugins/org/src/components/getEntityRelations.ts new file mode 100644 index 0000000000..9230aaebee --- /dev/null +++ b/plugins/org/src/components/getEntityRelations.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity, EntityName } from '@backstage/catalog-model'; + +// TODO: this file is copied from /packages/app/catalog/src/components/getEntityRelations.ts and +// should be replaced once common relation-functions are introduced. + +/** + * Get the related entity references. + */ +export function getEntityRelations( + entity: Entity | undefined, + relationType: string, + filter?: { kind: string }, +): EntityName[] { + let entityNames = + entity?.relations + ?.filter(r => r.type === relationType) + ?.map(r => r.target) || []; + + if (filter?.kind) { + entityNames = entityNames?.filter( + e => e.kind.toLowerCase() === filter.kind.toLowerCase(), + ); + } + + return entityNames; +} diff --git a/plugins/org/src/components/isOwnerOf.ts b/plugins/org/src/components/isOwnerOf.ts new file mode 100644 index 0000000000..dd7c7d6805 --- /dev/null +++ b/plugins/org/src/components/isOwnerOf.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Entity, + EntityName, + getEntityName, + RELATION_MEMBER_OF, + RELATION_OWNED_BY, +} from '@backstage/catalog-model'; +import { getEntityRelations } from './getEntityRelations'; + +// TODO: this file is copied from /packages/app/catalog/src/components/isOwnerOf.ts and +// should be replaced once common relation-functions are introduced. + +/** + * Check if one entity is owned by another. Returns true, if the entity is owned by the + * owner directly, or if the entity is owned by a group that the owner is a member of. + */ +export function isOwnerOf(owner: Entity, owned: Entity) { + const possibleOwners: EntityName[] = [ + ...getEntityRelations(owner, RELATION_MEMBER_OF, { kind: 'group' }), + ...(owner ? [getEntityName(owner)] : []), + ]; + + const owners = getEntityRelations(owned, RELATION_OWNED_BY); + + for (const owner of owners) { + if ( + possibleOwners.find( + o => + owner.kind.toLowerCase() === o.kind.toLowerCase() && + owner.namespace.toLowerCase() === o.namespace.toLowerCase() && + owner.name.toLowerCase() === o.name.toLowerCase(), + ) !== undefined + ) { + return true; + } + } + + return false; +}