tweak the org plugin to better use builtin facilities
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Some cleanup in how types and components are used; leverage `EntityRefLinks`
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user