Merge branch 'master' of github.com:backstage/backstage into blam/isomorphic-git
* 'master' of github.com:backstage/backstage: (45 commits) Speed up some PRs with paths-ignore Remove unnecessary step catalog: export EntitySwitch component and conditions Apply suggestions from code review changeset: add changeset for new catalog composability components catalog: fix EntityLayout TabbedLayout route path matching catalog: gather all mount points beneath EntityLayout Routes catalog: add EntityLayout Change to raw Windows Discord hook Release plugin-cost-insights 0.5.3 catalog: add EntitySwitch catalog: export new CatalogIndexPage and CatalogEntityPage Create dull-seals-march.md core-api: add BackstageRoutes Fix plugin name fixed category lables feat(cost-insights): Make alert url field optional fossa: Add changeset fix(fossa): Bump core packages version to fix failing CI Update 2020-12-22-stability-index.md ...
This commit is contained in:
@@ -1,5 +1,23 @@
|
||||
# example-app
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [19554f6d6]
|
||||
- Updated dependencies [1dc445e89]
|
||||
- Updated dependencies [342270e4d]
|
||||
- Updated dependencies [7d72f9b09]
|
||||
- @backstage/cli@0.4.3
|
||||
- @backstage/plugin-github-actions@0.2.6
|
||||
- @backstage/plugin-scaffolder@0.3.5
|
||||
- @backstage/core@0.4.2
|
||||
- @backstage/test-utils@0.1.6
|
||||
- @backstage/plugin-graphiql@0.2.3
|
||||
- @backstage/plugin-catalog@0.2.8
|
||||
- @backstage/plugin-pagerduty@0.2.4
|
||||
- @backstage/plugin-techdocs@0.5.1
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+10
-10
@@ -1,39 +1,39 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"private": true,
|
||||
"bundled": true,
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.6.0",
|
||||
"@backstage/cli": "^0.4.2",
|
||||
"@backstage/core": "^0.4.1",
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@backstage/core": "^0.4.2",
|
||||
"@backstage/plugin-api-docs": "^0.4.1",
|
||||
"@backstage/plugin-catalog": "^0.2.7",
|
||||
"@backstage/plugin-catalog": "^0.2.8",
|
||||
"@backstage/plugin-catalog-import": "^0.3.2",
|
||||
"@backstage/plugin-circleci": "^0.2.5",
|
||||
"@backstage/plugin-cloudbuild": "^0.2.5",
|
||||
"@backstage/plugin-cost-insights": "^0.5.2",
|
||||
"@backstage/plugin-explore": "^0.2.2",
|
||||
"@backstage/plugin-gcp-projects": "^0.2.2",
|
||||
"@backstage/plugin-github-actions": "^0.2.5",
|
||||
"@backstage/plugin-github-actions": "^0.2.6",
|
||||
"@backstage/plugin-gitops-profiles": "^0.2.2",
|
||||
"@backstage/plugin-graphiql": "^0.2.2",
|
||||
"@backstage/plugin-graphiql": "^0.2.3",
|
||||
"@backstage/plugin-org": "^0.3.2",
|
||||
"@backstage/plugin-jenkins": "^0.3.4",
|
||||
"@backstage/plugin-kubernetes": "^0.3.3",
|
||||
"@backstage/plugin-lighthouse": "^0.2.6",
|
||||
"@backstage/plugin-newrelic": "^0.2.2",
|
||||
"@backstage/plugin-pagerduty": "0.2.3",
|
||||
"@backstage/plugin-pagerduty": "0.2.4",
|
||||
"@backstage/plugin-register-component": "^0.2.5",
|
||||
"@backstage/plugin-rollbar": "^0.2.7",
|
||||
"@backstage/plugin-scaffolder": "^0.3.4",
|
||||
"@backstage/plugin-scaffolder": "^0.3.5",
|
||||
"@backstage/plugin-sentry": "^0.3.1",
|
||||
"@backstage/plugin-search": "^0.2.4",
|
||||
"@backstage/plugin-tech-radar": "^0.3.1",
|
||||
"@backstage/plugin-techdocs": "^0.5.0",
|
||||
"@backstage/plugin-techdocs": "^0.5.1",
|
||||
"@backstage/plugin-user-settings": "^0.2.3",
|
||||
"@backstage/plugin-welcome": "^0.2.3",
|
||||
"@backstage/test-utils": "^0.1.5",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# example-backend
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 7cfcd58ee: use node 14 for backend Dockerfile
|
||||
- Updated dependencies [19554f6d6]
|
||||
- Updated dependencies [33a82a713]
|
||||
- Updated dependencies [5de26b9a6]
|
||||
- Updated dependencies [30d6c78fb]
|
||||
- Updated dependencies [5084e5039]
|
||||
- Updated dependencies [a8573e53b]
|
||||
- Updated dependencies [aed8f7f12]
|
||||
- @backstage/plugin-scaffolder-backend@0.3.6
|
||||
- @backstage/plugin-catalog-backend@0.5.1
|
||||
- @backstage/plugin-techdocs-backend@0.5.0
|
||||
- example-app@0.2.8
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -32,18 +32,18 @@
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/plugin-app-backend": "^0.3.3",
|
||||
"@backstage/plugin-auth-backend": "^0.2.7",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.0",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.1",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.4",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.2.3",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.3",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.5",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.3.5",
|
||||
"@backstage/plugin-techdocs-backend": "^0.4.0",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.3.6",
|
||||
"@backstage/plugin-techdocs-backend": "^0.5.0",
|
||||
"@gitbeaker/node": "^25.2.0",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"azure-devops-node-api": "^10.1.1",
|
||||
"dockerode": "^3.2.1",
|
||||
"example-app": "^0.2.7",
|
||||
"example-app": "^0.2.8",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"knex": "^0.21.6",
|
||||
@@ -53,7 +53,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.2",
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@types/dockerode": "^3.2.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
|
||||
@@ -15,12 +15,8 @@
|
||||
*/
|
||||
import {
|
||||
createRouter,
|
||||
DirectoryPreparer,
|
||||
Preparers,
|
||||
Generators,
|
||||
TechdocsGenerator,
|
||||
CommonGitPreparer,
|
||||
UrlPreparer,
|
||||
Publisher,
|
||||
} from '@backstage/plugin-techdocs-backend';
|
||||
import { PluginEnvironment } from '../types';
|
||||
@@ -33,30 +29,25 @@ export default async function createPlugin({
|
||||
reader,
|
||||
}: PluginEnvironment) {
|
||||
// Preparers are responsible for fetching source files for documentation.
|
||||
const preparers = new Preparers();
|
||||
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
preparers.register('dir', directoryPreparer);
|
||||
|
||||
const commonGitPreparer = new CommonGitPreparer(logger);
|
||||
preparers.register('github', commonGitPreparer);
|
||||
preparers.register('gitlab', commonGitPreparer);
|
||||
preparers.register('azure/api', commonGitPreparer);
|
||||
|
||||
const urlPreparer = new UrlPreparer(reader, logger);
|
||||
preparers.register('url', urlPreparer);
|
||||
const preparers = await Preparers.fromConfig(config, {
|
||||
logger,
|
||||
reader,
|
||||
});
|
||||
|
||||
// Generators are used for generating documentation sites.
|
||||
const generators = new Generators();
|
||||
const techdocsGenerator = new TechdocsGenerator(logger, config);
|
||||
generators.register('techdocs', techdocsGenerator);
|
||||
const generators = await Generators.fromConfig(config, {
|
||||
logger,
|
||||
});
|
||||
|
||||
// Publishers are used for
|
||||
// Publisher is used for
|
||||
// 1. Publishing generated files to storage
|
||||
// 2. Fetching files from storage and passing them to TechDocs frontend.
|
||||
const publisher = Publisher.fromConfig(config, logger, discovery);
|
||||
const publisher = await Publisher.fromConfig(config, {
|
||||
logger,
|
||||
discovery,
|
||||
});
|
||||
|
||||
// Docker client used by the generators.
|
||||
// Docker client (conditionally) used by the generators, based on techdocs.generators config.
|
||||
const dockerClient = new Docker();
|
||||
|
||||
return await createRouter({
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @backstage/cli
|
||||
|
||||
## 0.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 19554f6d6: Added Github Actions for Create React App, and allow better imports of files inside a module when they're exposed using `files` in `package.json`
|
||||
- 7d72f9b09: Fix for `app.listen.host` configuration not properly overriding listening host.
|
||||
|
||||
## 0.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.4.2",
|
||||
"version": "0.4.3",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -113,9 +113,9 @@
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.4.1",
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/core": "^0.4.1",
|
||||
"@backstage/core": "^0.4.2",
|
||||
"@backstage/dev-utils": "^0.1.6",
|
||||
"@backstage/test-utils": "^0.1.5",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@types/diff": "^4.0.2",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/core-api
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d681db2b5: Fix for GitHub and SAML auth not properly updating session state when already logged in.
|
||||
- 1dc445e89: Introduce new plugin extension API
|
||||
- Updated dependencies [1dc445e89]
|
||||
- @backstage/test-utils@0.1.6
|
||||
|
||||
## 0.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-api",
|
||||
"description": "Internal Core API used by Backstage plugins and apps",
|
||||
"version": "0.2.6",
|
||||
"version": "0.2.7",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/test-utils": "^0.1.5",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
@@ -43,7 +43,7 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.2",
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@backstage/test-utils-core": "^0.1.1",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 { ReactNode, Children, isValidElement, Fragment } from 'react';
|
||||
import { useRoutes } from 'react-router-dom';
|
||||
|
||||
type RouteObject = {
|
||||
path: string;
|
||||
element: JSX.Element;
|
||||
children?: RouteObject[];
|
||||
};
|
||||
|
||||
// Similar to the same function from react-router, this collects routes from the
|
||||
// children, but only the first level of routes
|
||||
function createRoutesFromChildren(children: ReactNode): RouteObject[] {
|
||||
return Children.toArray(children)
|
||||
.flatMap(child => {
|
||||
if (!isValidElement(child)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { children } = child.props;
|
||||
|
||||
if (child.type === Fragment) {
|
||||
return createRoutesFromChildren(children);
|
||||
}
|
||||
|
||||
let path = child.props.path as string | undefined;
|
||||
|
||||
// TODO(Rugvip): Work around plugins registering empty paths, remove once deprecated routes are gone
|
||||
if (path === '') {
|
||||
return [];
|
||||
}
|
||||
path = path?.replace(/\/\*$/, '') ?? '/';
|
||||
|
||||
return [
|
||||
{
|
||||
path,
|
||||
element: child,
|
||||
children: children && [
|
||||
{
|
||||
path: '/*',
|
||||
element: children,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
})
|
||||
.sort((a, b) => b.path.localeCompare(a.path))
|
||||
.map(obj => {
|
||||
obj.path = obj.path === '/' ? '/' : `${obj.path}/*`;
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
type BackstageRoutesProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const BackstageRoutes = (
|
||||
props: BackstageRoutesProps,
|
||||
): JSX.Element | null => {
|
||||
const routes = createRoutesFromChildren(props.children);
|
||||
return useRoutes(routes);
|
||||
};
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { routePathCollector, routeParentCollector } from './collectors';
|
||||
import {
|
||||
routePathCollector,
|
||||
routeParentCollector,
|
||||
routeObjectCollector,
|
||||
} from './collectors';
|
||||
|
||||
import {
|
||||
traverseElementTree,
|
||||
@@ -24,21 +28,22 @@ import {
|
||||
} from '../extensions/traversal';
|
||||
import { createRouteRef } from './RouteRef';
|
||||
import { createPlugin } from '../plugin';
|
||||
import { createRoutableExtension } from '../extensions';
|
||||
import { attachComponentData, createRoutableExtension } from '../extensions';
|
||||
import { MemoryRouter, Routes, Route } from 'react-router-dom';
|
||||
import { RouteRef } from './types';
|
||||
|
||||
const mockConfig = () => ({ path: '/foo', title: 'Foo' });
|
||||
const MockComponent = ({ children }: PropsWithChildren<{ path?: string }>) => (
|
||||
<>{children}</>
|
||||
);
|
||||
|
||||
const plugin = createPlugin({ id: 'my-plugin' });
|
||||
|
||||
const ref1 = createRouteRef(mockConfig());
|
||||
const ref2 = createRouteRef(mockConfig());
|
||||
const ref3 = createRouteRef(mockConfig());
|
||||
const ref4 = createRouteRef(mockConfig());
|
||||
const ref5 = createRouteRef(mockConfig());
|
||||
const ref1 = createRouteRef({ path: '/foo1', title: 'Foo' });
|
||||
const ref2 = createRouteRef({ path: '/foo2', title: 'Foo' });
|
||||
const ref3 = createRouteRef({ path: '/foo3', title: 'Foo' });
|
||||
const ref4 = createRouteRef({ path: '/foo4', title: 'Foo' });
|
||||
const ref5 = createRouteRef({ path: '/foo5', title: 'Foo' });
|
||||
const refOrder = [ref1, ref2, ref3, ref4, ref5];
|
||||
|
||||
const Extension1 = plugin.provide(
|
||||
createRoutableExtension({
|
||||
@@ -71,6 +76,30 @@ const Extension5 = plugin.provide(
|
||||
}),
|
||||
);
|
||||
|
||||
const AggregationComponent = ({
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
path: string;
|
||||
}>) => <>{children}</>;
|
||||
|
||||
attachComponentData(AggregationComponent, 'core.gatherMountPoints', true);
|
||||
|
||||
function sortedEntries<T>(map: Map<RouteRef, T>): [RouteRef, T][] {
|
||||
return Array.from(map).sort(
|
||||
([a], [b]) => refOrder.indexOf(a) - refOrder.indexOf(b),
|
||||
);
|
||||
}
|
||||
|
||||
function routeObj(path: string, refs: RouteRef[], children: any[] = []) {
|
||||
return {
|
||||
path: path,
|
||||
caseSensitive: false,
|
||||
element: null,
|
||||
routeRefs: new Set(refs),
|
||||
children: children,
|
||||
};
|
||||
}
|
||||
|
||||
describe('discovery', () => {
|
||||
it('should collect routes', () => {
|
||||
const list = [
|
||||
@@ -109,33 +138,40 @@ describe('discovery', () => {
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const { routes, routeParents } = traverseElementTree({
|
||||
const { routes, routeParents, routeObjects } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routes: routePathCollector,
|
||||
routeParents: routeParentCollector,
|
||||
routeObjects: routeObjectCollector,
|
||||
},
|
||||
});
|
||||
expect(routes).toEqual(
|
||||
new Map([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/bar/:id'],
|
||||
[ref3, '/baz'],
|
||||
[ref4, '/divsoup'],
|
||||
[ref5, '/blop'],
|
||||
]),
|
||||
);
|
||||
|
||||
expect(routeParents).toEqual(
|
||||
new Map([
|
||||
[ref1, undefined],
|
||||
[ref2, ref1],
|
||||
[ref3, ref2],
|
||||
[ref4, undefined],
|
||||
[ref5, ref1],
|
||||
]),
|
||||
);
|
||||
expect(sortedEntries(routes)).toEqual([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/bar/:id'],
|
||||
[ref3, '/baz'],
|
||||
[ref4, '/divsoup'],
|
||||
[ref5, '/blop'],
|
||||
]);
|
||||
expect(sortedEntries(routeParents)).toEqual([
|
||||
[ref1, undefined],
|
||||
[ref2, ref1],
|
||||
[ref3, ref2],
|
||||
[ref4, undefined],
|
||||
[ref5, ref1],
|
||||
]);
|
||||
expect(routeObjects).toEqual([
|
||||
routeObj(
|
||||
'/foo',
|
||||
[ref1],
|
||||
[
|
||||
routeObj('/bar/:id', [ref2], [routeObj('/baz', [ref3])]),
|
||||
routeObj('/blop', [ref5]),
|
||||
],
|
||||
),
|
||||
routeObj('/divsoup', [ref4]),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle all react router Route patterns', () => {
|
||||
@@ -168,24 +204,154 @@ describe('discovery', () => {
|
||||
routeParents: routeParentCollector,
|
||||
},
|
||||
});
|
||||
expect(routes).toEqual(
|
||||
new Map([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/bar/:id'],
|
||||
[ref3, '/baz'],
|
||||
[ref4, '/divsoup'],
|
||||
[ref5, '/blop'],
|
||||
]),
|
||||
expect(sortedEntries(routes)).toEqual([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/bar/:id'],
|
||||
[ref3, '/baz'],
|
||||
[ref4, '/divsoup'],
|
||||
[ref5, '/blop'],
|
||||
]);
|
||||
expect(sortedEntries(routeParents)).toEqual([
|
||||
[ref1, undefined],
|
||||
[ref2, ref1],
|
||||
[ref3, undefined],
|
||||
[ref4, ref3],
|
||||
[ref5, ref3],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should use the route aggregator key to bind child routes to the same path', () => {
|
||||
const root = (
|
||||
<MemoryRouter>
|
||||
<Routes>
|
||||
<AggregationComponent path="/foo">
|
||||
<Extension1 />
|
||||
<div>
|
||||
<Extension2 />
|
||||
</div>
|
||||
HELLO
|
||||
</AggregationComponent>
|
||||
<Extension3 path="/bar">
|
||||
<AggregationComponent path="/baz">
|
||||
<Extension4>
|
||||
<Extension5 />
|
||||
</Extension4>
|
||||
</AggregationComponent>
|
||||
</Extension3>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(routeParents).toEqual(
|
||||
new Map([
|
||||
[ref1, undefined],
|
||||
[ref2, ref1],
|
||||
[ref3, undefined],
|
||||
[ref4, ref3],
|
||||
[ref5, ref3],
|
||||
]),
|
||||
|
||||
const { routes, routeParents, routeObjects } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routes: routePathCollector,
|
||||
routeParents: routeParentCollector,
|
||||
routeObjects: routeObjectCollector,
|
||||
},
|
||||
});
|
||||
expect(sortedEntries(routes)).toEqual([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/foo'],
|
||||
[ref3, '/bar'],
|
||||
[ref4, '/baz'],
|
||||
[ref5, '/baz'],
|
||||
]);
|
||||
expect(sortedEntries(routeParents)).toEqual([
|
||||
[ref1, undefined],
|
||||
[ref2, undefined],
|
||||
[ref3, undefined],
|
||||
[ref4, ref3],
|
||||
[ref5, ref3],
|
||||
]);
|
||||
expect(routeObjects).toEqual([
|
||||
routeObj('/foo', [ref1, ref2]),
|
||||
routeObj('/bar', [ref3], [routeObj('/baz', [ref4, ref5])]),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should use the route aggregator but stop when encountering explicit path', () => {
|
||||
const root = (
|
||||
<MemoryRouter>
|
||||
<Routes>
|
||||
<Extension1 path="/foo">
|
||||
<AggregationComponent path="/bar">
|
||||
<Extension2>
|
||||
<Extension3 path="/baz">
|
||||
<Extension4 path="/blop" />
|
||||
</Extension3>
|
||||
<Extension5 />
|
||||
</Extension2>
|
||||
</AggregationComponent>
|
||||
</Extension1>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const { routes, routeParents, routeObjects } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routes: routePathCollector,
|
||||
routeParents: routeParentCollector,
|
||||
routeObjects: routeObjectCollector,
|
||||
},
|
||||
});
|
||||
expect(sortedEntries(routes)).toEqual([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/bar'],
|
||||
[ref3, '/baz'],
|
||||
[ref4, '/blop'],
|
||||
[ref5, '/bar'],
|
||||
]);
|
||||
expect(sortedEntries(routeParents)).toEqual([
|
||||
[ref1, undefined],
|
||||
[ref2, ref1],
|
||||
[ref3, ref1],
|
||||
[ref4, ref3],
|
||||
[ref5, ref1],
|
||||
]);
|
||||
expect(routeObjects).toEqual([
|
||||
routeObj(
|
||||
'/foo',
|
||||
[ref1],
|
||||
[
|
||||
routeObj(
|
||||
'/bar',
|
||||
[ref2, ref5],
|
||||
[routeObj('/baz', [ref3], [routeObj('/blop', [ref4])])],
|
||||
),
|
||||
],
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should stop gathering mount points after encountering explicit path', () => {
|
||||
const root = (
|
||||
<MemoryRouter>
|
||||
<Routes>
|
||||
<Extension1 path="/foo">
|
||||
<AggregationComponent path="/bar">
|
||||
<Extension2 path="/baz">
|
||||
<Extension3 />
|
||||
</Extension2>
|
||||
</AggregationComponent>
|
||||
</Extension1>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routes: routePathCollector,
|
||||
routeParents: routeParentCollector,
|
||||
},
|
||||
});
|
||||
}).toThrow('Mounted routable extension must have a path');
|
||||
});
|
||||
|
||||
it('should not visit the same element twice', () => {
|
||||
|
||||
@@ -32,25 +32,48 @@ function getMountPoint(node: ReactElement): RouteRef | undefined {
|
||||
|
||||
export const routePathCollector = createCollector(
|
||||
() => new Map<RouteRef, string>(),
|
||||
(acc, node, parent) => {
|
||||
(acc, node, parent, ctxPath: string | undefined) => {
|
||||
// The context path is used during mount point gathering to assign the same path
|
||||
// to all discovered mount points
|
||||
let currentCtxPath = ctxPath;
|
||||
|
||||
if (parent?.props.element === node) {
|
||||
return;
|
||||
return currentCtxPath;
|
||||
}
|
||||
|
||||
// Start gathering mount points when we encounter a mount point gathering flag
|
||||
if (getComponentData<boolean>(node, 'core.gatherMountPoints')) {
|
||||
const path: string | undefined = node.props?.path;
|
||||
if (!path) {
|
||||
throw new Error('Mount point gatherer must have a path');
|
||||
}
|
||||
currentCtxPath = path;
|
||||
}
|
||||
|
||||
const routeRef = getMountPoint(node);
|
||||
if (routeRef) {
|
||||
const path: string | undefined = node.props?.path;
|
||||
let path: string | undefined = node.props?.path;
|
||||
// If we're gathering mount points we use the context path as out path, unless
|
||||
// the element has its own path, in which case we use that instead and stop gathering
|
||||
if (currentCtxPath) {
|
||||
if (path) {
|
||||
currentCtxPath = undefined;
|
||||
} else {
|
||||
path = currentCtxPath;
|
||||
}
|
||||
}
|
||||
if (!path) {
|
||||
throw new Error('Mounted routable extension must have a path');
|
||||
}
|
||||
acc.set(routeRef, path);
|
||||
}
|
||||
return currentCtxPath;
|
||||
},
|
||||
);
|
||||
|
||||
export const routeParentCollector = createCollector(
|
||||
() => new Map<RouteRef, RouteRef | undefined>(),
|
||||
(acc, node, parent, parentRouteRef?: RouteRef) => {
|
||||
(acc, node, parent, parentRouteRef?: RouteRef | { sticky: RouteRef }) => {
|
||||
if (parent?.props.element === node) {
|
||||
return parentRouteRef;
|
||||
}
|
||||
@@ -59,8 +82,27 @@ export const routeParentCollector = createCollector(
|
||||
|
||||
const routeRef = getMountPoint(node);
|
||||
if (routeRef) {
|
||||
acc.set(routeRef, parentRouteRef);
|
||||
nextParent = routeRef;
|
||||
// "sticky" route ref is when we've encountered a mount point gatherer, and we want a
|
||||
// mount points beneath it to have the same parent, regardless of internal structure
|
||||
if (parentRouteRef && 'sticky' in parentRouteRef) {
|
||||
acc.set(routeRef, parentRouteRef.sticky);
|
||||
|
||||
// When we encounter a mount point with an explicit path, we stop gathering
|
||||
// mount points withing the children and remove the sticky state
|
||||
if (node.props?.path) {
|
||||
nextParent = routeRef;
|
||||
} else {
|
||||
nextParent = parentRouteRef;
|
||||
}
|
||||
} else {
|
||||
acc.set(routeRef, parentRouteRef);
|
||||
nextParent = routeRef;
|
||||
}
|
||||
}
|
||||
|
||||
// Mount point gatherers are marked as "sticky"
|
||||
if (getComponentData<boolean>(node, 'core.gatherMountPoints')) {
|
||||
return { sticky: nextParent };
|
||||
}
|
||||
|
||||
return nextParent;
|
||||
@@ -69,9 +111,10 @@ export const routeParentCollector = createCollector(
|
||||
|
||||
export const routeObjectCollector = createCollector(
|
||||
() => Array<BackstageRouteObject>(),
|
||||
(acc, node, parent, parentChildArr: BackstageRouteObject[] = acc) => {
|
||||
(acc, node, parent, parentObj: BackstageRouteObject | undefined) => {
|
||||
const parentChildren = parentObj?.children ?? acc;
|
||||
if (parent?.props.element === node) {
|
||||
return parentChildArr;
|
||||
return parentObj;
|
||||
}
|
||||
|
||||
const path: string | undefined = node.props?.path;
|
||||
@@ -79,20 +122,40 @@ export const routeObjectCollector = createCollector(
|
||||
|
||||
const routeRef = getMountPoint(node);
|
||||
if (routeRef) {
|
||||
const children: BackstageRouteObject[] = [];
|
||||
if (!path) {
|
||||
throw new Error(`No path found for mount point ${routeRef}`);
|
||||
if (path) {
|
||||
const newObject: BackstageRouteObject = {
|
||||
caseSensitive,
|
||||
path,
|
||||
element: null,
|
||||
routeRefs: new Set([routeRef]),
|
||||
children: [],
|
||||
};
|
||||
parentChildren.push(newObject);
|
||||
return newObject;
|
||||
}
|
||||
parentChildArr.push({
|
||||
|
||||
parentObj?.routeRefs.add(routeRef);
|
||||
}
|
||||
|
||||
const isGatherer = getComponentData<boolean>(
|
||||
node,
|
||||
'core.gatherMountPoints',
|
||||
);
|
||||
if (isGatherer) {
|
||||
if (!path) {
|
||||
throw new Error('Mount point gatherer must have a path');
|
||||
}
|
||||
const newObject: BackstageRouteObject = {
|
||||
caseSensitive,
|
||||
path,
|
||||
element: null,
|
||||
routeRef,
|
||||
children,
|
||||
});
|
||||
return children;
|
||||
routeRefs: new Set(),
|
||||
children: [],
|
||||
};
|
||||
parentChildren.push(newObject);
|
||||
return newObject;
|
||||
}
|
||||
|
||||
return parentChildArr;
|
||||
return parentObj;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -60,8 +60,8 @@ class RouteResolver {
|
||||
currentRouteRef;
|
||||
currentRouteRef = this.routeParents.get(currentRouteRef)
|
||||
) {
|
||||
matchIndex = match.findIndex(
|
||||
m => (m.route as BackstageRouteObject).routeRef === currentRouteRef,
|
||||
matchIndex = match.findIndex(m =>
|
||||
(m.route as BackstageRouteObject).routeRefs.has(currentRouteRef!),
|
||||
);
|
||||
if (matchIndex !== -1) {
|
||||
break;
|
||||
|
||||
@@ -21,5 +21,6 @@ export type {
|
||||
ConcreteRoute,
|
||||
MutableRouteRef,
|
||||
} from './types';
|
||||
export { BackstageRoutes } from './BackstageRoutes';
|
||||
export { createRouteRef } from './RouteRef';
|
||||
export { useRouteRef } from './hooks';
|
||||
|
||||
@@ -62,5 +62,5 @@ export interface BackstageRouteObject {
|
||||
children?: BackstageRouteObject[];
|
||||
element: React.ReactNode;
|
||||
path: string;
|
||||
routeRef: AnyRouteRef;
|
||||
routeRefs: Set<AnyRouteRef>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @backstage/core
|
||||
|
||||
## 0.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1dc445e89: Update to use new plugin extension API
|
||||
- 342270e4d: Create AboutCard in core and use it in pagerduty and catalog plugin
|
||||
- Updated dependencies [d681db2b5]
|
||||
- Updated dependencies [1dc445e89]
|
||||
- @backstage/core-api@0.2.7
|
||||
|
||||
## 0.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core",
|
||||
"description": "Core API used by Backstage plugins and apps",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/core-api": "^0.2.6",
|
||||
"@backstage/core-api": "^0.2.7",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
@@ -65,8 +65,8 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.2",
|
||||
"@backstage/test-utils": "^0.1.5",
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@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",
|
||||
|
||||
@@ -1,5 +1,48 @@
|
||||
# @backstage/create-app
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 0101c7a16: Add search plugin to default template for CLI created apps
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a8573e53b: techdocs-backend: Simplified file, removing individual preparers and generators.
|
||||
techdocs-backend: UrlReader is now available to use in preparers.
|
||||
|
||||
In your Backstage app, `packages/backend/plugins/techdocs.ts` file has now been simplified,
|
||||
to remove registering individual preparers and generators.
|
||||
|
||||
Please update the file when upgrading the version of `@backstage/plugin-techdocs-backend` package.
|
||||
|
||||
```typescript
|
||||
const preparers = await Preparers.fromConfig(config, {
|
||||
logger,
|
||||
reader,
|
||||
});
|
||||
|
||||
const generators = await Generators.fromConfig(config, {
|
||||
logger,
|
||||
});
|
||||
|
||||
const publisher = await Publisher.fromConfig(config, {
|
||||
logger,
|
||||
discovery,
|
||||
});
|
||||
```
|
||||
|
||||
You should be able to remove unnecessary imports, and just do
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createRouter,
|
||||
Preparers,
|
||||
Generators,
|
||||
Publisher,
|
||||
} from '@backstage/plugin-techdocs-backend';
|
||||
```
|
||||
|
||||
## 0.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/create-app",
|
||||
"description": "Create app package for Backstage",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -39,29 +39,29 @@
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.4.1",
|
||||
"@backstage/catalog-model": "^0.6.0",
|
||||
"@backstage/cli": "^0.4.2",
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/core": "^0.4.1",
|
||||
"@backstage/core": "^0.4.2",
|
||||
"@backstage/plugin-api-docs": "^0.4.1",
|
||||
"@backstage/plugin-app-backend": "^0.3.3",
|
||||
"@backstage/plugin-auth-backend": "^0.2.7",
|
||||
"@backstage/plugin-catalog": "^0.2.7",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.0",
|
||||
"@backstage/plugin-catalog": "^0.2.8",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.1",
|
||||
"@backstage/plugin-circleci": "^0.2.5",
|
||||
"@backstage/plugin-explore": "^0.2.2",
|
||||
"@backstage/plugin-github-actions": "^0.2.5",
|
||||
"@backstage/plugin-github-actions": "^0.2.6",
|
||||
"@backstage/plugin-lighthouse": "^0.2.6",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.3",
|
||||
"@backstage/plugin-register-component": "^0.2.5",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.5",
|
||||
"@backstage/plugin-scaffolder": "^0.3.4",
|
||||
"@backstage/plugin-scaffolder": "^0.3.5",
|
||||
"@backstage/plugin-search": "^0.2.4",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.3.5",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.3.6",
|
||||
"@backstage/plugin-tech-radar": "^0.3.1",
|
||||
"@backstage/plugin-techdocs": "^0.5.0",
|
||||
"@backstage/plugin-techdocs-backend": "^0.4.0",
|
||||
"@backstage/plugin-techdocs": "^0.5.1",
|
||||
"@backstage/plugin-techdocs-backend": "^0.5.0",
|
||||
"@backstage/plugin-user-settings": "^0.2.3",
|
||||
"@backstage/test-utils": "^0.1.5",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"@types/inquirer": "^7.3.1",
|
||||
|
||||
+18
-15
@@ -1,10 +1,7 @@
|
||||
import {
|
||||
createRouter,
|
||||
DirectoryPreparer,
|
||||
Preparers,
|
||||
Generators,
|
||||
TechdocsGenerator,
|
||||
CommonGitPreparer,
|
||||
Publisher,
|
||||
} from '@backstage/plugin-techdocs-backend';
|
||||
import { PluginEnvironment } from '../types';
|
||||
@@ -14,22 +11,28 @@ export default async function createPlugin({
|
||||
logger,
|
||||
config,
|
||||
discovery,
|
||||
reader,
|
||||
}: PluginEnvironment) {
|
||||
const generators = new Generators();
|
||||
const techdocsGenerator = new TechdocsGenerator(logger, config);
|
||||
// Preparers are responsible for fetching source files for documentation.
|
||||
const preparers = await Preparers.fromConfig(config, {
|
||||
logger,
|
||||
reader,
|
||||
});
|
||||
|
||||
generators.register('techdocs', techdocsGenerator);
|
||||
// Generators are used for generating documentation sites.
|
||||
const generators = await Generators.fromConfig(config, {
|
||||
logger,
|
||||
});
|
||||
|
||||
const preparers = new Preparers();
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
const commonGitPreparer = new CommonGitPreparer(logger);
|
||||
|
||||
preparers.register('dir', directoryPreparer);
|
||||
preparers.register('github', commonGitPreparer);
|
||||
preparers.register('gitlab', commonGitPreparer);
|
||||
|
||||
const publisher = Publisher.fromConfig(config, logger, discovery);
|
||||
// Publisher is used for
|
||||
// 1. Publishing generated files to storage
|
||||
// 2. Fetching files from storage and passing them to TechDocs frontend.
|
||||
const publisher = await Publisher.fromConfig(config, {
|
||||
logger,
|
||||
discovery,
|
||||
});
|
||||
|
||||
// Docker client (conditionally) used by the generators, based on techdocs.generators config.
|
||||
const dockerClient = new Docker();
|
||||
|
||||
return await createRouter({
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
# @backstage/techdocs-common
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- a8573e53b: techdocs-backend: Simplified file, removing individual preparers and generators.
|
||||
techdocs-backend: UrlReader is now available to use in preparers.
|
||||
|
||||
In your Backstage app, `packages/backend/plugins/techdocs.ts` file has now been simplified,
|
||||
to remove registering individual preparers and generators.
|
||||
|
||||
Please update the file when upgrading the version of `@backstage/plugin-techdocs-backend` package.
|
||||
|
||||
```typescript
|
||||
const preparers = await Preparers.fromConfig(config, {
|
||||
logger,
|
||||
reader,
|
||||
});
|
||||
|
||||
const generators = await Generators.fromConfig(config, {
|
||||
logger,
|
||||
});
|
||||
|
||||
const publisher = await Publisher.fromConfig(config, {
|
||||
logger,
|
||||
discovery,
|
||||
});
|
||||
```
|
||||
|
||||
You should be able to remove unnecessary imports, and just do
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createRouter,
|
||||
Preparers,
|
||||
Generators,
|
||||
Publisher,
|
||||
} from '@backstage/plugin-techdocs-backend';
|
||||
```
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/techdocs-common",
|
||||
"description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -54,7 +54,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.2",
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@types/fs-extra": "^9.0.5",
|
||||
"@types/git-url-parse": "^9.0.0",
|
||||
"@types/js-yaml": "^3.12.5",
|
||||
|
||||
@@ -14,18 +14,32 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Logger } from 'winston';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { TechdocsGenerator } from '.';
|
||||
import {
|
||||
GeneratorBase,
|
||||
SupportedGeneratorKey,
|
||||
GeneratorBuilder,
|
||||
} from './types';
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { getGeneratorKey } from './helpers';
|
||||
|
||||
export class Generators implements GeneratorBuilder {
|
||||
private generatorMap = new Map<SupportedGeneratorKey, GeneratorBase>();
|
||||
|
||||
static async fromConfig(
|
||||
config: Config,
|
||||
{ logger }: { logger: Logger },
|
||||
): Promise<GeneratorBuilder> {
|
||||
const generators = new Generators();
|
||||
|
||||
const techdocsGenerator = new TechdocsGenerator(logger, config);
|
||||
generators.register('techdocs', techdocsGenerator);
|
||||
|
||||
return generators;
|
||||
}
|
||||
|
||||
register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase) {
|
||||
this.generatorMap.set(generatorKey, generator);
|
||||
}
|
||||
|
||||
@@ -13,14 +13,45 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PreparerBase, RemoteProtocol, PreparerBuilder } from './types';
|
||||
import { Logger } from 'winston';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { DirectoryPreparer, CommonGitPreparer, UrlPreparer } from '.';
|
||||
import { PreparerBase, RemoteProtocol, PreparerBuilder } from './types';
|
||||
import { parseReferenceAnnotation } from '../../helpers';
|
||||
|
||||
type factoryOptions = {
|
||||
logger: Logger;
|
||||
reader: UrlReader;
|
||||
};
|
||||
|
||||
export class Preparers implements PreparerBuilder {
|
||||
private preparerMap = new Map<RemoteProtocol, PreparerBase>();
|
||||
|
||||
static async fromConfig(
|
||||
// @ts-ignore
|
||||
// Config not used now, but will be used in urlPreparer when it starts using
|
||||
// @backstage/integration to get the tokens for providers.
|
||||
config: Config,
|
||||
{ logger, reader }: factoryOptions,
|
||||
): Promise<PreparerBuilder> {
|
||||
const preparers = new Preparers();
|
||||
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
preparers.register('dir', directoryPreparer);
|
||||
|
||||
const commonGitPreparer = new CommonGitPreparer(logger);
|
||||
preparers.register('github', commonGitPreparer);
|
||||
preparers.register('gitlab', commonGitPreparer);
|
||||
preparers.register('azure/api', commonGitPreparer);
|
||||
|
||||
const urlPreparer = new UrlPreparer(reader, logger);
|
||||
preparers.register('url', urlPreparer);
|
||||
|
||||
return preparers;
|
||||
}
|
||||
|
||||
register(protocol: RemoteProtocol, preparer: PreparerBase) {
|
||||
this.preparerMap.set(protocol, preparer);
|
||||
}
|
||||
|
||||
@@ -23,24 +23,27 @@ import { LocalPublish } from './local';
|
||||
import { GoogleGCSPublish } from './googleStorage';
|
||||
|
||||
const logger = getVoidLogger();
|
||||
const testDiscovery: jest.Mocked<PluginEndpointDiscovery> = {
|
||||
const discovery: jest.Mocked<PluginEndpointDiscovery> = {
|
||||
getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7000'),
|
||||
getExternalBaseUrl: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Publisher', () => {
|
||||
it('should create local publisher by default', () => {
|
||||
it('should create local publisher by default', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7000',
|
||||
},
|
||||
});
|
||||
|
||||
const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery);
|
||||
const publisher = await Publisher.fromConfig(mockConfig, {
|
||||
logger,
|
||||
discovery,
|
||||
});
|
||||
expect(publisher).toBeInstanceOf(LocalPublish);
|
||||
});
|
||||
|
||||
it('should create local publisher from config', () => {
|
||||
it('should create local publisher from config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7000',
|
||||
@@ -50,11 +53,14 @@ describe('Publisher', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery);
|
||||
const publisher = await Publisher.fromConfig(mockConfig, {
|
||||
logger,
|
||||
discovery,
|
||||
});
|
||||
expect(publisher).toBeInstanceOf(LocalPublish);
|
||||
});
|
||||
|
||||
it('should create google gcs publisher from config', () => {
|
||||
it('should create google gcs publisher from config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7000',
|
||||
@@ -69,7 +75,10 @@ describe('Publisher', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery);
|
||||
const publisher = await Publisher.fromConfig(mockConfig, {
|
||||
logger,
|
||||
discovery,
|
||||
});
|
||||
expect(publisher).toBeInstanceOf(GoogleGCSPublish);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,16 +21,20 @@ import { PublisherType, PublisherBase } from './types';
|
||||
import { LocalPublish } from './local';
|
||||
import { GoogleGCSPublish } from './googleStorage';
|
||||
|
||||
type factoryOptions = {
|
||||
logger: Logger;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory class to create a TechDocs publisher based on defined publisher type in app config.
|
||||
* Uses `techdocs.publisher.type`.
|
||||
*/
|
||||
export class Publisher {
|
||||
static fromConfig(
|
||||
static async fromConfig(
|
||||
config: Config,
|
||||
logger: Logger,
|
||||
discovery: PluginEndpointDiscovery,
|
||||
): PublisherBase {
|
||||
{ logger, discovery }: factoryOptions,
|
||||
): Promise<PublisherBase> {
|
||||
const publisherType = (config.getOptionalString(
|
||||
'techdocs.publisher.type',
|
||||
) ?? 'local') as PublisherType;
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/test-utils
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1dc445e89: Update to use new plugin extension API
|
||||
- Updated dependencies [d681db2b5]
|
||||
- Updated dependencies [1dc445e89]
|
||||
- @backstage/core-api@0.2.7
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/test-utils",
|
||||
"description": "Utilities to test Backstage plugins and apps.",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,7 +29,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-api": "^0.2.5",
|
||||
"@backstage/core-api": "^0.2.7",
|
||||
"@backstage/test-utils-core": "^0.1.1",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
@@ -45,7 +45,7 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.1",
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user