Add ecosystem explorer

This commit is contained in:
Oliver Sand
2021-01-19 18:22:38 +01:00
parent 5fa3bdb553
commit 9cdd8f8888
18 changed files with 468 additions and 0 deletions
@@ -20,6 +20,7 @@ import HomeIcon from '@material-ui/icons/Home';
import ExtensionIcon from '@material-ui/icons/Extension';
import RuleIcon from '@material-ui/icons/AssignmentTurnedIn';
import MapIcon from '@material-ui/icons/MyLocation';
import LayersIcon from '@material-ui/icons/Layers';
import LibraryBooks from '@material-ui/icons/LibraryBooks';
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
import MoneyIcon from '@material-ui/icons/MonetizationOn';
@@ -86,6 +87,7 @@ const Root = ({ children }: PropsWithChildren<{}>) => (
{/* End global nav */}
<SidebarDivider />
<SidebarItem icon={MapIcon} to="tech-radar" text="Tech Radar" />
<SidebarItem icon={LayersIcon} to="ecosystem" text="Explore" />
<SidebarItem icon={RuleIcon} to="lighthouse" text="Lighthouse" />
<SidebarItem icon={MoneyIcon} to="cost-insights" text="Cost Insights" />
<SidebarItem
+1
View File
@@ -44,3 +44,4 @@ export { plugin as Buildkite } from '@roadiehq/backstage-plugin-buildkite';
export { plugin as Search } from '@backstage/plugin-search';
export { plugin as Org } from '@backstage/plugin-org';
export { plugin as Kafka } from '@backstage/plugin-kafka';
export { plugin as Ecosystem } from '@backstage/plugin-ecosystem';
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint')],
};
+24
View File
@@ -0,0 +1,24 @@
# ecosystem
Welcome to the ecosystem plugin!
This plugin helps to visualize the domains in your ecosystem.
![Domain Explorer](./docs/domains.png)
## Getting started
To install the plugin, include the following import your `plugins.ts`:
```typescript
export { plugin as Ecosystem } from '@backstage/plugin-ecosystem';
```
Add a link to the sidebar in `Root.tsx`:
```typescript
import LayersIcon from '@material-ui/icons/Layers';
...
<SidebarItem icon={LayersIcon} to="ecosystem" text="Explore" />
```
+58
View File
@@ -0,0 +1,58 @@
/*
* 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 { Entity } from '@backstage/catalog-model';
import { createDevApp } from '@backstage/dev-utils';
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog';
import { plugin } from '../src/plugin';
createDevApp()
.registerPlugin(plugin)
.registerApi({
api: catalogApiRef,
deps: {},
factory: () =>
({
async getEntities() {
const domainNames = [
'playback',
'artists',
'payments',
'analytics',
'songs',
'devops',
];
return {
items: domainNames.map(
(n, i) =>
({
apiVersion: 'backstage.io/v1alpha1',
kind: 'Domain',
metadata: {
name: n,
description: `Everything about ${n}`,
tags: i % 2 === 0 ? [n] : undefined,
},
spec: {
owner: `${n}@example.com`,
},
} as Entity),
),
};
},
} as CatalogApi),
})
.render();
Binary file not shown.

After

Width:  |  Height:  |  Size: 773 KiB

+50
View File
@@ -0,0 +1,50 @@
{
"name": "@backstage/plugin-ecosystem",
"version": "0.1.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"scripts": {
"build": "backstage-cli plugin:build",
"start": "backstage-cli plugin:serve",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"diff": "backstage-cli plugin:diff",
"prepack": "backstage-cli prepack",
"postpack": "backstage-cli postpack",
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.6.1",
"@backstage/core": "^0.4.4",
"@backstage/plugin-catalog": "^0.2.10",
"@backstage/theme": "^0.2.2",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "6.0.0-beta.0",
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.4.6",
"@backstage/dev-utils": "^0.1.7",
"@backstage/test-utils": "^0.1.6",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
"@types/jest": "^26.0.7",
"@types/node": "^12.0.0",
"msw": "^0.21.2",
"cross-fetch": "^3.0.6"
},
"files": [
"dist"
]
}
@@ -0,0 +1,49 @@
/*
* 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 { DomainEntity } from '@backstage/catalog-model';
import { render } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { DomainCard } from './DomainCard';
describe('<DomainCard />', () => {
it('renders a domain card', () => {
const entity: DomainEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Domain',
metadata: {
name: 'artists',
description: 'Everything about artists',
tags: ['a-tag'],
},
spec: {
owner: 'guest',
},
};
const { getByText } = render(<DomainCard entity={entity} />, {
wrapper: MemoryRouter,
});
expect(getByText('artists')).toBeInTheDocument();
expect(getByText('Everything about artists')).toBeInTheDocument();
expect(getByText('a-tag')).toBeInTheDocument();
expect(getByText('Explore')).toHaveAttribute(
'href',
'/catalog/default/domain/artists',
);
});
});
@@ -0,0 +1,38 @@
/*
* 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 { DomainEntity } from '@backstage/catalog-model';
import { ItemCard } from '@backstage/core';
import { entityRoute, entityRouteParams } from '@backstage/plugin-catalog';
import React from 'react';
import { generatePath } from 'react-router-dom';
type DomainCardProps = {
entity: DomainEntity;
};
export const DomainCard = ({ entity }: DomainCardProps) => (
<ItemCard
title={entity.metadata.name}
description={entity.metadata.description}
tags={entity.metadata.tags}
label="Explore"
// TODO: Use useRouteRef here to generate the path
href={generatePath(
`/catalog/${entityRoute.path}`,
entityRouteParams(entity),
)}
/>
);
@@ -0,0 +1,16 @@
/*
* 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.
*/
export { DomainCard } from './DomainCard';
@@ -0,0 +1,33 @@
/*
* 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 { DomainEntity } from '@backstage/catalog-model';
import { Grid } from '@material-ui/core';
import React from 'react';
import { DomainCard } from '../DomainCard';
type DomainExplorerProps = {
entities: DomainEntity[];
};
export const DomainExplorer = ({ entities }: DomainExplorerProps) => (
<Grid container spacing={4}>
{entities.map((e, i) => (
<Grid item xs={12} md={3} key={i}>
<DomainCard entity={e} />
</Grid>
))}
</Grid>
);
@@ -0,0 +1,16 @@
/*
* 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.
*/
export { DomainExplorer } from './DomainExplorer';
@@ -0,0 +1,78 @@
/*
* 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 { DomainEntity } from '@backstage/catalog-model';
import {
configApiRef,
Content,
ContentHeader,
EmptyState,
Header,
Page,
Progress,
SupportButton,
useApi,
} from '@backstage/core';
import { catalogApiRef } from '@backstage/plugin-catalog';
import { Button } from '@material-ui/core';
import React from 'react';
import { useAsync } from 'react-use';
import { DomainExplorer } from '../DomainExplorer/DomainExplorer';
export const DomainExplorerPage = () => {
const configApi = useApi(configApiRef);
const organizationName =
configApi.getOptionalString('organization.name') ?? 'Backstage';
const catalogApi = useApi(catalogApiRef);
const { value: entities, loading } = useAsync(async () => {
const response = await catalogApi.getEntities({
filter: { kind: 'domain' },
});
return response.items as DomainEntity[];
}, [catalogApi]);
return (
<Page themeId="home">
<Header
title={`Explore the ${organizationName} ecosystem`}
subtitle="Discover the domains in your ecosystem"
/>
<Content>
<ContentHeader title="Domains">
<SupportButton>Discover the domains in your ecosystem.</SupportButton>
</ContentHeader>
{loading && <Progress />}
{!loading && (!entities || entities.length === 0) && (
<EmptyState
missing="info"
title="No domains to display"
description={`You haven't added any domains yet.`}
action={
<Button
variant="contained"
color="primary"
href="https://backstage.io/docs/features/software-catalog/descriptor-format#kind-domain"
>
Read more
</Button>
}
/>
)}
{!loading && entities && <DomainExplorer entities={entities} />}
</Content>
</Page>
);
};
@@ -0,0 +1,16 @@
/*
* 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.
*/
export { DomainExplorerPage } from './DomainExplorerPage';
+16
View File
@@ -0,0 +1,16 @@
/*
* 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.
*/
export { plugin } from './plugin';
+22
View File
@@ -0,0 +1,22 @@
/*
* 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 { plugin } from './plugin';
describe('ecosystem', () => {
it('should export plugin', () => {
expect(plugin).toBeDefined();
});
});
+29
View File
@@ -0,0 +1,29 @@
/*
* 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 { createPlugin, createRouteRef } from '@backstage/core';
import { DomainExplorerPage } from './components/DomainExplorerPage';
export const rootRouteRef = createRouteRef({
path: '/ecosystem',
title: 'ecosystem',
});
export const plugin = createPlugin({
id: 'ecosystem',
register({ router }) {
router.addRoute(rootRouteRef, DomainExplorerPage);
},
});
+17
View File
@@ -0,0 +1,17 @@
/*
* 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 '@testing-library/jest-dom';
import 'cross-fetch/polyfill';