Derive the owned entities in the catalog from group memberships

This commit is contained in:
Dominik Henneke
2020-11-30 14:46:22 +01:00
parent 7aa0830abb
commit ebf37bbae8
4 changed files with 113 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': patch
---
Use the OWNED_BY relation and compare it to the users MEMBER_OF relation. The user entity is searched by name, based on the userId of the identity.
@@ -15,7 +15,11 @@
*/
import { CatalogApi } from '@backstage/catalog-client';
import { Entity } from '@backstage/catalog-model';
import {
Entity,
RELATION_MEMBER_OF,
RELATION_OWNED_BY,
} from '@backstage/catalog-model';
import {
ApiProvider,
ApiRegistry,
@@ -46,6 +50,12 @@ describe('CatalogPage', () => {
owner: 'tools@example.com',
type: 'service',
},
relations: [
{
type: RELATION_OWNED_BY,
target: { kind: 'Group', name: 'tools', namespace: 'default' },
},
],
},
{
apiVersion: 'backstage.io/v1alpha1',
@@ -57,11 +67,34 @@ describe('CatalogPage', () => {
owner: 'not-tools@example.com',
type: 'service',
},
relations: [
{
type: RELATION_OWNED_BY,
target: {
kind: 'Group',
name: 'not-tools',
namespace: 'default',
},
},
],
},
] as Entity[],
}),
getLocationByEntity: () =>
Promise.resolve({ id: 'id', type: 'github', target: 'url' }),
getEntityByName: async entityName => {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: { name: entityName.name },
relations: [
{
type: RELATION_MEMBER_OF,
target: { namespace: 'default', kind: 'Group', name: 'tools' },
},
],
};
},
};
const testProfile: Partial<ProfileInfo> = {
displayName: 'Display Name',
@@ -19,7 +19,6 @@ import {
Content,
ContentHeader,
errorApiRef,
identityApiRef,
SupportButton,
useApi,
} from '@backstage/core';
@@ -38,6 +37,8 @@ import { ResultsFilter } from '../ResultsFilter/ResultsFilter';
import CatalogLayout from './CatalogLayout';
import { CatalogTabs, LabeledComponentType } from './CatalogTabs';
import { WelcomeBanner } from './WelcomeBanner';
import { useUserGroups } from '../useUserGroups';
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
const useStyles = makeStyles(theme => ({
contentWrapper: {
@@ -65,7 +66,6 @@ const CatalogPageContents = () => {
const catalogApi = useApi(catalogApiRef);
const errorApi = useApi(errorApiRef);
const { isStarredEntity } = useStarredEntities();
const userId = useApi(identityApiRef).getUserId();
const [selectedTab, setSelectedTab] = useState<string>();
const [selectedSidebarItem, setSelectedSidebarItem] = useState<string>();
const orgName = configApi.getOptionalString('organization.name') ?? 'Company';
@@ -112,6 +112,8 @@ const CatalogPageContents = () => {
[],
);
const { groups } = useUserGroups();
const filterGroups = useMemo<ButtonGroup[]>(
() => [
{
@@ -121,7 +123,16 @@ const CatalogPageContents = () => {
id: 'owned',
label: 'Owned',
icon: SettingsIcon,
filterFn: entity => entity.spec?.owner === userId,
filterFn: entity => {
const ownerRelation = entity.relations?.find(
r =>
r.type === RELATION_OWNED_BY &&
r.target.kind.toLowerCase() === 'group',
);
return (
!!ownerRelation && groups.includes(ownerRelation.target.name)
);
},
},
{
id: 'starred',
@@ -142,7 +153,7 @@ const CatalogPageContents = () => {
],
},
],
[isStarredEntity, userId, orgName],
[isStarredEntity, orgName, groups],
);
const showAddExampleEntities =
@@ -0,0 +1,59 @@
/*
* 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 { useAsync } from 'react-use';
import { useMemo } from 'react';
import { RELATION_MEMBER_OF } from '@backstage/catalog-model';
import { identityApiRef, useApi } from '@backstage/core';
import { catalogApiRef } from '../plugin';
/**
* Get the group memberships of the logged-in user.
*/
export const useUserGroups: () => {
groups: string[];
loading: boolean;
error?: Error;
} = () => {
const catalogApi = useApi(catalogApiRef);
const userId = useApi(identityApiRef).getUserId();
// TODO: should the identityApiRef already include the entity? or at least a full EntityName?
const { value: user, loading, error } = useAsync(async () => {
return await catalogApi.getEntityByName({
kind: 'User',
namespace: 'default',
name: userId,
});
}, [catalogApi, userId]);
// calculate the group memberships
const groups = useMemo<string[]>(() => {
if (user && user.relations) {
return user.relations
.filter(
r =>
r.type === RELATION_MEMBER_OF &&
r.target.kind.toLowerCase() === 'group',
)
.map(r => r.target.name);
}
return [];
}, [user]);
return { groups, loading, error };
};