tweak the org plugin to better use builtin facilities

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2021-03-26 10:14:02 +01:00
parent 22c5b750ad
commit 9f48b548c0
8 changed files with 73 additions and 118 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-org': patch
---
Some cleanup in how types and components are used; leverage `EntityRefLinks`
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-api-docs': patch
'@backstage/plugin-catalog-react': patch
---
Make it possible to specify entity type to `useEntity` when it's known
@@ -15,8 +15,8 @@
*/
import { ApiEntity } from '@backstage/catalog-model';
import { useEntity } from '@backstage/plugin-catalog-react';
import { CardTab, TabbedCard, useApi } from '@backstage/core';
import { useEntity } from '@backstage/plugin-catalog-react';
import { Alert } from '@material-ui/lab';
import React from 'react';
import { apiDocsConfigRef } from '../../config';
@@ -28,7 +28,7 @@ type Props = {
};
export const ApiDefinitionCard = (_: Props) => {
const entity = useEntity().entity as ApiEntity;
const entity = useEntity<ApiEntity>().entity;
const config = useApi(apiDocsConfigRef);
const { getApiDefinitionWidget } = config;
+6 -10
View File
@@ -18,8 +18,8 @@ import { errorApiRef, useApi } from '@backstage/core';
import { createContext, useContext, useEffect } from 'react';
import { useNavigate } from 'react-router';
import { useAsync } from 'react-use';
import { useEntityCompoundName } from './useEntityCompoundName';
import { catalogApiRef } from '../api';
import { useEntityCompoundName } from './useEntityCompoundName';
type EntityLoadingStatus = {
entity?: Entity;
@@ -55,13 +55,9 @@ export const useEntityFromUrl = (): EntityLoadingStatus => {
};
/**
* Grab Entity from the context and its current loading state.
* Grab the current entity from the context and its current loading state.
*/
export const useEntity = () => {
const { entity, loading, error } = useContext<{
entity: Entity;
loading: boolean;
error: Error;
}>(EntityContext as any);
return { entity, loading, error };
};
export function useEntity<T extends Entity = Entity>() {
const { entity, loading, error } = useContext(EntityContext);
return { entity: entity as T, loading, error };
}
@@ -15,14 +15,13 @@
*/
import {
Entity,
GroupEntity,
RELATION_CHILD_OF,
RELATION_PARENT_OF,
} from '@backstage/catalog-model';
import { Avatar, InfoCard, InfoCardVariants } from '@backstage/core';
import {
entityRouteParams,
EntityRefLinks,
getEntityRelations,
useEntity,
} from '@backstage/plugin-catalog-react';
@@ -41,34 +40,7 @@ import EmailIcon from '@material-ui/icons/Email';
import GroupIcon from '@material-ui/icons/Group';
import Alert from '@material-ui/lab/Alert';
import React from 'react';
import { generatePath, Link as RouterLink } from 'react-router-dom';
const GroupLink = ({
groupName,
index = 0,
}: {
groupName: string;
index?: number;
/** @deprecated The entity is now grabbed from context instead */
entity?: Entity;
}) => {
const { entity } = useEntity();
return (
<>
{index >= 1 ? ', ' : ''}
<Link
component={RouterLink}
to={generatePath(
`/catalog/:namespace/group/${groupName}`,
entityRouteParams(entity),
)}
>
[{groupName}]
</Link>
</>
);
};
const CardTitle = ({ title }: { title: string }) => (
<Box display="flex" alignItems="center">
<GroupIcon fontSize="inherit" />
@@ -83,23 +55,25 @@ export const GroupProfileCard = ({
entity?: GroupEntity;
variant?: InfoCardVariants;
}) => {
const group = useEntity().entity as GroupEntity;
const group = useEntity<GroupEntity>().entity;
if (!group) {
return <Alert severity="error">User not found</Alert>;
}
const {
metadata: { name, description },
spec: { profile },
} = group;
const parent = group?.relations
?.filter(r => r.type === RELATION_CHILD_OF)
?.map(groupItem => groupItem.target.name)
.toString();
const childRelations = getEntityRelations(group, RELATION_PARENT_OF, {
kind: 'Group',
});
const parentRelations = getEntityRelations(group, RELATION_CHILD_OF, {
kind: 'group',
});
const displayName = profile?.displayName ?? name;
if (!group) return <Alert severity="error">User not found</Alert>;
const emailHref = profile?.email ? `mailto:${profile.email}` : undefined;
return (
<InfoCard
@@ -120,10 +94,13 @@ export const GroupProfileCard = ({
<EmailIcon />
</Tooltip>
</ListItemIcon>
<ListItemText>{profile.email}</ListItemText>
<ListItemText>
<Link href={emailHref}>{profile.email}</Link>
</ListItemText>
</ListItem>
)}
{parent ? (
{parentRelations.length ? (
<ListItem>
<ListItemIcon>
<Tooltip title="Parent Group">
@@ -131,11 +108,15 @@ export const GroupProfileCard = ({
</Tooltip>
</ListItemIcon>
<ListItemText>
<GroupLink groupName={parent} entity={group} />
<EntityRefLinks
entityRefs={parentRelations}
defaultKind="Group"
/>
</ListItemText>
</ListItem>
) : null}
{childRelations?.length ? (
{childRelations.length ? (
<ListItem>
<ListItemIcon>
<Tooltip title="Child Groups">
@@ -143,13 +124,10 @@ export const GroupProfileCard = ({
</Tooltip>
</ListItemIcon>
<ListItemText>
{childRelations.map((children, index) => (
<GroupLink
groupName={children.name}
entity={group}
index={index}
/>
))}
<EntityRefLinks
entityRefs={childRelations}
defaultKind="Group"
/>
</ListItemText>
</ListItem>
) : null}
@@ -21,9 +21,9 @@ import {
} from '@backstage/catalog-model';
import { Avatar, InfoCard, Progress, useApi } from '@backstage/core';
import {
useEntity,
catalogApiRef,
entityRouteParams,
useEntity,
} from '@backstage/plugin-catalog-react';
import {
Box,
@@ -110,7 +110,7 @@ export const MembersListCard = (_props: {
/** @deprecated The entity is now grabbed from context instead */
entity?: GroupEntity;
}) => {
const groupEntity = useEntity().entity as GroupEntity;
const groupEntity = useEntity<GroupEntity>().entity;
const {
metadata: { name: groupName },
spec: { profile },
@@ -121,11 +121,9 @@ export const MembersListCard = (_props: {
const { loading, error, value: members } = useAsync(async () => {
const membersList = await catalogApi.getEntities({
filter: {
kind: 'User',
},
filter: { kind: 'User' },
});
const groupMembersList = ((membersList.items as unknown) as Array<UserEntity>).filter(
const groupMembersList = (membersList.items as UserEntity[]).filter(
member =>
member?.relations?.some(
r =>
@@ -150,7 +148,7 @@ export const MembersListCard = (_props: {
subheader={`of ${displayName}`}
>
<Grid container spacing={3}>
{members && members.length ? (
{members && members.length > 0 ? (
members.map(member => (
<MemberComponent
member={member}
@@ -61,7 +61,7 @@ describe('UserSummary Test', () => {
'src',
'https://example.com/staff/calum.jpeg',
);
expect(rendered.getByText('[ExampleGroup]')).toHaveAttribute(
expect(rendered.getByText('ExampleGroup')).toHaveAttribute(
'href',
'/catalog/default/group/ExampleGroup',
);
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Entity,
RELATION_MEMBER_OF,
UserEntity,
} from '@backstage/catalog-model';
import { RELATION_MEMBER_OF, UserEntity } from '@backstage/catalog-model';
import { Avatar, InfoCard, InfoCardVariants } from '@backstage/core';
import { entityRouteParams, useEntity } from '@backstage/plugin-catalog-react';
import {
EntityRefLinks,
getEntityRelations,
useEntity,
} from '@backstage/plugin-catalog-react';
import {
Box,
Grid,
@@ -35,30 +35,6 @@ import GroupIcon from '@material-ui/icons/Group';
import PersonIcon from '@material-ui/icons/Person';
import Alert from '@material-ui/lab/Alert';
import React from 'react';
import { generatePath, Link as RouterLink } from 'react-router-dom';
const GroupLink = ({
groupName,
index,
entity,
}: {
groupName: string;
index: number;
entity: Entity;
}) => (
<>
{index >= 1 ? ', ' : ''}
<Link
component={RouterLink}
to={generatePath(
`/catalog/:namespace/group/${groupName}`,
entityRouteParams(entity),
)}
>
[{groupName}]
</Link>
</>
);
const CardTitle = ({ title }: { title?: string }) =>
title ? (
@@ -75,22 +51,20 @@ export const UserProfileCard = ({
entity?: UserEntity;
variant?: InfoCardVariants;
}) => {
const user = useEntity().entity as UserEntity;
const {
metadata: { name: metaName },
spec: { profile },
} = user;
const groupNames =
user?.relations
?.filter(r => r.type === RELATION_MEMBER_OF)
?.map(group => group.target.name) || [];
const displayName = profile?.displayName ?? metaName;
const user = useEntity<UserEntity>().entity;
if (!user) {
return <Alert severity="error">User not found</Alert>;
}
const emailHref = profile?.email ? `mailto:${profile.email}` : '';
const {
metadata: { name: metaName },
spec: { profile },
} = user;
const displayName = profile?.displayName ?? metaName;
const emailHref = profile?.email ? `mailto:${profile.email}` : undefined;
const memberOfRelations = getEntityRelations(user, RELATION_MEMBER_OF, {
kind: 'Group',
});
return (
<InfoCard title={<CardTitle title={displayName} />} variant={variant}>
@@ -104,7 +78,9 @@ export const UserProfileCard = ({
{profile?.email && (
<ListItem>
<ListItemIcon>
<EmailIcon />
<Tooltip title="Email">
<EmailIcon />
</Tooltip>
</ListItemIcon>
<ListItemText>
<Link href={emailHref}>{profile.email}</Link>
@@ -119,14 +95,10 @@ export const UserProfileCard = ({
</Tooltip>
</ListItemIcon>
<ListItemText>
{groupNames.map((groupName, index) => (
<GroupLink
groupName={groupName}
index={index}
key={groupName}
entity={user}
/>
))}
<EntityRefLinks
entityRefs={memberOfRelations}
defaultKind="Group"
/>
</ListItemText>
</ListItem>
</List>