Added RelatedEntitesCard as a base implementation of displaying entities that are related to another entity, and refactored cards that display related entities to use it.

Signed-off-by: Jonah Grimes <jgrimes@appriss.com>
This commit is contained in:
Jonah Grimes
2021-04-29 13:05:57 -04:00
parent 2f989a972d
commit 4f88bf4e07
10 changed files with 267 additions and 384 deletions
+7 -3
View File
@@ -2,7 +2,11 @@
'@backstage/plugin-catalog': patch
---
- Added `EntityHasResourcesCard` to display resources that are part of a system.
- Added `EntityDependsOnComponentsCard` to display components that are dependencies of a component.
- Added `EntityDependsOnResourcesCard` to display resources that are dependencies of a component.
- Added `RelatedEntitesCard` as a base implementation of displaying entities that are related to another entity.
- Added `HasResourcesCard` to display resources that are part of a system.
- Added `DependsOnComponentsCard` to display components that are dependencies of a component.
- Added `DependsOnResourcesCard` to display resources that are dependencies of a component.
- Refactored `HasComponentsCard` to use base `RelatedEntitiesCard`. Card remains backwards compatible.
- Refactored `HasSubcomponentsCard` to use base `RelatedEntitiesCard`. Card remains backwards compatible.
- Refactored `HasSystemsCard` to use base `RelatedEntitiesCard`. Card remains backwards compatible.
- Updated the example app to take advantage of these new components.
@@ -14,79 +14,30 @@
* limitations under the License.
*/
import { ComponentEntity, RELATION_DEPENDS_ON } from '@backstage/catalog-model';
import {
CodeSnippet,
InfoCard,
Link,
Progress,
WarningPanel,
} from '@backstage/core';
import { Typography } from '@material-ui/core';
import {
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import { RELATION_DEPENDS_ON } from '@backstage/catalog-model';
import React from 'react';
import {
asComponentEntities,
componentColumns,
componentHelpLink,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const DependsOnComponentsCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: RELATION_DEPENDS_ON,
kind: 'Component',
});
if (loading) {
return (
<InfoCard variant={variant} title="Components">
<Progress />
</InfoCard>
);
}
if (error || !entities) {
return (
<InfoCard variant={variant} title="Components">
<WarningPanel
severity="error"
title="Could not load components"
message={<CodeSnippet text={`${error}`} language="text" />}
/>
</InfoCard>
);
}
return (
<EntityTable
title="Components"
<RelatedEntitiesCard
variant={variant}
emptyContent={
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">
No component is a dependency of this component.
</Typography>
<Typography variant="body2">
<Link to="https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component">
Learn how to change this.
</Link>
</Typography>
</div>
}
columns={columns}
entities={entities as ComponentEntity[]}
title="Components"
entityKind="Component"
relationType={RELATION_DEPENDS_ON}
columns={componentColumns}
emptyMessage="No component is a dependency of this component"
emptyHelpLink={componentHelpLink}
asRenderableEntities={asComponentEntities}
/>
);
};
@@ -14,79 +14,30 @@
* limitations under the License.
*/
import { ComponentEntity, RELATION_DEPENDS_ON } from '@backstage/catalog-model';
import {
CodeSnippet,
InfoCard,
Link,
Progress,
WarningPanel,
} from '@backstage/core';
import { Typography } from '@material-ui/core';
import {
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import { RELATION_DEPENDS_ON } from '@backstage/catalog-model';
import React from 'react';
import {
asResourceEntities,
componentHelpLink,
RelatedEntitiesCard,
resourceColumns,
} from '../RelatedEntitiesCard';
type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'resource' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const DependsOnResourcesCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: RELATION_DEPENDS_ON,
kind: 'Resource',
});
if (loading) {
return (
<InfoCard variant={variant} title="Resources">
<Progress />
</InfoCard>
);
}
if (error || !entities) {
return (
<InfoCard variant={variant} title="Resources">
<WarningPanel
severity="error"
title="Could not load resources"
message={<CodeSnippet text={`${error}`} language="text" />}
/>
</InfoCard>
);
}
return (
<EntityTable
title="Resources"
<RelatedEntitiesCard
variant={variant}
emptyContent={
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">
No resource is a dependency of this component.
</Typography>
<Typography variant="body2">
<Link to="https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component">
Learn how to change this.
</Link>
</Typography>
</div>
}
columns={columns}
entities={entities as ComponentEntity[]}
title="Resources"
entityKind="Resource"
relationType={RELATION_DEPENDS_ON}
columns={resourceColumns}
emptyMessage="No resource is a dependency of this component"
emptyHelpLink={componentHelpLink}
asRenderableEntities={asResourceEntities}
/>
);
};
@@ -14,79 +14,30 @@
* limitations under the License.
*/
import { ComponentEntity, RELATION_HAS_PART } from '@backstage/catalog-model';
import {
CodeSnippet,
InfoCard,
Link,
Progress,
WarningPanel,
} from '@backstage/core';
import { Typography } from '@material-ui/core';
import {
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import { RELATION_HAS_PART } from '@backstage/catalog-model';
import React from 'react';
import {
asComponentEntities,
componentColumns,
componentHelpLink,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasComponentsCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: RELATION_HAS_PART,
kind: 'Component',
});
if (loading) {
return (
<InfoCard variant={variant} title="Components">
<Progress />
</InfoCard>
);
}
if (error || !entities) {
return (
<InfoCard variant={variant} title="Components">
<WarningPanel
severity="error"
title="Could not load components"
message={<CodeSnippet text={`${error}`} language="text" />}
/>
</InfoCard>
);
}
return (
<EntityTable
title="Components"
<RelatedEntitiesCard
variant={variant}
emptyContent={
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">
No component is part of this system.
</Typography>
<Typography variant="body2">
<Link to="https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component">
Learn how to change this.
</Link>
</Typography>
</div>
}
columns={columns}
entities={entities as ComponentEntity[]}
title="Components"
entityKind="Component"
relationType={RELATION_HAS_PART}
columns={componentColumns}
emptyMessage="No component is part of this system"
emptyHelpLink={componentHelpLink}
asRenderableEntities={asComponentEntities}
/>
);
};
@@ -14,79 +14,30 @@
* limitations under the License.
*/
import { ResourceEntity, RELATION_HAS_PART } from '@backstage/catalog-model';
import {
CodeSnippet,
InfoCard,
Link,
Progress,
WarningPanel,
} from '@backstage/core';
import { Typography } from '@material-ui/core';
import {
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import { RELATION_HAS_PART } from '@backstage/catalog-model';
import React from 'react';
import {
asResourceEntities,
RelatedEntitiesCard,
resourceColumns,
resourceHelpLink,
} from '../RelatedEntitiesCard';
type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'resource' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasResourcesCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: RELATION_HAS_PART,
kind: 'Resource',
});
if (loading) {
return (
<InfoCard variant={variant} title="Resources">
<Progress />
</InfoCard>
);
}
if (error || !entities) {
return (
<InfoCard variant={variant} title="Resources">
<WarningPanel
severity="error"
title="Could not load resources"
message={<CodeSnippet text={`${error}`} language="text" />}
/>
</InfoCard>
);
}
return (
<EntityTable
title="Resources"
<RelatedEntitiesCard
variant={variant}
emptyContent={
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">
No resource is part of this system.
</Typography>
<Typography variant="body2">
<Link to="https://backstage.io/docs/features/software-catalog/descriptor-format#kind-resource">
Learn how to change this.
</Link>
</Typography>
</div>
}
columns={columns}
entities={entities as ResourceEntity[]}
title="Resources"
entityKind="Resource"
relationType={RELATION_HAS_PART}
columns={resourceColumns}
asRenderableEntities={asResourceEntities}
emptyMessage="No resource is part of this system"
emptyHelpLink={resourceHelpLink}
/>
);
};
@@ -14,79 +14,29 @@
* limitations under the License.
*/
import { ComponentEntity, RELATION_HAS_PART } from '@backstage/catalog-model';
import {
CodeSnippet,
InfoCard,
Link,
Progress,
WarningPanel,
} from '@backstage/core';
import { Typography } from '@material-ui/core';
import {
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import { RELATION_HAS_PART } from '@backstage/catalog-model';
import React from 'react';
import {
asComponentEntities,
componentColumns,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasSubcomponentsCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: RELATION_HAS_PART,
kind: 'Component',
});
if (loading) {
return (
<InfoCard variant={variant} title="Subcomponents">
<Progress />
</InfoCard>
);
}
if (error || !entities) {
return (
<InfoCard variant={variant} title="Subcomponents">
<WarningPanel
severity="error"
title="Could not load subcomponents"
message={<CodeSnippet text={`${error}`} language="text" />}
/>
</InfoCard>
);
}
return (
<EntityTable
title="Subcomponents"
<RelatedEntitiesCard
variant={variant}
emptyContent={
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">
No subcomponent is part of this component.
</Typography>
<Typography variant="body2">
<Link to="https://backstage.io/docs/features/software-catalog/descriptor-format#specsubcomponentof-optional">
Learn how to change this.
</Link>
</Typography>
</div>
}
columns={columns}
entities={entities as ComponentEntity[]}
title="Subcomponents"
entityKind="Component"
relationType={RELATION_HAS_PART}
columns={componentColumns}
asRenderableEntities={asComponentEntities}
emptyMessage="No subcomponent is part of this component"
emptyHelpLink="https://backstage.io/docs/features/software-catalog/descriptor-format#specsubcomponentof-optional"
/>
);
};
@@ -14,76 +14,30 @@
* limitations under the License.
*/
import { RELATION_HAS_PART, SystemEntity } from '@backstage/catalog-model';
import {
CodeSnippet,
InfoCard,
Link,
Progress,
WarningPanel,
} from '@backstage/core';
import { Typography } from '@material-ui/core';
import {
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import { RELATION_HAS_PART } from '@backstage/catalog-model';
import React from 'react';
import {
asSystemEntities,
RelatedEntitiesCard,
systemColumns,
systemHelpLink,
} from '../RelatedEntitiesCard';
type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'system' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasSystemsCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: RELATION_HAS_PART,
});
if (loading) {
return (
<InfoCard variant={variant} title="Systems">
<Progress />
</InfoCard>
);
}
if (error || !entities) {
return (
<InfoCard variant={variant} title="Systems">
<WarningPanel
severity="error"
title="Could not load systems"
message={<CodeSnippet text={`${error}`} language="text" />}
/>
</InfoCard>
);
}
return (
<EntityTable
title="Systems"
<RelatedEntitiesCard
variant={variant}
emptyContent={
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">
No system is part of this domain.
</Typography>
<Typography variant="body2">
<Link to="https://backstage.io/docs/features/software-catalog/descriptor-format#kind-system">
Learn how to change this.
</Link>
</Typography>
</div>
}
columns={columns}
entities={entities as SystemEntity[]}
title="Systems"
entityKind="System"
relationType={RELATION_HAS_PART}
columns={systemColumns}
asRenderableEntities={asSystemEntities}
emptyMessage="No system is part of this domain"
emptyHelpLink={systemHelpLink}
/>
);
};
@@ -0,0 +1,97 @@
/*
* 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 } from '@backstage/catalog-model';
import {
CodeSnippet,
InfoCard,
Link,
Progress,
TableColumn,
WarningPanel,
} from '@backstage/core';
import { Typography } from '@material-ui/core';
import {
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import React from 'react';
type Props = {
variant?: 'gridItem';
title: string;
columns: TableColumn<Entity>[];
entityKind: string;
relationType: string;
emptyMessage: string;
emptyHelpLink: string;
asRenderableEntities: (entities: Entity[]) => Entity[];
};
export const RelatedEntitiesCard = ({
variant = 'gridItem',
title,
columns,
entityKind,
relationType,
emptyMessage,
emptyHelpLink,
asRenderableEntities,
}: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: relationType,
kind: entityKind,
});
if (loading) {
return (
<InfoCard variant={variant} title={title}>
<Progress />
</InfoCard>
);
}
if (error || !entities) {
return (
<InfoCard variant={variant} title={title}>
<WarningPanel
severity="error"
title={`Could not load ${{ title }}`}
message={<CodeSnippet text={`${error}`} language="text" />}
/>
</InfoCard>
);
}
return (
<EntityTable
title={title}
variant={variant}
emptyContent={
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">{emptyMessage}</Typography>
<Typography variant="body2">
<Link to={emptyHelpLink}>Learn how to change this.</Link>
</Typography>
</div>
}
columns={columns}
entities={asRenderableEntities(entities)}
/>
);
};
@@ -0,0 +1,56 @@
/*
* Copyright 2021 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 {
ComponentEntity,
Entity,
ResourceEntity,
SystemEntity,
} from '@backstage/catalog-model';
import { EntityTable } from '@backstage/plugin-catalog-react';
export const componentColumns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const componentHelpLink =
'https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component';
export const asComponentEntities = (entities: Entity[]) =>
entities as ComponentEntity[];
export const resourceColumns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'resource' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const resourceHelpLink =
'https://backstage.io/docs/features/software-catalog/descriptor-format#kind-resource';
export const asResourceEntities = (entities: Entity[]) =>
entities as ResourceEntity[];
export const systemColumns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'system' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const systemHelpLink =
'https://backstage.io/docs/features/software-catalog/descriptor-format#kind-system';
export const asSystemEntities = (entities: Entity[]) =>
entities as SystemEntity[];
@@ -0,0 +1,18 @@
/*
* 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 { RelatedEntitiesCard } from './RelatedEntitiesCard';
export * from './constants';