link group ownership boxes through filtered catalog page

Signed-off-by: Prasetya Aria Wibawa <prasetya.wibawa@grabtaxi.com>
This commit is contained in:
Prasetya Aria Wibawa
2021-08-13 21:35:51 +07:00
parent 451536fbc1
commit e64d1c503b
2 changed files with 84 additions and 104 deletions
@@ -125,29 +125,17 @@ describe('OwnershipCard', () => {
</ApiProvider>,
);
expect(getByText('Services')).toBeInTheDocument();
expect(getByText('OPENAPI')).toBeInTheDocument();
expect(
queryByText(getByText('Services').parentElement!, '1'),
queryByText(getByText('OPENAPI').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('Documentation')).toBeInTheDocument();
expect(getByText('SERVICE')).toBeInTheDocument();
expect(
queryByText(getByText('Documentation').parentElement!, '0'),
queryByText(getByText('SERVICE').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('APIs')).toBeInTheDocument();
expect(getByText('LIBRARY')).toBeInTheDocument();
expect(
queryByText(getByText('APIs').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('Libraries')).toBeInTheDocument();
expect(
queryByText(getByText('Libraries').parentElement!, '1'),
).toBeInTheDocument();
expect(getByText('Websites')).toBeInTheDocument();
expect(
queryByText(getByText('Websites').parentElement!, '0'),
).toBeInTheDocument();
expect(getByText('Tools')).toBeInTheDocument();
expect(
queryByText(getByText('Tools').parentElement!, '0'),
queryByText(getByText('LIBRARY').parentElement!, '1'),
).toBeInTheDocument();
});
});
@@ -26,8 +26,8 @@ import {
createStyles,
Grid,
makeStyles,
Tooltip,
Typography,
Link,
} from '@material-ui/core';
import React from 'react';
import { useAsync } from 'react-use';
@@ -38,16 +38,9 @@ import {
Progress,
ResponseErrorPanel,
} from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { useApi, configApiRef } from '@backstage/core-plugin-api';
type EntitiesKinds = 'Component' | 'API';
type EntitiesTypes =
| 'service'
| 'website'
| 'library'
| 'documentation'
| 'api'
| 'tool';
type BoxTypes = 'box1' | 'box2' | 'box3' | 'box4' | 'box5' | 'box6';
const createPageTheme = (
theme: BackstageTheme,
@@ -75,67 +68,53 @@ const useStyles = makeStyles((theme: BackstageTheme) =>
bold: {
fontWeight: theme.typography.fontWeightBold,
},
service: {
box1: {
background: createPageTheme(theme, 'home', 'service'),
},
website: {
box2: {
background: createPageTheme(theme, 'home', 'website'),
},
library: {
box3: {
background: createPageTheme(theme, 'home', 'library'),
},
documentation: {
box4: {
background: createPageTheme(theme, 'home', 'documentation'),
},
api: {
box5: {
background: createPageTheme(theme, 'home', 'home'),
},
tool: {
box6: {
background: createPageTheme(theme, 'home', 'tool'),
},
}),
);
const listEntitiesBy = (
entities: Array<Entity>,
kind: EntitiesKinds,
type?: EntitiesTypes,
) =>
const listEntitiesBy = (entities: Array<Entity>, kind: string, type?: string) =>
entities.filter(
e => e.kind === kind && (type ? e?.spec?.type === type : true),
);
const countEntitiesBy = (
entities: Array<Entity>,
kind: EntitiesKinds,
type?: EntitiesTypes,
kind: string,
type?: string,
) => listEntitiesBy(entities, kind, type).length;
const EntityCountTile = ({
counter,
className,
entities,
name,
url,
}: {
counter: number;
className: EntitiesTypes;
entities: Entity[];
className: BoxTypes;
name: string;
url: string;
}) => {
let entityNames;
const classes = useStyles();
if (entities.length < 20) {
entityNames = entities.map(e => e.metadata.name).join(', ');
} else {
entityNames = `${entities
.map(e => e.metadata.name)
.slice(0, 20)
.join(', ')}, ...`;
}
return (
<Tooltip title={entityNames} arrow>
<Link href={url} target="_blank" rel="noreferrer noopenner" variant="body2">
<Box
className={`${classes.card} ${classes[className]}`}
display="flex"
@@ -149,10 +128,24 @@ const EntityCountTile = ({
{name}
</Typography>
</Box>
</Tooltip>
</Link>
);
};
const getFilteredUrl = (
owner: Entity,
type: string,
entityKind: string,
baseUrl: string,
): string => {
const ownerName = owner.metadata.name;
const filteredUrl = `
${baseUrl}/catalog/?filters[kind]=${entityKind}&filters[type][0]=${type}&filters[user]=all&filters[owners][0]=${ownerName}
`;
return filteredUrl;
};
export const OwnershipCard = ({
variant,
}: {
@@ -162,6 +155,8 @@ export const OwnershipCard = ({
}) => {
const { entity } = useEntity();
const catalogApi = useApi(catalogApiRef);
const baseUrl = useApi(configApiRef).getString('app.baseUrl');
const {
loading,
error,
@@ -185,56 +180,53 @@ export const OwnershipCard = ({
isOwnerOf(entity, component),
);
return [
{
counter: countEntitiesBy(ownedEntitiesList, 'Component', 'service'),
className: 'service',
entities: listEntitiesBy(ownedEntitiesList, 'Component', 'service'),
name: 'Services',
},
{
counter: countEntitiesBy(
ownedEntitiesList,
'Component',
'documentation',
),
className: 'documentation',
entities: listEntitiesBy(
ownedEntitiesList,
'Component',
'documentation',
),
name: 'Documentation',
},
{
counter: countEntitiesBy(ownedEntitiesList, 'API'),
className: 'api',
entities: listEntitiesBy(ownedEntitiesList, 'API'),
name: 'APIs',
},
{
counter: countEntitiesBy(ownedEntitiesList, 'Component', 'library'),
className: 'library',
entities: listEntitiesBy(ownedEntitiesList, 'Component', 'library'),
name: 'Libraries',
},
{
counter: countEntitiesBy(ownedEntitiesList, 'Component', 'website'),
className: 'website',
entities: listEntitiesBy(ownedEntitiesList, 'Component', 'website'),
name: 'Websites',
},
{
counter: countEntitiesBy(ownedEntitiesList, 'Component', 'tool'),
className: 'tool',
entities: listEntitiesBy(ownedEntitiesList, 'Component', 'tool'),
name: 'Tools',
},
] as Array<{
// Get key-value pair of Entity type and its kind
const entityKindObject = ownedEntitiesList.reduce((acc, ownedEntity) => {
if (typeof ownedEntity.spec?.type !== 'string') return acc;
const entityType = ownedEntity.spec.type.toLocaleLowerCase('en-US');
acc[entityType] = ownedEntity.kind;
return acc;
}, {} as Record<string, string>);
// Sort by entity count descending, so the most common types appear on top
const countByType = ownedEntitiesList.reduce((acc, ownedEntity) => {
if (typeof ownedEntity.spec?.type !== 'string') return acc;
const entityType = ownedEntity.spec.type.toLocaleLowerCase('en-US');
if (!acc[entityType]) {
acc[entityType] = 0;
}
acc[entityType] += 1;
return acc;
}, {} as Record<string, number>);
// Get top 6 entity types to be displayed in OwnershipCard
const topSixEntityTypes = Object.entries(countByType)
.sort(([, count1], [, count2]) => count2 - count1)
.map(([type]) => type)
.slice(0, 6);
return topSixEntityTypes.map((entityType, index) => ({
counter: countEntitiesBy(
ownedEntitiesList,
entityKindObject[entityType],
entityType,
),
className: `box${index + 1}`,
name: entityType.toLocaleUpperCase('en-US'),
url: getFilteredUrl(
entity,
entityType,
entityKindObject[entityType],
baseUrl,
),
})) as Array<{
counter: number;
className: EntitiesTypes;
className: BoxTypes;
entities: Entity[];
name: string;
url: string;
}>;
}, [catalogApi, entity]);
@@ -252,8 +244,8 @@ export const OwnershipCard = ({
<EntityCountTile
counter={c.counter}
className={c.className}
entities={c.entities}
name={c.name}
url={c.url}
/>
</Grid>
))}