Merge branch 'master' of github.com:spotify/backstage into shmidt-i/circle-ci-plugin-new-route-api
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
|
||||
@@ -14,8 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createApiFactory,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/core';
|
||||
import { catalogApiRef } from './api/types';
|
||||
import { CatalogClient } from './api/CatalogClient';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'catalog',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: catalogApiRef,
|
||||
deps: { discoveryApi: discoveryApiRef },
|
||||
factory: ({ discoveryApi }) => new CatalogClient({ discoveryApi }),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -20,9 +20,9 @@ import { circleCIApiRef, CircleCIApi } from '../src/api';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(plugin)
|
||||
.registerApiFactory({
|
||||
.registerApi({
|
||||
api: circleCIApiRef,
|
||||
deps: {},
|
||||
factory: () => new CircleCIApi(),
|
||||
implements: circleCIApiRef,
|
||||
})
|
||||
.render();
|
||||
|
||||
@@ -13,8 +13,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createPlugin } from '@backstage/core';
|
||||
|
||||
import { createPlugin, createApiFactory, configApiRef } from '@backstage/core';
|
||||
import { circleCIApiRef, CircleCIApi } from './api';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'circleci',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: circleCIApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) =>
|
||||
new CircleCIApi(
|
||||
`${configApi.getString('backend.baseUrl')}/proxy/circleci/api`,
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-use": "^15.3.3"
|
||||
"react-use": "^15.3.3",
|
||||
"react-router": "6.0.0-beta.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.21",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { makeStyles, Typography } from '@material-ui/core';
|
||||
import {
|
||||
Content,
|
||||
@@ -107,7 +107,7 @@ const toolsCards = [
|
||||
},
|
||||
];
|
||||
|
||||
const ExplorePluginPage: FC<{}> = () => {
|
||||
export const ExplorePluginPage = () => {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Page theme={pageTheme.home}>
|
||||
@@ -130,5 +130,3 @@ const ExplorePluginPage: FC<{}> = () => {
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExplorePluginPage;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import { Route, Routes } from 'react-router';
|
||||
import { ExplorePluginPage } from './ExplorePluginPage';
|
||||
import { rootRouteRef } from '../plugin';
|
||||
|
||||
export const Router = () => (
|
||||
<Routes>
|
||||
<Route path={`/${rootRouteRef.path}`} element={<ExplorePluginPage />} />
|
||||
</Routes>
|
||||
);
|
||||
@@ -15,3 +15,4 @@
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export { Router } from './components/Router';
|
||||
|
||||
@@ -14,12 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import ExplorePluginPage from './components/ExplorePluginPage';
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
|
||||
export const rootRouteRef = createRouteRef({ path: '', title: 'Explore' });
|
||||
export const plugin = createPlugin({
|
||||
id: 'explore',
|
||||
register({ router }) {
|
||||
router.registerRoute('/explore', ExplorePluginPage);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -14,10 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
createApiFactory,
|
||||
} from '@backstage/core';
|
||||
import { ProjectListPage } from './components/ProjectListPage';
|
||||
import { ProjectDetailsPage } from './components/ProjectDetailsPage';
|
||||
import { NewProjectPage } from './components/NewProjectPage';
|
||||
import { GCPApiRef, GCPClient } from './api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
path: '/gcp-projects',
|
||||
@@ -34,6 +39,7 @@ export const NewProjectRouteRef = createRouteRef({
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'gcp-projects',
|
||||
apis: [createApiFactory(GCPApiRef, new GCPClient())],
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, ProjectListPage);
|
||||
router.addRoute(ProjectRouteRef, ProjectDetailsPage);
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
createApiFactory,
|
||||
} from '@backstage/core';
|
||||
import { githubActionsApiRef, GithubActionsClient } from './api';
|
||||
|
||||
// TODO(freben): This is just a demo route for now
|
||||
export const rootRouteRef = createRouteRef({
|
||||
@@ -29,4 +34,5 @@ export const buildRouteRef = createRouteRef({
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'github-actions',
|
||||
apis: [createApiFactory(githubActionsApiRef, new GithubActionsClient())],
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import { createPlugin, createApiFactory } from '@backstage/core';
|
||||
import ProfileCatalog from './components/ProfileCatalog';
|
||||
import ClusterPage from './components/ClusterPage';
|
||||
import ClusterList from './components/ClusterList';
|
||||
@@ -23,9 +23,13 @@ import {
|
||||
gitOpsClusterDetailsRoute,
|
||||
gitOpsClusterCreateRoute,
|
||||
} from './routes';
|
||||
import { gitOpsApiRef, GitOpsRestApi } from './api';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'gitops-profiles',
|
||||
apis: [
|
||||
createApiFactory(gitOpsApiRef, new GitOpsRestApi('http://localhost:3008')),
|
||||
],
|
||||
register({ router }) {
|
||||
router.addRoute(gitOpsClusterListRoute, ClusterList);
|
||||
router.addRoute(gitOpsClusterDetailsRoute, ClusterPage);
|
||||
|
||||
@@ -20,8 +20,8 @@ import { plugin, GraphQLEndpoints, graphQlBrowseApiRef } from '../src';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(plugin)
|
||||
.registerApiFactory({
|
||||
implements: graphQlBrowseApiRef,
|
||||
.registerApi({
|
||||
api: graphQlBrowseApiRef,
|
||||
deps: {
|
||||
errorApi: errorApiRef,
|
||||
githubAuthApi: githubAuthApiRef,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Content,
|
||||
Header,
|
||||
@@ -30,7 +30,7 @@ import { graphQlBrowseApiRef } from '../../lib/api';
|
||||
import { GraphiQLBrowser } from '../GraphiQLBrowser';
|
||||
import { Typography } from '@material-ui/core';
|
||||
|
||||
export const GraphiQLPage: FC<{}> = () => {
|
||||
export const GraphiQLPage = () => {
|
||||
const graphQlBrowseApi = useApi(graphQlBrowseApiRef);
|
||||
const endpoints = useAsync(() => graphQlBrowseApi.getEndpoints());
|
||||
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export { GraphiQLPage as Router } from './components';
|
||||
export * from './lib/api';
|
||||
export * from './route-refs';
|
||||
|
||||
@@ -14,13 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import { GraphiQLPage } from './components';
|
||||
import { graphiQLRouteRef } from './route-refs';
|
||||
import { createPlugin, createApiFactory } from '@backstage/core';
|
||||
import { graphQlBrowseApiRef, GraphQLEndpoints } from './lib/api';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'graphiql',
|
||||
register({ router }) {
|
||||
router.addRoute(graphiQLRouteRef, GraphiQLPage);
|
||||
},
|
||||
apis: [
|
||||
// GitLab is used as an example endpoint, but most plug
|
||||
createApiFactory(
|
||||
graphQlBrowseApiRef,
|
||||
GraphQLEndpoints.from([
|
||||
GraphQLEndpoints.create({
|
||||
id: 'gitlab',
|
||||
title: 'GitLab',
|
||||
url: 'https://gitlab.com/api/graphql',
|
||||
}),
|
||||
]),
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -14,8 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
createApiFactory,
|
||||
configApiRef,
|
||||
} from '@backstage/core';
|
||||
import { DetailedViewPage } from './pages/BuildWithStepsPage';
|
||||
import { jenkinsApiRef, JenkinsApi } from './api';
|
||||
|
||||
export const buildRouteRef = createRouteRef({
|
||||
path: '/jenkins/job',
|
||||
@@ -24,6 +30,16 @@ export const buildRouteRef = createRouteRef({
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'jenkins',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: jenkinsApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) =>
|
||||
new JenkinsApi(
|
||||
`${configApi.getString('backend.baseUrl')}/proxy/jenkins/api`,
|
||||
),
|
||||
}),
|
||||
],
|
||||
register({ router }) {
|
||||
router.addRoute(buildRouteRef, DetailedViewPage);
|
||||
},
|
||||
|
||||
@@ -20,8 +20,8 @@ import { lighthouseApiRef, LighthouseRestApi } from '../src';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(plugin)
|
||||
.registerApiFactory({
|
||||
implements: lighthouseApiRef,
|
||||
.registerApi({
|
||||
api: lighthouseApiRef,
|
||||
deps: {},
|
||||
factory: () => new LighthouseRestApi('http://localhost:3003'),
|
||||
})
|
||||
|
||||
@@ -14,13 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import { createPlugin, createApiFactory, configApiRef } from '@backstage/core';
|
||||
import AuditList from './components/AuditList';
|
||||
import AuditView from './components/AuditView';
|
||||
import CreateAudit from './components/CreateAudit';
|
||||
import { lighthouseApiRef, LighthouseRestApi } from './api';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'lighthouse',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: lighthouseApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) => LighthouseRestApi.fromConfig(configApi),
|
||||
}),
|
||||
],
|
||||
register({ router }) {
|
||||
router.registerRoute('/lighthouse', AuditList);
|
||||
router.registerRoute('/lighthouse/audit/:id', AuditView);
|
||||
|
||||
@@ -14,13 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createApiFactory,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/core';
|
||||
import { rootRouteRef, entityRouteRef } from './routes';
|
||||
import { RollbarHome } from './components/RollbarHome/RollbarHome';
|
||||
import { RollbarProjectPage } from './components/RollbarProjectPage/RollbarProjectPage';
|
||||
import { rollbarApiRef } from './api/RollbarApi';
|
||||
import { RollbarClient } from './api/RollbarClient';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'rollbar',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: rollbarApiRef,
|
||||
deps: { discoveryApi: discoveryApiRef },
|
||||
factory: ({ discoveryApi }) => new RollbarClient({ discoveryApi }),
|
||||
}),
|
||||
],
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, RollbarHome);
|
||||
router.addRoute(entityRouteRef, RollbarProjectPage);
|
||||
|
||||
@@ -14,13 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createApiFactory,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/core';
|
||||
import { ScaffolderPage } from './components/ScaffolderPage';
|
||||
import { TemplatePage } from './components/TemplatePage';
|
||||
import { rootRoute, templateRoute } from './routes';
|
||||
import { scaffolderApiRef, ScaffolderApi } from './api';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'scaffolder',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: scaffolderApiRef,
|
||||
deps: { discoveryApi: discoveryApiRef },
|
||||
factory: ({ discoveryApi }) => new ScaffolderApi({ discoveryApi }),
|
||||
}),
|
||||
],
|
||||
register({ router }) {
|
||||
router.addRoute(rootRoute, ScaffolderPage);
|
||||
router.addRoute(templateRoute, TemplatePage);
|
||||
|
||||
@@ -29,71 +29,34 @@ For either simple or advanced installations, you'll need to add the dependency u
|
||||
yarn add @backstage/plugin-tech-radar
|
||||
```
|
||||
|
||||
### Simple Configuration
|
||||
### Configuration
|
||||
|
||||
In your `apis.ts` set up the simple "out of the box" implementation for Tech Radar:
|
||||
|
||||
```ts
|
||||
import { ApiHolder, ApiRegistry } from '@backstage/core';
|
||||
import {
|
||||
techRadarApiRef,
|
||||
TechRadar,
|
||||
} from '@backstage/plugin-tech-radar';
|
||||
|
||||
const builder = ApiRegistry.builder();
|
||||
|
||||
builder.add(techRadarApiRef, new TechRadar({
|
||||
width: 1400,
|
||||
height: 800
|
||||
));
|
||||
|
||||
export default builder.build() as ApiHolder;
|
||||
```
|
||||
|
||||
Congrats, you're done! We'll just load it with [example data](src/sampleData.ts) to get you started. Just go to <http://localhost:3000/tech-radar> to see it live in action.
|
||||
|
||||
And if you'd like to configure it more, such as providing it with your own data, see the `TechRadarApi` TypeScript interface below for the options:
|
||||
|
||||
```ts
|
||||
export interface TechRadarComponentProps {
|
||||
width: number;
|
||||
height: number;
|
||||
getData?: () => Promise<TechRadarLoaderResponse>;
|
||||
svgProps?: object;
|
||||
}
|
||||
|
||||
export interface TechRadarApi extends TechRadarComponentProps {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
}
|
||||
```
|
||||
|
||||
You can see the API directly over at [src/api.ts](./src/api.ts).
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This way won't expose an `/tech-radar` path. Instead, you'll need to create your own Backstage plugin and use the Tech Radar as any other React UI component.
|
||||
|
||||
In your Backstage app, run the following command:
|
||||
|
||||
```sh
|
||||
yarn create-plugin
|
||||
```
|
||||
|
||||
In your plugin, in any React component you'd like to import the Tech Radar, do the following:
|
||||
Modify your app routes to include the Router component exported from the tech radar, for example:
|
||||
|
||||
```tsx
|
||||
import { TechRadarComponent } from '@backstage/plugin-tech-radar';
|
||||
import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar';
|
||||
|
||||
function MyCustomRadar() {
|
||||
return <TechRadarComponent width={1400} height={800} />;
|
||||
}
|
||||
// Inside App component
|
||||
<Routes>
|
||||
{/* other routes ... */}
|
||||
<Route
|
||||
path="/tech-radar"
|
||||
element={<TechRadarRouter width={1500} height={800} />}
|
||||
/>
|
||||
{/* other routes ... */}
|
||||
</Routes>;
|
||||
```
|
||||
|
||||
If you'd like to configure it more, see the `TechRadarComponentProps` TypeScript interface for options:
|
||||
If you'd like to configure it more, see the `TechRadarPageProps` and `TechRadarComponentProps` types for options:
|
||||
|
||||
```ts
|
||||
export interface TechRadarComponentProps {
|
||||
export type TechRadarPageProps = TechRadarComponentProps & {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
pageTitle?: string;
|
||||
};
|
||||
|
||||
export interface TechRadarPageProps {
|
||||
width: number;
|
||||
height: number;
|
||||
getData?: () => Promise<TechRadarLoaderResponse>;
|
||||
@@ -101,8 +64,6 @@ export interface TechRadarComponentProps {
|
||||
}
|
||||
```
|
||||
|
||||
You can see the API directly over at [src/api.ts](./src/api.ts).
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
### Who created the Tech Radar?
|
||||
@@ -111,7 +72,7 @@ You can see the API directly over at [src/api.ts](./src/api.ts).
|
||||
|
||||
### How do I load in my own data?
|
||||
|
||||
It's simple. In both the Simple (Backstage plugin) and Advanced (React component) configurations, you can pass through a `getData` prop which expects a `Promise<TechRadarLoaderResponse>` signature. See more in [src/api.ts](./src/api.ts).
|
||||
It's simple, you can pass through a `getData` prop which expects a `Promise<TechRadarLoaderResponse>` signature.
|
||||
|
||||
Here's an example:
|
||||
|
||||
@@ -133,42 +94,21 @@ const getHardCodedData = () =>
|
||||
],
|
||||
});
|
||||
|
||||
// Simple
|
||||
builder.add(techRadarApiRef, new TechRadar({
|
||||
width: 1400,
|
||||
height: 800,
|
||||
getData: getHardCodedData
|
||||
));
|
||||
|
||||
// Advanced
|
||||
<TechRadarComponent width={1400} height={800} getData={getHardCodedData} />
|
||||
<TechRadarComponent width={1400} height={800} getData={getHardCodedData} />;
|
||||
```
|
||||
|
||||
### How do I write tests?
|
||||
|
||||
You can use the `svgProps` option to pass custom React props to the `<svg>` element we create for the Tech Radar. This complements well with the `data-testid` attribute and the `@testing-library/react` library we use in Backstage.
|
||||
|
||||
```ts
|
||||
// Simple
|
||||
builder.add(
|
||||
techRadarApiRef,
|
||||
new TechRadar({
|
||||
width: 1400,
|
||||
height: 800,
|
||||
svgProps: {
|
||||
'data-testid': 'tech-radar-svg',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Advanced
|
||||
```tsx
|
||||
<TechRadarComponent
|
||||
width={1400}
|
||||
height={800}
|
||||
svgProps={{
|
||||
'data-testid': 'tech-radar-svg',
|
||||
}}
|
||||
/>;
|
||||
/>
|
||||
|
||||
// Then, in your tests...
|
||||
// const { getByTestId } = render(...);
|
||||
|
||||
@@ -15,14 +15,6 @@
|
||||
*/
|
||||
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { techRadarApiRef, TechRadar } from '../src';
|
||||
import { plugin } from '../src';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(plugin)
|
||||
.registerApiFactory({
|
||||
implements: techRadarApiRef,
|
||||
deps: {},
|
||||
factory: () => new TechRadar({ width: 1500, height: 800 }),
|
||||
})
|
||||
.render();
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.21",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.21",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.21",
|
||||
"@backstage/theme": "^0.1.1-alpha.21",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef } from '@backstage/core';
|
||||
import { MovedState } from './utils/types';
|
||||
|
||||
/**
|
||||
@@ -72,37 +71,3 @@ export interface TechRadarApi extends TechRadarComponentProps {
|
||||
subtitle?: string;
|
||||
pageTitle?: string;
|
||||
}
|
||||
|
||||
export const techRadarApiRef = createApiRef<TechRadarApi>({
|
||||
id: 'plugin.techradar',
|
||||
description: 'Used by the Tech Radar to render the visualization',
|
||||
});
|
||||
|
||||
export class TechRadar implements TechRadarApi {
|
||||
// Default columns
|
||||
public width: TechRadarApi['width'];
|
||||
public height: TechRadarApi['height'];
|
||||
public getData: TechRadarApi['getData'];
|
||||
public svgProps: TechRadarApi['svgProps'];
|
||||
public title: TechRadarApi['title'];
|
||||
public subtitle: TechRadarApi['subtitle'];
|
||||
public pageTitle: TechRadarApi['pageTitle'];
|
||||
|
||||
constructor(overrideOptions: TechRadarApi) {
|
||||
const defaultOptions: Partial<TechRadarApi> = {
|
||||
title: 'Tech Radar',
|
||||
subtitle: 'Pick the recommended technologies for your projects',
|
||||
pageTitle: 'Company Radar',
|
||||
};
|
||||
|
||||
const options = { ...defaultOptions, ...overrideOptions };
|
||||
|
||||
this.width = options.width;
|
||||
this.height = options.height;
|
||||
this.getData = options.getData;
|
||||
this.svgProps = options.svgProps;
|
||||
this.title = options.title;
|
||||
this.subtitle = options.subtitle;
|
||||
this.pageTitle = options.pageTitle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { render, waitForElement } from '@testing-library/react';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core';
|
||||
import { withLogCollector } from '@backstage/test-utils-core';
|
||||
import { withLogCollector } from '@backstage/test-utils';
|
||||
|
||||
import GetBBoxPolyfill from '../utils/polyfills/getBBox';
|
||||
import RadarComponent from './RadarComponent';
|
||||
|
||||
@@ -19,11 +19,10 @@ import { render, waitForElement } from '@testing-library/react';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core';
|
||||
import { withLogCollector } from '@backstage/test-utils-core';
|
||||
|
||||
import GetBBoxPolyfill from '../utils/polyfills/getBBox';
|
||||
import { techRadarApiRef, TechRadar } from '../index';
|
||||
import RadarPage from './RadarPage';
|
||||
import { RadarPage } from './RadarPage';
|
||||
import { MockErrorApi, wrapInTestApp } from '@backstage/test-utils';
|
||||
|
||||
describe('RadarPage', () => {
|
||||
beforeAll(() => {
|
||||
@@ -35,24 +34,18 @@ describe('RadarPage', () => {
|
||||
});
|
||||
|
||||
it('should render a progress bar', async () => {
|
||||
const errorApi = { post: () => {} };
|
||||
const techRadarApi = new TechRadar({
|
||||
const techRadarProps = {
|
||||
width: 1200,
|
||||
height: 800,
|
||||
svgProps: { 'data-testid': 'tech-radar-svg' },
|
||||
});
|
||||
};
|
||||
|
||||
const { getByTestId, queryByTestId } = render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[errorApiRef, errorApi],
|
||||
[techRadarApiRef, techRadarApi],
|
||||
])}
|
||||
>
|
||||
<RadarPage />
|
||||
</ApiProvider>
|
||||
</ThemeProvider>,
|
||||
wrapInTestApp(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<RadarPage {...techRadarProps} />
|
||||
</ThemeProvider>,
|
||||
),
|
||||
);
|
||||
|
||||
expect(getByTestId('progress')).toBeInTheDocument();
|
||||
@@ -61,24 +54,18 @@ describe('RadarPage', () => {
|
||||
});
|
||||
|
||||
it('should render a header with a svg', async () => {
|
||||
const errorApi = { post: () => {} };
|
||||
const techRadarApi = new TechRadar({
|
||||
const techRadarProps = {
|
||||
width: 1200,
|
||||
height: 800,
|
||||
svgProps: { 'data-testid': 'tech-radar-svg' },
|
||||
});
|
||||
};
|
||||
|
||||
const { getByText, getByTestId } = render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[errorApiRef, errorApi],
|
||||
[techRadarApiRef, techRadarApi],
|
||||
])}
|
||||
>
|
||||
<RadarPage />
|
||||
</ApiProvider>
|
||||
</ThemeProvider>,
|
||||
wrapInTestApp(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<RadarPage {...techRadarProps} />
|
||||
</ThemeProvider>,
|
||||
),
|
||||
);
|
||||
|
||||
await waitForElement(() => getByTestId('tech-radar-svg'));
|
||||
@@ -90,78 +77,29 @@ describe('RadarPage', () => {
|
||||
});
|
||||
|
||||
it('should call the errorApi if load fails', async () => {
|
||||
const errorApi = { post: jest.fn() };
|
||||
const errorApi = new MockErrorApi({ collect: true });
|
||||
const techRadarLoadFail = () =>
|
||||
Promise.reject(new Error('404 Page Not Found'));
|
||||
const techRadarApi = new TechRadar({
|
||||
const techRadarProps = {
|
||||
width: 1200,
|
||||
height: 800,
|
||||
getData: techRadarLoadFail,
|
||||
svgProps: { 'data-testid': 'tech-radar-svg' },
|
||||
});
|
||||
};
|
||||
|
||||
const { queryByTestId } = render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[errorApiRef, errorApi],
|
||||
[techRadarApiRef, techRadarApi],
|
||||
])}
|
||||
>
|
||||
<RadarPage />
|
||||
<ApiProvider apis={ApiRegistry.with(errorApiRef, errorApi)}>
|
||||
<RadarPage {...techRadarProps} />
|
||||
</ApiProvider>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
await waitForElement(() => !queryByTestId('progress'));
|
||||
|
||||
expect(errorApi.post).toHaveBeenCalledTimes(1);
|
||||
expect(errorApi.post).toHaveBeenCalledWith(new Error('404 Page Not Found'));
|
||||
expect(errorApi.getErrors()).toEqual([
|
||||
{ error: new Error('404 Page Not Found'), context: undefined },
|
||||
]);
|
||||
expect(queryByTestId('tech-radar-svg')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render without errorApiRef', () => {
|
||||
const techRadarApi = new TechRadar({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
});
|
||||
|
||||
expect(
|
||||
withLogCollector(['error'], () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([[techRadarApiRef, techRadarApi]])}
|
||||
>
|
||||
<RadarPage />
|
||||
</ApiProvider>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
}).toThrow();
|
||||
}).error[0],
|
||||
).toMatch(
|
||||
/^Error: Uncaught \[Error: No implementation available for apiRef{core.error}\]/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render without techRadarApiRef', () => {
|
||||
const errorApi = { post: () => {} };
|
||||
|
||||
expect(
|
||||
withLogCollector(['error'], () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApiProvider apis={ApiRegistry.from([[errorApiRef, errorApi]])}>
|
||||
<RadarPage />
|
||||
</ApiProvider>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
}).toThrow();
|
||||
}).error[0],
|
||||
).toMatch(
|
||||
/^Error: Uncaught \[Error: No implementation available for apiRef{plugin.techradar}\]/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,36 +24,46 @@ import {
|
||||
HeaderLabel,
|
||||
SupportButton,
|
||||
pageTheme,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import RadarComponent from '../components/RadarComponent';
|
||||
import { techRadarApiRef, TechRadarApi } from '../api';
|
||||
import { TechRadarComponentProps } from '../api';
|
||||
|
||||
const RadarPage = (): JSX.Element => {
|
||||
const techRadarApi = useApi<TechRadarApi>(techRadarApiRef);
|
||||
|
||||
return (
|
||||
<Page theme={pageTheme.tool}>
|
||||
<Header title={techRadarApi.title} subtitle={techRadarApi.subtitle}>
|
||||
<HeaderLabel label="Owner" value="Spotify" />
|
||||
<HeaderLabel label="Lifecycle" value="Beta" />
|
||||
</Header>
|
||||
<Content>
|
||||
<ContentHeader title={techRadarApi.pageTitle}>
|
||||
<SupportButton>
|
||||
This is used for visualizing the official guidelines of different
|
||||
areas of software development such as languages, frameworks,
|
||||
infrastructure and processes.
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<Grid container spacing={3} direction="row">
|
||||
<Grid item xs={12} sm={6} md={4}>
|
||||
<RadarComponent {...techRadarApi} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
export type TechRadarPageProps = TechRadarComponentProps & {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
pageTitle?: string;
|
||||
};
|
||||
|
||||
export default RadarPage;
|
||||
export const RadarPage = ({
|
||||
title,
|
||||
subtitle,
|
||||
pageTitle,
|
||||
...props
|
||||
}: TechRadarPageProps): JSX.Element => (
|
||||
<Page theme={pageTheme.tool}>
|
||||
<Header title={title} subtitle={subtitle}>
|
||||
<HeaderLabel label="Owner" value="Spotify" />
|
||||
<HeaderLabel label="Lifecycle" value="Beta" />
|
||||
</Header>
|
||||
<Content>
|
||||
<ContentHeader title={pageTitle}>
|
||||
<SupportButton>
|
||||
This is used for visualizing the official guidelines of different
|
||||
areas of software development such as languages, frameworks,
|
||||
infrastructure and processes.
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<Grid container spacing={3} direction="row">
|
||||
<Grid item xs={12} sm={6} md={4}>
|
||||
<RadarComponent {...props} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
|
||||
RadarPage.defaultProps = {
|
||||
title: 'Tech Radar',
|
||||
subtitle: 'Pick the recommended technologies for your projects',
|
||||
pageTitle: 'Company Radar',
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
export { plugin } from './plugin';
|
||||
|
||||
export { RadarPage as Router } from './components/RadarPage';
|
||||
|
||||
/**
|
||||
* The TypeScript API for configuring Tech Radar.
|
||||
*/
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import RadarPage from './components/RadarPage';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'tech-radar',
|
||||
register({ router }) {
|
||||
router.registerRoute('/tech-radar', RadarPage);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -20,13 +20,13 @@ import { TechDocsDevStorageApi } from './api';
|
||||
import { techdocsStorageApiRef } from '../src';
|
||||
|
||||
createDevApp()
|
||||
.registerApiFactory({
|
||||
.registerApi({
|
||||
api: techdocsStorageApiRef,
|
||||
deps: {},
|
||||
factory: () =>
|
||||
new TechDocsDevStorageApi({
|
||||
apiOrigin: 'http://localhost:3000/api',
|
||||
}),
|
||||
implements: techdocsStorageApiRef,
|
||||
})
|
||||
.registerPlugin(plugin)
|
||||
.render();
|
||||
|
||||
@@ -29,7 +29,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
createApiFactory,
|
||||
configApiRef,
|
||||
} from '@backstage/core';
|
||||
import { techdocsStorageApiRef, TechDocsStorageApi } from './api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
path: '',
|
||||
@@ -48,4 +54,14 @@ export const rootCatalogDocsRouteRef = createRouteRef({
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'techdocs',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: techdocsStorageApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) =>
|
||||
new TechDocsStorageApi({
|
||||
apiOrigin: configApi.getString('techdocs.storageUrl'),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user