Move code from ecosystem package to explore package

This commit is contained in:
Oliver Sand
2021-01-27 15:01:55 +01:00
parent 9cdd8f8888
commit 806929fe2f
26 changed files with 192 additions and 214 deletions
+18 -3
View File
@@ -1,7 +1,22 @@
# Title
# explore
Welcome to the explore plugin!
This plugin helps to visualize the domains and in your ecosystem.
## Sub-section 1
## Getting started
## Sub-section 2
To install the plugin, include the following import your `plugins.ts`:
```typescript
export { plugin as Ecosystem } from '@backstage/plugin-explore';
```
Add a link to the sidebar in `Root.tsx`:
```typescript
import LayersIcon from '@material-ui/icons/Layers';
...
<SidebarItem icon={LayersIcon} to="explore" text="Explore" />
```
+40 -1
View File
@@ -14,7 +14,46 @@
* 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).render();
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();
+2 -1
View File
@@ -31,6 +31,7 @@
},
"dependencies": {
"@backstage/core": "^0.5.0",
"@backstage/catalog-model": "^0.7.0",
"@backstage/theme": "^0.2.2",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -38,7 +39,7 @@
"classnames": "^2.2.6",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router": "6.0.0-beta.0",
"react-router-dom": "6.0.0-beta.0",
"react-use": "^15.3.3"
},
"devDependencies": {
@@ -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,66 @@
/*
* 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 {
Content,
ContentHeader,
EmptyState,
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 DomainExplorerContent = () => {
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 (
<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>
);
};
@@ -0,0 +1,34 @@
/*
* 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 { configApiRef, Header, Page, useApi } from '@backstage/core';
import React from 'react';
import { DomainExplorerContent } from './DomainExplorerContent';
export const DomainExplorerPage = () => {
const configApi = useApi(configApiRef);
const organizationName =
configApi.getOptionalString('organization.name') ?? 'Backstage';
return (
<Page themeId="home">
<Header
title={`Explore the ${organizationName} ecosystem`}
subtitle="Discover the domains in your ecosystem"
/>
<DomainExplorerContent />
</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';
@@ -0,0 +1,34 @@
/*
* 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 { configApiRef, Header, Page, useApi } from '@backstage/core';
import React from 'react';
import { ExploreTabs } from './ExploreTabs';
export const ExplorePage = () => {
const configApi = useApi(configApiRef);
const organizationName =
configApi.getOptionalString('organization.name') ?? 'Backstage';
return (
<Page themeId="home">
<Header
title={`Explore the ${organizationName} ecosystem`}
subtitle="Discover available solutions in your ecosystem"
/>
<ExploreTabs />
</Page>
);
};
@@ -0,0 +1,35 @@
/*
* 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 { Tabs } from '@backstage/core';
import React from 'react';
import { DomainExplorerContent } from '../DomainExplorerPage/DomainExplorerContent';
export const ExploreTabs = () => {
return (
<Tabs
tabs={[
{
label: `Domains`,
content: <DomainExplorerContent />,
},
{
label: `Tools`,
content: <DomainExplorerContent />,
},
]}
/>
);
};
@@ -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 { ExplorePage } from './ExplorePage';
+27
View File
@@ -0,0 +1,27 @@
/*
* 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 { createRoutableExtension } from '@backstage/core';
import { explorePlugin } from './plugin';
import { exploreRouteRef } from './routes';
export const ExplorePage = explorePlugin.provide(
createRoutableExtension({
component: () =>
import('./components/ExplorePage').then(m => m.ExplorePage),
mountPoint: exploreRouteRef,
}),
);
+3 -2
View File
@@ -14,5 +14,6 @@
* limitations under the License.
*/
export { plugin } from './plugin';
export { Router } from './components/Router';
export * from './extensions';
export { explorePlugin } from './plugin';
export * from './routes';
+6 -3
View File
@@ -14,9 +14,12 @@
* limitations under the License.
*/
import { createPlugin, createRouteRef } from '@backstage/core';
import { createPlugin } from '@backstage/core';
import { exploreRouteRef } from './routes';
export const rootRouteRef = createRouteRef({ path: '', title: 'Explore' });
export const plugin = createPlugin({
export const explorePlugin = createPlugin({
id: 'explore',
routes: {
explore: exploreRouteRef,
},
});
@@ -14,13 +14,12 @@
* limitations under the License.
*/
import React from 'react';
import { Route, Routes } from 'react-router';
import { ExplorePluginPage } from './ExplorePluginPage';
import { rootRouteRef } from '../plugin';
import { createRouteRef } from '@backstage/core';
export const Router = () => (
<Routes>
<Route path={`/${rootRouteRef.path}`} element={<ExplorePluginPage />} />
</Routes>
);
const NoIcon = () => null;
export const exploreRouteRef = createRouteRef({
icon: NoIcon,
path: '',
title: 'Explore',
});