Merge pull request #4090 from SDA-SE/feat/display-systems-in-catalog

Display the owner and system as links to the entity pages in the about card
This commit is contained in:
Oliver Sand
2021-01-18 11:54:56 +01:00
committed by GitHub
5 changed files with 293 additions and 31 deletions
@@ -14,15 +14,16 @@
* limitations under the License.
*/
import React from 'react';
import { Grid, Typography, Chip, makeStyles } from '@material-ui/core';
import { AboutField } from './AboutField';
import {
Entity,
ENTITY_DEFAULT_NAMESPACE,
RELATION_OWNED_BY,
serializeEntityRef,
RELATION_PART_OF,
} from '@backstage/catalog-model';
import { Chip, Grid, makeStyles, Typography } from '@material-ui/core';
import React from 'react';
import { EntityRefLink } from '../EntityRefLink';
import { getEntityRelations } from '../getEntityRelations';
import { AboutField } from './AboutField';
const useStyles = makeStyles({
description: {
@@ -36,6 +37,17 @@ type Props = {
export const AboutContent = ({ entity }: Props) => {
const classes = useStyles();
const isSystem = entity.kind.toLowerCase() === 'system';
const isDomain = entity.kind.toLowerCase() === 'domain';
const isResource = entity.kind.toLowerCase() === 'resource';
const [partOfSystemRelation] = getEntityRelations(entity, RELATION_PART_OF, {
kind: 'system',
});
const [partOfDomainRelation] = getEntityRelations(entity, RELATION_PART_OF, {
kind: 'domain',
});
const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
return (
<Grid container>
<AboutField label="Description" gridSizes={{ xs: 12 }}>
@@ -43,32 +55,56 @@ export const AboutContent = ({ entity }: Props) => {
{entity?.metadata?.description || 'No description'}
</Typography>
</AboutField>
<AboutField
label="Owner"
value={entity?.relations
?.filter(r => r.type === RELATION_OWNED_BY)
.map(({ target: { kind, name, namespace } }) =>
// TODO(Rugvip): we want to provide some utils for this
serializeEntityRef({
kind,
name,
namespace:
namespace === ENTITY_DEFAULT_NAMESPACE ? undefined : namespace,
}),
)
.join(', ')}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
<AboutField
label="Type"
value={entity?.spec?.type as string}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
<AboutField
label="Lifecycle"
value={entity?.spec?.lifecycle as string}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
<AboutField label="Owner" gridSizes={{ xs: 12, sm: 6, lg: 4 }}>
{ownedByRelations.map((t, i) => (
<React.Fragment key={i}>
{i > 0 && ', '}
<EntityRefLink entityRef={t} />
</React.Fragment>
))}
</AboutField>
{isSystem && (
<AboutField
label="Domain"
value="No Domain"
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
{partOfDomainRelation && (
<EntityRefLink
entityRef={partOfDomainRelation}
defaultKind="domain"
/>
)}
</AboutField>
)}
{!isSystem && !isDomain && (
<AboutField
label="System"
value="No System"
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
{partOfSystemRelation && (
<EntityRefLink
entityRef={partOfSystemRelation}
defaultKind="system"
/>
)}
</AboutField>
)}
{!isSystem && !isDomain && (
<AboutField
label="Type"
value={entity?.spec?.type as string}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
)}
{!isSystem && !isDomain && !isResource && (
<AboutField
label="Lifecycle"
value={entity?.spec?.lifecycle as string}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
)}
<AboutField
label="Tags"
value="No Tags"
@@ -0,0 +1,124 @@
/*
* 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 { render } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router';
import { EntityRefLink } from './EntityRefLink';
describe('<EntityRefLink />', () => {
it('renders link for entity in default namespace', () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
metadata: {
name: 'software',
},
spec: {
owner: 'guest',
type: 'service',
lifecycle: 'production',
},
};
const { getByText } = render(<EntityRefLink entityRef={entity} />, {
wrapper: MemoryRouter,
});
expect(getByText('component:software')).toBeInTheDocument();
});
it('renders link for entity in other namespace', () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
metadata: {
name: 'software',
namespace: 'test',
},
spec: {
owner: 'guest',
type: 'service',
lifecycle: 'production',
},
};
const { getByText } = render(<EntityRefLink entityRef={entity} />, {
wrapper: MemoryRouter,
});
expect(getByText('component:test/software')).toBeInTheDocument();
});
it('renders link for entity and hides default kind', () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
metadata: {
name: 'software',
namespace: 'test',
},
spec: {
owner: 'guest',
type: 'service',
lifecycle: 'production',
},
};
const { getByText } = render(
<EntityRefLink entityRef={entity} defaultKind="Component" />,
{
wrapper: MemoryRouter,
},
);
expect(getByText('test/software')).toBeInTheDocument();
});
it('renders link for entity name in default namespace', () => {
const entityName = {
kind: 'Component',
namespace: 'default',
name: 'software',
};
const { getByText } = render(<EntityRefLink entityRef={entityName} />, {
wrapper: MemoryRouter,
});
expect(getByText('component:software')).toBeInTheDocument();
});
it('renders link for entity name in other namespace', () => {
const entityName = {
kind: 'Component',
namespace: 'test',
name: 'software',
};
const { getByText } = render(<EntityRefLink entityRef={entityName} />, {
wrapper: MemoryRouter,
});
expect(getByText('component:test/software')).toBeInTheDocument();
});
it('renders link for entity name and hides default kind', () => {
const entityName = {
kind: 'Component',
namespace: 'test',
name: 'software',
};
const { getByText } = render(
<EntityRefLink entityRef={entityName} defaultKind="component" />,
{
wrapper: MemoryRouter,
},
);
expect(getByText('test/software')).toBeInTheDocument();
});
});
@@ -0,0 +1,80 @@
/*
* 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,
ENTITY_DEFAULT_NAMESPACE,
serializeEntityRef,
} from '@backstage/catalog-model';
import { Link } from '@material-ui/core';
import React from 'react';
import { generatePath } from 'react-router';
import { Link as RouterLink } from 'react-router-dom';
import { entityRoute } from '../../routes';
type EntityRefLinkProps = {
entityRef: Entity | EntityName;
defaultKind?: string;
};
// TODO: This component is private for now, as it should probably belong into
// some kind of helper module for the catalog plugin to avoid a dependency on
// the catalog plugin itself.
export const EntityRefLink = ({
entityRef,
defaultKind,
}: EntityRefLinkProps) => {
let kind;
let namespace;
let name;
if ('metadata' in entityRef) {
kind = entityRef.kind;
namespace = entityRef.metadata.namespace;
name = entityRef.metadata.name;
} else {
kind = entityRef.kind;
namespace = entityRef.namespace;
name = entityRef.name;
}
if (namespace === ENTITY_DEFAULT_NAMESPACE) {
namespace = undefined;
}
kind = kind.toLowerCase();
const title = `${serializeEntityRef({
kind: defaultKind && defaultKind.toLowerCase() === kind ? undefined : kind,
name,
namespace,
})}`;
const routeParams = {
kind,
namespace: namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE,
name,
};
// TODO: Use useRouteRef here to generate the path
return (
<Link
component={RouterLink}
to={generatePath(`/catalog/${entityRoute.path}`, routeParams)}
>
{title}
</Link>
);
};
@@ -0,0 +1,16 @@
/*
* 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.
*/
export { EntityRefLink } from './EntityRefLink';