diff --git a/.changeset/healthy-comics-drive.md b/.changeset/healthy-comics-drive.md new file mode 100644 index 0000000000..3847274eda --- /dev/null +++ b/.changeset/healthy-comics-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Display the owner and system as links to the entity pages in the about card. diff --git a/plugins/catalog/src/components/AboutCard/AboutContent.tsx b/plugins/catalog/src/components/AboutCard/AboutContent.tsx index b8d5d84316..725217db9a 100644 --- a/plugins/catalog/src/components/AboutCard/AboutContent.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutContent.tsx @@ -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,11 @@ type Props = { export const AboutContent = ({ entity }: Props) => { const classes = useStyles(); + const [partOfSystemRelation] = getEntityRelations(entity, RELATION_PART_OF, { + kind: 'system', + }); + const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); + return ( @@ -43,22 +49,21 @@ export const AboutContent = ({ entity }: Props) => { {entity?.metadata?.description || 'No description'} + + {ownedByRelations.map((t, i) => [ + i > 0 && ', ', + , + ])} + 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(', ')} + label="System" + value="No System" gridSizes={{ xs: 12, sm: 6, lg: 4 }} - /> + > + {partOfSystemRelation && ( + + )} + ', () => { + 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(, { + 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(, { + wrapper: MemoryRouter, + }); + expect(getByText('component:test/software')).toBeInTheDocument(); + }); + + it('renders link for entity name in default namespace', () => { + const entityName = { + kind: 'Component', + namespace: 'default', + name: 'software', + }; + const { getByText } = render(, { + 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(, { + wrapper: MemoryRouter, + }); + expect(getByText('component:test/software')).toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx b/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx new file mode 100644 index 0000000000..1084fbd37e --- /dev/null +++ b/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx @@ -0,0 +1,75 @@ +/* + * 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; +}; + +// 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 }: 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, + name, + namespace, + })}`; + const routeParams = { + kind, + namespace: namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE, + name, + }; + + return ( + + {title} + + ); +}; diff --git a/plugins/catalog/src/components/EntityRefLink/index.ts b/plugins/catalog/src/components/EntityRefLink/index.ts new file mode 100644 index 0000000000..aa2c6641ef --- /dev/null +++ b/plugins/catalog/src/components/EntityRefLink/index.ts @@ -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';