Add api for configuring tools in the explore plugin

This commit is contained in:
Oliver Sand
2021-01-28 16:54:59 +01:00
parent 207024e2e0
commit c4c0f82b52
16 changed files with 364 additions and 98 deletions
+1
View File
@@ -33,6 +33,7 @@
"@backstage/catalog-model": "^0.7.0",
"@backstage/core": "^0.5.0",
"@backstage/plugin-catalog": "^0.2.12",
"@backstage/plugin-explore-react": "^0.0.1",
"@backstage/theme": "^0.2.2",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -43,6 +43,7 @@ export const DomainExplorerContent = () => {
<ContentHeader title="Domains">
<SupportButton>Discover the domains in your ecosystem.</SupportButton>
</ContentHeader>
{loading && <Progress />}
{!loading && (!entities || entities.length === 0) && (
<EmptyState
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { ExploreTool } from '@backstage/plugin-explore-react';
import { BackstageTheme } from '@backstage/theme';
import {
Button,
@@ -27,7 +28,6 @@ import {
} from '@material-ui/core';
import classNames from 'classnames';
import React from 'react';
import { CardData } from './types';
// TODO: Align styling between Domain and ToolCard
@@ -69,7 +69,7 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
}));
type Props = {
card: CardData;
card: ExploreTool;
objectFit?: 'cover' | 'contain';
};
@@ -14,11 +14,11 @@
* limitations under the License.
*/
import { ExploreTool } from '@backstage/plugin-explore-react';
import { BackstageTheme } from '@backstage/theme';
import { makeStyles } from '@material-ui/core';
import React from 'react';
import { ToolCard } from './ToolCard';
import { CardData } from './types';
const useStyles = makeStyles<BackstageTheme>(theme => ({
container: {
@@ -30,7 +30,7 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
}));
type ToolCardGridProps = {
tools: CardData[];
tools: ExploreTool[];
};
export const ToolCardGrid = ({ tools }: ToolCardGridProps) => {
@@ -38,7 +38,7 @@ export const ToolCardGrid = ({ tools }: ToolCardGridProps) => {
return (
<div className={classes.container}>
{tools.map((card: CardData, ix: any) => (
{tools.map((card: ExploreTool, ix: any) => (
<ToolCard card={card} key={ix} />
))}
</div>
@@ -15,4 +15,3 @@
*/
export { ToolCard } from './ToolCard';
export { ToolCardGrid } from './ToolCardGrid';
export type { CardData } from './types';
@@ -1,25 +0,0 @@
/*
* 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 type CardData = {
title: string;
description: string;
url: string;
image: string;
tags?: string[];
lifecycle?: string;
newsTag?: string;
};
@@ -0,0 +1,94 @@
/*
* 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 { ApiProvider, ApiRegistry } from '@backstage/core';
import {
ExploreTool,
exploreToolsConfigRef,
} from '@backstage/plugin-explore-react';
import { lightTheme } from '@backstage/theme';
import { ThemeProvider } from '@material-ui/core';
import { render, waitFor } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ToolExplorerContent } from './ToolExplorerContent';
describe('<ToolExplorerContent />', () => {
const exploreToolsConfigApi: jest.Mocked<typeof exploreToolsConfigRef.T> = {
getTools: jest.fn(),
};
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
<ThemeProvider theme={lightTheme}>
<MemoryRouter>
<ApiProvider
apis={ApiRegistry.with(exploreToolsConfigRef, exploreToolsConfigApi)}
>
{children}
</ApiProvider>
</MemoryRouter>
</ThemeProvider>
);
beforeEach(() => {
jest.resetAllMocks();
});
it('renders a grid of tools', async () => {
const tools: ExploreTool[] = [
{
title: 'Lighthouse',
description:
"Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.",
url: '/lighthouse',
image:
'https://raw.githubusercontent.com/GoogleChrome/lighthouse/8b3d7f052b2e64dd857e741d7395647f487697e7/assets/lighthouse-logo.png',
tags: ['web', 'seo', 'accessibility', 'performance'],
},
{
title: 'Tech Radar',
description:
'Tech Radar is a list of technologies, complemented by an assessment result, called ring assignment.',
url: '/tech-radar',
image:
'https://storage.googleapis.com/wf-blogs-engineering-media/2018/09/fe13bb32-wf-tech-radar-hero-1024x597.png',
tags: ['standards', 'landscape'],
},
];
exploreToolsConfigApi.getTools.mockResolvedValue(tools);
const { getByText } = render(<ToolExplorerContent />, {
wrapper: Wrapper,
});
await waitFor(() => {
expect(getByText('Lighthouse')).toBeInTheDocument();
expect(getByText('Tech Radar')).toBeInTheDocument();
});
});
it('renders empty state', async () => {
exploreToolsConfigApi.getTools.mockResolvedValue([]);
const { getByText } = render(<ToolExplorerContent />, {
wrapper: Wrapper,
});
await waitFor(() =>
expect(getByText('No tools to display')).toBeInTheDocument(),
);
});
});
@@ -13,97 +13,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Content, ContentHeader, SupportButton } from '@backstage/core';
import {
Content,
ContentHeader,
EmptyState,
Progress,
SupportButton,
useApi,
} from '@backstage/core';
import React from 'react';
import { useAsync } from 'react-use';
import { exploreToolsConfigRef } from '../../../../explore-react/src';
import { ToolCardGrid } from '../ToolCard';
// TODO: Provide API to configure the toolsCards
const toolsCards = [
{
title: 'New Relic',
description:
'Observability platform built to help engineers create and monitor their software',
url: '/newrelic',
image: 'https://i.imgur.com/L37ikrX.jpg',
tags: ['newrelic', 'performance', 'monitoring', 'errors', 'alerting'],
},
{
title: 'CircleCI',
description:
'Provides builds overview, detailed build info and retriggering functionality for CircleCI.',
url: '/circleci',
image: 'https://miro.medium.com/max/1200/1*hkTBp22vLAqlIHkrkZHPnw.png',
tags: ['circleci', 'ci', 'dev'],
},
{
title: 'Sentry',
description:
'Self-hosted and cloud-based error monitoring that helps software teams discover, triage, and prioritize errors in real-time.',
url: '/sentry',
image: 'https://sentry-brand.storage.googleapis.com/sentry-logo-black.png',
tags: ['sentry', 'monitoring', 'errors'],
},
{
title: 'Lighthouse',
description:
"Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.",
url: '/lighthouse',
image:
'https://raw.githubusercontent.com/GoogleChrome/lighthouse/8b3d7f052b2e64dd857e741d7395647f487697e7/assets/lighthouse-logo.png',
tags: ['web', 'seo', 'accessibility', 'performance'],
},
{
title: 'Tech Radar',
description:
'Tech Radar is a list of technologies, complemented by an assessment result, called ring assignment.',
url: '/tech-radar',
image:
'https://storage.googleapis.com/wf-blogs-engineering-media/2018/09/fe13bb32-wf-tech-radar-hero-1024x597.png',
tags: ['standards', 'landscape'],
},
{
title: 'Cost Insights',
description: 'Insights into cloud costs for your organization.',
url: '/cost-insights',
image: 'https://cloud.google.com/images/press/logo-cloud.png',
tags: ['cloud', 'finops'],
},
{
title: 'GraphiQL',
description:
'Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage.',
url: '/graphiql',
image:
'https://camo.githubusercontent.com/517398c3fbe0687d3d4dcbe05da82970b882e75a/68747470733a2f2f64337676366c703535716a6171632e636c6f756466726f6e742e6e65742f6974656d732f33413061324e314c3346324f304c3377326e316a2f477261706869514c382e706e673f582d436c6f75644170702d56697369746f722d49643d3433363432',
tags: ['graphql', 'dev'],
},
{
title: 'GitOps Clusters',
description:
'Create GitOps-managed clusters with Backstage. Currently supports EKS flavors and profiles like Machine Learning Ops (MLOps)',
url: '/gitops-clusters',
image: 'https://miro.medium.com/max/801/1*R28u8gj-hVdDFISoYqPhrQ.png',
tags: ['gitops', 'dev'],
},
{
title: 'Rollbar',
description:
'Error monitoring and crash reporting for agile development and continuous delivery',
url: '/rollbar',
image:
'https://images.ctfassets.net/cj4mgtttlyx7/4DfiWj9CbuHBi10uWK7JHn/5e94a6c5dbd5d50bdcd8d9e78f88689b/rollbar-seo.png',
tags: ['rollbar', 'monitoring', 'errors'],
},
];
export const ToolExplorerContent = () => {
const exploreToolsConfigApi = useApi(exploreToolsConfigRef);
const { value: tools, loading } = useAsync(async () => {
return await exploreToolsConfigApi.getTools();
}, [exploreToolsConfigApi]);
return (
<Content noPadding>
<ContentHeader title="Tools">
<SupportButton>Discover the tools in your ecosystem.</SupportButton>
</ContentHeader>
<ToolCardGrid tools={toolsCards} />
{loading && <Progress />}
{!loading && (!tools || tools.length === 0) && (
<EmptyState
missing="info"
title="No tools to display"
description={`You haven't added any tools yet.`}
/>
)}
{!loading && tools && <ToolCardGrid tools={tools} />}
</Content>
);
};
+16 -1
View File
@@ -14,11 +14,26 @@
* limitations under the License.
*/
import { createPlugin } from '@backstage/core';
import { createApiFactory, createPlugin } from '@backstage/core';
import { exploreToolsConfigRef } from '@backstage/plugin-explore-react';
import { exploreRouteRef } from './routes';
import { exampleTools } from './util/examples';
export const explorePlugin = createPlugin({
id: 'explore',
apis: [
// Register a default for exploreToolsConfigRef, you may want to override
// the API locally in your app.
createApiFactory({
api: exploreToolsConfigRef,
deps: {},
factory: () => ({
async getTools() {
return exampleTools;
},
}),
}),
],
routes: {
explore: exploreRouteRef,
},
+93
View File
@@ -0,0 +1,93 @@
/*
* 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 const exampleTools = [
{
title: 'New Relic',
description:
'Observability platform built to help engineers create and monitor their software',
url: '/newrelic',
image: 'https://i.imgur.com/L37ikrX.jpg',
tags: ['newrelic', 'performance', 'monitoring', 'errors', 'alerting'],
},
{
title: 'CircleCI',
description:
'Provides builds overview, detailed build info and retriggering functionality for CircleCI.',
url: '/circleci',
image: 'https://miro.medium.com/max/1200/1*hkTBp22vLAqlIHkrkZHPnw.png',
tags: ['circleci', 'ci', 'dev'],
},
{
title: 'Sentry',
description:
'Self-hosted and cloud-based error monitoring that helps software teams discover, triage, and prioritize errors in real-time.',
url: '/sentry',
image: 'https://sentry-brand.storage.googleapis.com/sentry-logo-black.png',
tags: ['sentry', 'monitoring', 'errors'],
},
{
title: 'Lighthouse',
description:
"Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.",
url: '/lighthouse',
image:
'https://raw.githubusercontent.com/GoogleChrome/lighthouse/8b3d7f052b2e64dd857e741d7395647f487697e7/assets/lighthouse-logo.png',
tags: ['web', 'seo', 'accessibility', 'performance'],
},
{
title: 'Tech Radar',
description:
'Tech Radar is a list of technologies, complemented by an assessment result, called ring assignment.',
url: '/tech-radar',
image:
'https://storage.googleapis.com/wf-blogs-engineering-media/2018/09/fe13bb32-wf-tech-radar-hero-1024x597.png',
tags: ['standards', 'landscape'],
},
{
title: 'Cost Insights',
description: 'Insights into cloud costs for your organization.',
url: '/cost-insights',
image: 'https://cloud.google.com/images/press/logo-cloud.png',
tags: ['cloud', 'finops'],
},
{
title: 'GraphiQL',
description:
'Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage.',
url: '/graphiql',
image:
'https://camo.githubusercontent.com/517398c3fbe0687d3d4dcbe05da82970b882e75a/68747470733a2f2f64337676366c703535716a6171632e636c6f756466726f6e742e6e65742f6974656d732f33413061324e314c3346324f304c3377326e316a2f477261706869514c382e706e673f582d436c6f75644170702d56697369746f722d49643d3433363432',
tags: ['graphql', 'dev'],
},
{
title: 'GitOps Clusters',
description:
'Create GitOps-managed clusters with Backstage. Currently supports EKS flavors and profiles like Machine Learning Ops (MLOps)',
url: '/gitops-clusters',
image: 'https://miro.medium.com/max/801/1*R28u8gj-hVdDFISoYqPhrQ.png',
tags: ['gitops', 'dev'],
},
{
title: 'Rollbar',
description:
'Error monitoring and crash reporting for agile development and continuous delivery',
url: '/rollbar',
image:
'https://images.ctfassets.net/cj4mgtttlyx7/4DfiWj9CbuHBi10uWK7JHn/5e94a6c5dbd5d50bdcd8d9e78f88689b/rollbar-seo.png',
tags: ['rollbar', 'monitoring', 'errors'],
},
];