Merge remote-tracking branch 'upstream/master' into org-repo
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search': minor
|
||||
---
|
||||
|
||||
Migrated to new composability API, exporting the plugin instance as `searchPlugin`, and page as `SearchPage`. Due to the old router component also being called `SearchPage`, this is a breaking change. The old page component is now exported as `Router`, which can be used to maintain the old behavior.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Don't respond to a request twice if an entity has not been found.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-register-component': patch
|
||||
---
|
||||
|
||||
Migrated to new composability API, exporting the plugin instance as `registerComponentPlugin`, and page as `RegisterComponentPage`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Migrated to new composability API, exporting the plugin instance as `techdocsPlugin`, the top-level page as `TechdocsPage`, and the entity content as `EntityTechdocsContent`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-pagerduty': patch
|
||||
---
|
||||
|
||||
Migrated to new composability API, exporting the plugin instance as `pagerDutyPlugin`, entity card as `EntityPagerDutyCard`, and entity conditional as `isPagerDutyAvailable`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/test-utils': patch
|
||||
---
|
||||
|
||||
Added `mountedRoutes` option to `wrapInTestApp`, allowing routes to be associated to concrete paths to make `useRouteRef` usable in tested components.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-newrelic': patch
|
||||
---
|
||||
|
||||
Migrate to new composability API, exporting the plugin instance as `newRelicPlugin`, and the root page as `NewRelicPage`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
---
|
||||
|
||||
Migrate to new composability API, exporting the plugin instance as `kubernetesPlugin` and entity content as `EntityKubernetesContent`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kafka': patch
|
||||
---
|
||||
|
||||
Migrate to new composability API, exporting the plugin instance as `kafkaPlugin`, entity content as `EntityKafkaContent`, and entity conditional as `isKafkaAvailable`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Enhance API calls to support trapping 500 errors from techdocs-backend
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core': patch
|
||||
---
|
||||
|
||||
Fixed type inference of `createRouteRef`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-gitops-profiles': patch
|
||||
---
|
||||
|
||||
Migrated to new composability API, exporting the plugin instance as `gitopsProfilesPlugin` and pages as `GitopsProfilesClusterListPage`, `GitopsProfilesClusterPage`, and `GitopsProfilesCreatePage`.
|
||||
@@ -90,7 +90,7 @@ $ docker build -t example-deployment .
|
||||
To run the image locally you can run:
|
||||
|
||||
```sh
|
||||
$ docker run -p -it 7000:7000 example-deployment
|
||||
$ docker run -it -p 7000:7000 example-deployment
|
||||
```
|
||||
|
||||
You should then start to get logs in your terminal, and then you can open your
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"**/@roadiehq/**/@backstage/core": "*",
|
||||
"**/@roadiehq/**/@backstage/plugin-catalog": "*",
|
||||
"**/@roadiehq/**/@backstage/catalog-model": "*"
|
||||
},
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -71,7 +71,11 @@ function withRetries(count: number, fn: () => Promise<void>) {
|
||||
error = err;
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
if (!error.message.match(/rate limit|Too Many Requests/)) {
|
||||
throw error;
|
||||
} else {
|
||||
console.warn('Request was rate limited', error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { RouteRefConfig, RouteRef } from './types';
|
||||
import { RouteRef } from './types';
|
||||
import { IconComponent } from '../icons';
|
||||
|
||||
export type RouteRefConfig<Params extends { [param in string]: string }> = {
|
||||
params?: Array<keyof Params>;
|
||||
/** @deprecated Route refs no longer decide their own path */
|
||||
path?: string;
|
||||
icon?: IconComponent;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export class AbsoluteRouteRef<Params extends { [param in string]: string }> {
|
||||
constructor(private readonly config: RouteRefConfig<Params>) {}
|
||||
@@ -38,9 +47,20 @@ export class AbsoluteRouteRef<Params extends { [param in string]: string }> {
|
||||
}
|
||||
|
||||
export function createRouteRef<
|
||||
ParamKeys extends string,
|
||||
Params extends { [param in string]: string } = { [name in ParamKeys]: string }
|
||||
>(config: RouteRefConfig<Params>): RouteRef<Params> {
|
||||
// Params is the type that we care about and the one to be embedded in the route ref.
|
||||
// For example, given the params ['name', 'kind'], Params will be {name: string, kind: string}
|
||||
Params extends { [param in ParamKey]: string },
|
||||
// ParamKey is here to make sure the Params type properly has its keys narrowed down
|
||||
// to only the elements of params. Defaulting to never makes sure we end up with
|
||||
// Param = {} if the params array is empty.
|
||||
ParamKey extends string = never
|
||||
>(config: {
|
||||
params?: ParamKey[];
|
||||
/** @deprecated Route refs no longer decide their own path */
|
||||
path?: string;
|
||||
icon?: IconComponent;
|
||||
title: string;
|
||||
}): RouteRef<Params> {
|
||||
return new AbsoluteRouteRef<Params>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,9 @@ import {
|
||||
createRouteRef,
|
||||
createExternalRouteRef,
|
||||
ExternalRouteRef,
|
||||
RouteRefConfig,
|
||||
} from './RouteRef';
|
||||
import { RouteRef, RouteRefConfig } from './types';
|
||||
import { RouteRef } from './types';
|
||||
|
||||
const mockConfig = (extra?: Partial<RouteRefConfig<{}>>) => ({
|
||||
path: '/unused',
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
export type {
|
||||
RouteRef,
|
||||
RouteRefConfig,
|
||||
AbsoluteRouteRef,
|
||||
ConcreteRoute,
|
||||
MutableRouteRef,
|
||||
} from './types';
|
||||
export { FlatRoutes } from './FlatRoutes';
|
||||
export { createRouteRef } from './RouteRef';
|
||||
export type { RouteRefConfig } from './RouteRef';
|
||||
export { useRouteRef } from './hooks';
|
||||
|
||||
@@ -45,14 +45,6 @@ export type AbsoluteRouteRef = RouteRef<{}>;
|
||||
*/
|
||||
export type MutableRouteRef = RouteRef<{}>;
|
||||
|
||||
export type RouteRefConfig<Params extends { [param in string]: string }> = {
|
||||
params?: Array<keyof Params>;
|
||||
/** @deprecated Route refs no longer decide their own path */
|
||||
path?: string;
|
||||
icon?: IconComponent;
|
||||
title: string;
|
||||
};
|
||||
|
||||
// A duplicate of the react-router RouteObject, but with routeRef added
|
||||
export interface BackstageRouteObject {
|
||||
caseSensitive: boolean;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 019fe39a0: `@backstage/plugin-catalog` stopped exporting hooks and helpers for other
|
||||
plugins. They are migrated to `@backstage/plugin-catalog-react`.
|
||||
Change both your dependencies and imports to the new package.
|
||||
- 019fe39a0: **BREAKING CHANGE**: The `useEntity` hook has been moved from `@backstage/plugin-catalog` to `@backstage/plugin-catalog-react`.
|
||||
To apply this change to an existing app, add `@backstage/plugin-catalog-react` to your dependencies in `packages/app/package.json`, and update
|
||||
the import inside `packages/app/src/components/catalog/EntityPage.tsx` as well as any other places you were using `useEntity` or any other functions that were moved to `@backstage/plugin-catalog-react`.
|
||||
- 436ca3f62: Remove techdocs.requestUrl and techdocs.storageUrl from app-config.yaml
|
||||
- Updated dependencies [ceef4dd89]
|
||||
- Updated dependencies [720149854]
|
||||
|
||||
@@ -21,9 +21,11 @@ import { Route, Routes } from 'react-router';
|
||||
import { withLogCollector } from '@backstage/test-utils-core';
|
||||
import {
|
||||
useApi,
|
||||
useRouteRef,
|
||||
errorApiRef,
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
createRouteRef,
|
||||
} from '@backstage/core-api';
|
||||
import { MockErrorApi } from './apis';
|
||||
|
||||
@@ -113,4 +115,29 @@ describe('wrapInTestApp', () => {
|
||||
expect(rendered.getByText('foo')).toBeInTheDocument();
|
||||
expect(mockErrorApi.getErrors()).toEqual([{ error: new Error('NOPE') }]);
|
||||
});
|
||||
|
||||
it('should allow route refs to be mounted on specific paths', async () => {
|
||||
const aRouteRef = createRouteRef({ title: 'A' });
|
||||
const bRouteRef = createRouteRef({ title: 'B', params: ['name'] });
|
||||
|
||||
const MyComponent = () => {
|
||||
const a = useRouteRef(aRouteRef);
|
||||
const b = useRouteRef(bRouteRef);
|
||||
return (
|
||||
<div>
|
||||
<div>Link A: {a()}</div>
|
||||
<div>Link B: {b({ name: 'x' })}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const rendered = await renderInTestApp(<MyComponent />, {
|
||||
mountedRoutes: {
|
||||
'/my-a-path': aRouteRef,
|
||||
'/my-b-path/:name': bRouteRef,
|
||||
},
|
||||
});
|
||||
expect(rendered.getByText('Link A: /my-a-path')).toBeInTheDocument();
|
||||
expect(rendered.getByText('Link B: /my-b-path/x')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,9 @@ import { lightTheme } from '@backstage/theme';
|
||||
import privateExports, {
|
||||
defaultSystemIcons,
|
||||
BootErrorPageProps,
|
||||
RouteRef,
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
} from '@backstage/core-api';
|
||||
import { RenderResult } from '@testing-library/react';
|
||||
import { renderWithEffects } from '@backstage/test-utils-core';
|
||||
@@ -44,6 +47,22 @@ type TestAppOptions = {
|
||||
* Initial route entries to pass along as `initialEntries` to the router.
|
||||
*/
|
||||
routeEntries?: string[];
|
||||
|
||||
/**
|
||||
* An object of paths to mount route ref on, with the key being the path and the value
|
||||
* being the RouteRef that the path will be bound to. This allows the route refs to be
|
||||
* used by `useRouteRef` in the rendered elements.
|
||||
*
|
||||
* @example
|
||||
* wrapInTestApp(<MyComponent />, {
|
||||
* mountedRoutes: {
|
||||
* '/my-path': myRouteRef,
|
||||
* }
|
||||
* })
|
||||
* // ...
|
||||
* const link = useRouteRef(myRouteRef)
|
||||
*/
|
||||
mountedRoutes?: { [path: string]: RouteRef };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -90,12 +109,27 @@ export function wrapInTestApp(
|
||||
Wrapper = () => Component as React.ReactElement;
|
||||
}
|
||||
|
||||
const routePlugin = createPlugin({ id: 'mock-route-plugin' });
|
||||
const routeElements = Object.entries(options.mountedRoutes ?? {}).map(
|
||||
([path, routeRef]) => {
|
||||
const PageComponent = () => <div>Mounted at {path}</div>;
|
||||
const Page = routePlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: async () => PageComponent,
|
||||
mountPoint: routeRef,
|
||||
}),
|
||||
);
|
||||
return <Route key={path} path={path} element={<Page />} />;
|
||||
},
|
||||
);
|
||||
|
||||
const AppProvider = app.getProvider();
|
||||
const AppRouter = app.getRouter();
|
||||
|
||||
return (
|
||||
<AppProvider>
|
||||
<AppRouter>
|
||||
{routeElements}
|
||||
{/* The path of * here is needed to be set as a catch all, so it will render the wrapper element
|
||||
* and work with nested routes if they exist too */}
|
||||
<Route path="*" element={<Wrapper />} />
|
||||
|
||||
@@ -76,8 +76,9 @@ export async function createRouter(
|
||||
);
|
||||
if (!entities.length) {
|
||||
res.status(404).send(`No entity with uid ${uid}`);
|
||||
} else {
|
||||
res.status(200).send(entities[0]);
|
||||
}
|
||||
res.status(200).send(entities[0]);
|
||||
})
|
||||
.delete('/entities/by-uid/:uid', async (req, res) => {
|
||||
const { uid } = req.params;
|
||||
@@ -99,8 +100,9 @@ export async function createRouter(
|
||||
.send(
|
||||
`No entity with kind ${kind} namespace ${namespace} name ${name}`,
|
||||
);
|
||||
} else {
|
||||
res.status(200).send(entities[0]);
|
||||
}
|
||||
res.status(200).send(entities[0]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
*/
|
||||
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { gitopsProfilesPlugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
createDevApp().registerPlugin(gitopsProfilesPlugin).render();
|
||||
|
||||
@@ -14,5 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export {
|
||||
gitopsProfilesPlugin,
|
||||
gitopsProfilesPlugin as plugin,
|
||||
GitopsProfilesClusterListPage,
|
||||
GitopsProfilesClusterPage,
|
||||
GitopsProfilesCreatePage,
|
||||
} from './plugin';
|
||||
export * from './api';
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { plugin } from './plugin';
|
||||
import { gitopsProfilesPlugin } from './plugin';
|
||||
|
||||
describe('gitops-profiles', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(gitopsProfilesPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin, createApiFactory } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createApiFactory,
|
||||
createRoutableExtension,
|
||||
} from '@backstage/core';
|
||||
import ProfileCatalog from './components/ProfileCatalog';
|
||||
import ClusterPage from './components/ClusterPage';
|
||||
import ClusterList from './components/ClusterList';
|
||||
@@ -25,7 +29,7 @@ import {
|
||||
} from './routes';
|
||||
import { gitOpsApiRef, GitOpsRestApi } from './api';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const gitopsProfilesPlugin = createPlugin({
|
||||
id: 'gitops-profiles',
|
||||
apis: [
|
||||
createApiFactory(gitOpsApiRef, new GitOpsRestApi('http://localhost:3008')),
|
||||
@@ -35,4 +39,30 @@ export const plugin = createPlugin({
|
||||
router.addRoute(gitOpsClusterDetailsRoute, ClusterPage);
|
||||
router.addRoute(gitOpsClusterCreateRoute, ProfileCatalog);
|
||||
},
|
||||
routes: {
|
||||
listPage: gitOpsClusterListRoute,
|
||||
detailsPage: gitOpsClusterDetailsRoute,
|
||||
createPage: gitOpsClusterCreateRoute,
|
||||
},
|
||||
});
|
||||
|
||||
export const GitopsProfilesClusterListPage = gitopsProfilesPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./components/ClusterList').then(m => m.default),
|
||||
mountPoint: gitOpsClusterListRoute,
|
||||
}),
|
||||
);
|
||||
|
||||
export const GitopsProfilesClusterPage = gitopsProfilesPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./components/ClusterPage').then(m => m.default),
|
||||
mountPoint: gitOpsClusterDetailsRoute,
|
||||
}),
|
||||
);
|
||||
|
||||
export const GitopsProfilesCreatePage = gitopsProfilesPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./components/ProfileCatalog').then(m => m.default),
|
||||
mountPoint: gitOpsClusterCreateRoute,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -28,6 +28,7 @@ export const gitOpsClusterDetailsRoute = createRouteRef({
|
||||
icon: NoIcon,
|
||||
path: '/gitops-cluster/:owner/:repo',
|
||||
title: 'GitOps Cluster details',
|
||||
params: ['owner', 'repo'],
|
||||
});
|
||||
|
||||
export const gitOpsClusterCreateRoute = createRouteRef({
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { kafkaPlugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
createDevApp().registerPlugin(kafkaPlugin).render();
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import React from 'react';
|
||||
import { Route, Routes } from 'react-router';
|
||||
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { rootCatalogKafkaRouteRef } from './plugin';
|
||||
import { KAFKA_CONSUMER_GROUP_ANNOTATION } from './constants';
|
||||
import { KafkaTopicsForConsumer } from './components/ConsumerGroupOffsets/ConsumerGroupOffsets';
|
||||
@@ -26,10 +26,23 @@ import { MissingAnnotationEmptyState } from '@backstage/core';
|
||||
export const isPluginApplicableToEntity = (entity: Entity) =>
|
||||
Boolean(entity.metadata.annotations?.[KAFKA_CONSUMER_GROUP_ANNOTATION]);
|
||||
|
||||
export const Router = ({ entity }: { entity: Entity }) => {
|
||||
return !isPluginApplicableToEntity(entity) ? (
|
||||
<MissingAnnotationEmptyState annotation={KAFKA_CONSUMER_GROUP_ANNOTATION} />
|
||||
) : (
|
||||
type Props = {
|
||||
/** @deprecated The entity is now grabbed from context instead */
|
||||
entity?: Entity;
|
||||
};
|
||||
|
||||
export const Router = (_props: Props) => {
|
||||
const { entity } = useEntity();
|
||||
|
||||
if (!isPluginApplicableToEntity(entity)) {
|
||||
return (
|
||||
<MissingAnnotationEmptyState
|
||||
annotation={KAFKA_CONSUMER_GROUP_ANNOTATION}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
path={`${rootCatalogKafkaRouteRef.path}`}
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { plugin } from './plugin';
|
||||
export {
|
||||
kafkaPlugin,
|
||||
kafkaPlugin as plugin,
|
||||
EntityKafkaContent,
|
||||
} from './plugin';
|
||||
export { KAFKA_CONSUMER_GROUP_ANNOTATION } from './constants';
|
||||
export { Router, isPluginApplicableToEntity } from './Router';
|
||||
export {
|
||||
Router,
|
||||
isPluginApplicableToEntity,
|
||||
isPluginApplicableToEntity as isKafkaAvailable,
|
||||
} from './Router';
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { plugin } from './plugin';
|
||||
import { kafkaPlugin } from './plugin';
|
||||
|
||||
describe('kafka', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(kafkaPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import {
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/core';
|
||||
@@ -27,7 +28,7 @@ export const rootCatalogKafkaRouteRef = createRouteRef({
|
||||
title: 'Kafka',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const kafkaPlugin = createPlugin({
|
||||
id: 'kafka',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
@@ -36,4 +37,14 @@ export const plugin = createPlugin({
|
||||
factory: ({ discoveryApi }) => new KafkaBackendClient({ discoveryApi }),
|
||||
}),
|
||||
],
|
||||
routes: {
|
||||
entityContent: rootCatalogKafkaRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const EntityKafkaContent = kafkaPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./Router').then(m => m.Router),
|
||||
mountPoint: rootCatalogKafkaRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src';
|
||||
import { kubernetesPlugin } from '../src';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
createDevApp().registerPlugin(kubernetesPlugin).render();
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.7.1",
|
||||
"@backstage/plugin-catalog-react": "^0.0.2",
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/core": "^0.6.0",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.2.6",
|
||||
|
||||
@@ -16,15 +16,22 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { rootCatalogKubernetesRouteRef } from './plugin';
|
||||
import { KubernetesContent } from './components/KubernetesContent';
|
||||
import { MissingAnnotationEmptyState } from '@backstage/core';
|
||||
|
||||
const KUBERNETES_ANNOTATION = 'backstage.io/kubernetes-id';
|
||||
|
||||
export const Router = ({ entity }: { entity: Entity }) => {
|
||||
type Props = {
|
||||
/** @deprecated The entity is now grabbed from context instead */
|
||||
entity?: Entity;
|
||||
};
|
||||
|
||||
export const Router = (_props: Props) => {
|
||||
const { entity } = useEntity();
|
||||
|
||||
const kubernetesAnnotationValue =
|
||||
entity.metadata.annotations?.[KUBERNETES_ANNOTATION];
|
||||
|
||||
|
||||
@@ -13,5 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { plugin } from './plugin';
|
||||
export {
|
||||
kubernetesPlugin,
|
||||
kubernetesPlugin as plugin,
|
||||
EntityKubernetesContent,
|
||||
} from './plugin';
|
||||
export { Router } from './Router';
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { plugin } from './plugin';
|
||||
import { kubernetesPlugin } from './plugin';
|
||||
|
||||
describe('kubernetes', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(kubernetesPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
googleAuthApiRef,
|
||||
createRoutableExtension,
|
||||
} from '@backstage/core';
|
||||
import { KubernetesBackendClient } from './api/KubernetesBackendClient';
|
||||
import { kubernetesApiRef } from './api/types';
|
||||
@@ -30,7 +31,7 @@ export const rootCatalogKubernetesRouteRef = createRouteRef({
|
||||
title: 'Kubernetes',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const kubernetesPlugin = createPlugin({
|
||||
id: 'kubernetes',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
@@ -47,4 +48,14 @@ export const plugin = createPlugin({
|
||||
},
|
||||
}),
|
||||
],
|
||||
routes: {
|
||||
entityContent: rootCatalogKubernetesRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const EntityKubernetesContent = kubernetesPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./Router').then(m => m.Router),
|
||||
mountPoint: rootCatalogKubernetesRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
*/
|
||||
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { newRelicPlugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
createDevApp().registerPlugin(newRelicPlugin).render();
|
||||
|
||||
@@ -14,4 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export {
|
||||
newRelicPlugin,
|
||||
newRelicPlugin as plugin,
|
||||
NewRelicPage,
|
||||
} from './plugin';
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { plugin } from './plugin';
|
||||
import { newRelicPlugin } from './plugin';
|
||||
|
||||
describe('newrelic', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(newRelicPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
createRoutableExtension,
|
||||
} from '@backstage/core';
|
||||
import { NewRelicClient, newRelicApiRef } from './api';
|
||||
import NewRelicComponent from './components/NewRelicComponent';
|
||||
@@ -28,7 +29,7 @@ export const rootRouteRef = createRouteRef({
|
||||
title: 'newrelic',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const newRelicPlugin = createPlugin({
|
||||
id: 'newrelic',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
@@ -40,4 +41,15 @@ export const plugin = createPlugin({
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, NewRelicComponent);
|
||||
},
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const NewRelicPage = newRelicPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () =>
|
||||
import('./components/NewRelicComponent').then(m => m.default),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { pagerDutyPlugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
createDevApp().registerPlugin(pagerDutyPlugin).render();
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.7.1",
|
||||
"@backstage/plugin-catalog-react": "^0.0.2",
|
||||
"@backstage/core": "^0.6.0",
|
||||
"@backstage/theme": "^0.2.3",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
|
||||
@@ -17,6 +17,7 @@ import React from 'react';
|
||||
import { render, waitFor, fireEvent, act } from '@testing-library/react';
|
||||
import { PagerDutyCard } from './PagerDutyCard';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
alertApiRef,
|
||||
@@ -80,7 +81,9 @@ describe('PageDutyCard', () => {
|
||||
const { getByText, queryByTestId } = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<PagerDutyCard entity={entity} />
|
||||
<EntityProvider entity={entity}>
|
||||
<PagerDutyCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
),
|
||||
);
|
||||
@@ -99,7 +102,9 @@ describe('PageDutyCard', () => {
|
||||
const { getByText, queryByTestId } = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<PagerDutyCard entity={entity} />
|
||||
<EntityProvider entity={entity}>
|
||||
<PagerDutyCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
),
|
||||
);
|
||||
@@ -114,7 +119,9 @@ describe('PageDutyCard', () => {
|
||||
const { getByText, queryByTestId } = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<PagerDutyCard entity={entity} />
|
||||
<EntityProvider entity={entity}>
|
||||
<PagerDutyCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
),
|
||||
);
|
||||
@@ -134,7 +141,9 @@ describe('PageDutyCard', () => {
|
||||
const { getByText, queryByTestId, getByTestId, getByRole } = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<PagerDutyCard entity={entity} />
|
||||
<EntityProvider entity={entity}>
|
||||
<PagerDutyCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { useApi, Progress, HeaderIconLinkRow } from '@backstage/core';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
Button,
|
||||
makeStyles,
|
||||
@@ -56,11 +57,13 @@ export const isPluginApplicableToEntity = (entity: Entity) =>
|
||||
Boolean(entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY]);
|
||||
|
||||
type Props = {
|
||||
entity: Entity;
|
||||
/** @deprecated The entity is now grabbed from context instead */
|
||||
entity?: Entity;
|
||||
};
|
||||
|
||||
export const PagerDutyCard = ({ entity }: Props) => {
|
||||
export const PagerDutyCard = (_props: Props) => {
|
||||
const classes = useStyles();
|
||||
const { entity } = useEntity();
|
||||
const api = useApi(pagerDutyApiRef);
|
||||
const [showDialog, setShowDialog] = useState<boolean>(false);
|
||||
const [refreshIncidents, setRefreshIncidents] = useState<boolean>(false);
|
||||
|
||||
@@ -13,9 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { plugin } from './plugin';
|
||||
export {
|
||||
pagerDutyPlugin,
|
||||
pagerDutyPlugin as plugin,
|
||||
EntityPagerDutyCard,
|
||||
} from './plugin';
|
||||
export {
|
||||
isPluginApplicableToEntity,
|
||||
isPluginApplicableToEntity as isPagerDutyAvailable,
|
||||
PagerDutyCard,
|
||||
} from './components/PagerDutyCard';
|
||||
export {
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { plugin } from './plugin';
|
||||
import { pagerDutyPlugin } from './plugin';
|
||||
|
||||
describe('pagerduty', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(pagerDutyPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
configApiRef,
|
||||
createComponentExtension,
|
||||
} from '@backstage/core';
|
||||
import { pagerDutyApiRef, PagerDutyClient } from './api';
|
||||
|
||||
@@ -27,7 +28,7 @@ export const rootRouteRef = createRouteRef({
|
||||
title: 'pagerduty',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const pagerDutyPlugin = createPlugin({
|
||||
id: 'pagerduty',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
@@ -38,3 +39,12 @@ export const plugin = createPlugin({
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const EntityPagerDutyCard = pagerDutyPlugin.provide(
|
||||
createComponentExtension({
|
||||
component: {
|
||||
lazy: () =>
|
||||
import('./components/PagerDutyCard').then(m => m.PagerDutyCard),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
*/
|
||||
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { registerComponentPlugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
createDevApp().registerPlugin(registerComponentPlugin).render();
|
||||
|
||||
@@ -14,5 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export {
|
||||
registerComponentPlugin,
|
||||
registerComponentPlugin as plugin,
|
||||
RegisterComponentPage,
|
||||
} from './plugin';
|
||||
export { Router } from './components/Router';
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { plugin } from './plugin';
|
||||
import { registerComponentPlugin } from './plugin';
|
||||
|
||||
describe('register-component', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(registerComponentPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,8 +14,29 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import {
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
createRouteRef,
|
||||
} from '@backstage/core';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'register-component',
|
||||
const rootRouteRef = createRouteRef({
|
||||
title: 'Register Component',
|
||||
});
|
||||
|
||||
export const registerComponentPlugin = createPlugin({
|
||||
id: 'register-component',
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const RegisterComponentPage = registerComponentPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () =>
|
||||
import('./components/RegisterComponentPage').then(
|
||||
m => m.RegisterComponentPage,
|
||||
),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { searchPlugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
createDevApp().registerPlugin(searchPlugin).render();
|
||||
|
||||
@@ -13,5 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { plugin } from './plugin';
|
||||
export * from './components';
|
||||
export { searchPlugin, searchPlugin as plugin, SearchPage } from './plugin';
|
||||
export {
|
||||
Filters,
|
||||
FiltersButton,
|
||||
SearchBar,
|
||||
SearchPage as Router,
|
||||
SearchResult,
|
||||
SidebarSearch,
|
||||
} from './components';
|
||||
export type { FiltersState } from './components';
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { plugin } from './plugin';
|
||||
import { searchPlugin } from './plugin';
|
||||
|
||||
describe('search', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(searchPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,17 +13,31 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import { SearchPage } from './components/SearchPage';
|
||||
import {
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
createRoutableExtension,
|
||||
} from '@backstage/core';
|
||||
import { SearchPage as SearchPageComponent } from './components/SearchPage';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
path: '/search',
|
||||
title: 'search',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const searchPlugin = createPlugin({
|
||||
id: 'search',
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, SearchPage);
|
||||
router.addRoute(rootRouteRef, SearchPageComponent);
|
||||
},
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const SearchPage = searchPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./components/SearchPage').then(m => m.SearchPage),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { configApiRef, discoveryApiRef } from '@backstage/core';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
import { techdocsPlugin } from '../src/plugin';
|
||||
import { TechDocsDevStorageApi } from './api';
|
||||
import { techdocsStorageApiRef } from '../src';
|
||||
|
||||
@@ -30,5 +30,5 @@ createDevApp()
|
||||
discoveryApi,
|
||||
}),
|
||||
})
|
||||
.registerPlugin(plugin)
|
||||
.registerPlugin(techdocsPlugin)
|
||||
.render();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import { MissingAnnotationEmptyState } from '@backstage/core';
|
||||
import {
|
||||
@@ -38,7 +39,14 @@ export const Router = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const EmbeddedDocsRouter = ({ entity }: { entity: Entity }) => {
|
||||
type Props = {
|
||||
/** @deprecated The entity is now grabbed from context instead */
|
||||
entity?: Entity;
|
||||
};
|
||||
|
||||
export const EmbeddedDocsRouter = (_props: Props) => {
|
||||
const { entity } = useEntity();
|
||||
|
||||
const projectId = entity.metadata.annotations?.[TECHDOCS_ANNOTATION];
|
||||
|
||||
if (!projectId) {
|
||||
|
||||
@@ -157,14 +157,23 @@ export class TechDocsStorageApi implements TechDocsStorage {
|
||||
`${url.endsWith('/') ? url : `${url}/`}index.html`,
|
||||
);
|
||||
|
||||
if (request.status === 404) {
|
||||
let errorMessage = 'Page not found. ';
|
||||
// path is empty for the home page of an entity's docs site
|
||||
if (!path) {
|
||||
errorMessage +=
|
||||
'This could be because there is no index.md file in the root of the docs directory of this repository.';
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
let errorMessage = '';
|
||||
switch (request.status) {
|
||||
case 404:
|
||||
errorMessage = 'Page not found. ';
|
||||
// path is empty for the home page of an entity's docs site
|
||||
if (!path) {
|
||||
errorMessage +=
|
||||
'This could be because there is no index.md file in the root of the docs directory of this repository.';
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
case 500:
|
||||
errorMessage =
|
||||
'Could not generate documentation or an error in the TechDocs backend. ';
|
||||
throw new Error(errorMessage);
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
return request.text();
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export {
|
||||
techdocsPlugin,
|
||||
techdocsPlugin as plugin,
|
||||
TechdocsPage,
|
||||
EntityTechdocsContent,
|
||||
} from './plugin';
|
||||
export { Router, EmbeddedDocsRouter } from './Router';
|
||||
export * from './reader';
|
||||
export * from './api';
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { plugin } from './plugin';
|
||||
import { techdocsPlugin } from './plugin';
|
||||
|
||||
describe('techdocs', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect(techdocsPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
createApiFactory,
|
||||
configApiRef,
|
||||
discoveryApiRef,
|
||||
createRoutableExtension,
|
||||
} from '@backstage/core';
|
||||
import {
|
||||
techdocsStorageApiRef,
|
||||
@@ -58,7 +59,7 @@ export const rootCatalogDocsRouteRef = createRouteRef({
|
||||
title: 'Docs',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const techdocsPlugin = createPlugin({
|
||||
id: 'techdocs',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
@@ -80,4 +81,22 @@ export const plugin = createPlugin({
|
||||
}),
|
||||
}),
|
||||
],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
entityContent: rootCatalogDocsRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const TechdocsPage = techdocsPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./Router').then(m => m.Router),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
export const EntityTechdocsContent = techdocsPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./Router').then(m => m.EmbeddedDocsRouter),
|
||||
mountPoint: rootCatalogDocsRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -155,7 +155,9 @@ export const Reader = ({ entityId, onReady }: Props) => {
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
return <TechDocsNotFound errorMessage={error.message} />;
|
||||
// TODO Enhance API call to return customize error objects so we can identify which we ran into
|
||||
// For now this defaults to display error code 404
|
||||
return <TechDocsNotFound statusCode={404} errorMessage={error.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -41,3 +41,20 @@ describe('<TechDocsNotFound errorMessage="This is a custom error message" />', (
|
||||
expect(rendered.getByTestId('go-back-link')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('<TechDocsNotFound statusCode={500} errorMessage="This is a custom error message" />', () => {
|
||||
it('should render with a custom status code, custom error message and go back link', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<TechDocsNotFound
|
||||
statusCode={500}
|
||||
errorMessage="This is a custom error message"
|
||||
/>,
|
||||
),
|
||||
);
|
||||
rendered.getByText(/This is a custom error message/i);
|
||||
rendered.getByText(/500/i);
|
||||
rendered.getByText(/Looks like someone dropped the mic!/i);
|
||||
expect(rendered.getByTestId('go-back-link')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,9 +19,10 @@ import { ErrorPage, useApi, configApiRef } from '@backstage/core';
|
||||
|
||||
type Props = {
|
||||
errorMessage?: string;
|
||||
statusCode?: number;
|
||||
};
|
||||
|
||||
export const TechDocsNotFound = ({ errorMessage }: Props) => {
|
||||
export const TechDocsNotFound = ({ errorMessage, statusCode }: Props) => {
|
||||
const techdocsBuilder = useApi(configApiRef).getOptionalString(
|
||||
'techdocs.builder',
|
||||
);
|
||||
@@ -37,7 +38,7 @@ export const TechDocsNotFound = ({ errorMessage }: Props) => {
|
||||
|
||||
return (
|
||||
<ErrorPage
|
||||
status="404"
|
||||
status={statusCode ? statusCode.toString() : '404'}
|
||||
statusMessage={errorMessage || 'Documentation not found'}
|
||||
additionalInfo={additionalInfo}
|
||||
/>
|
||||
|
||||
@@ -2645,55 +2645,15 @@
|
||||
remark-gfm "^1.0.0"
|
||||
zen-observable "^0.8.15"
|
||||
|
||||
"@backstage/core@^0.5.0":
|
||||
version "0.6.0"
|
||||
dependencies:
|
||||
"@backstage/config" "^0.1.2"
|
||||
"@backstage/core-api" "^0.2.8"
|
||||
"@backstage/theme" "^0.2.3"
|
||||
"@material-ui/core" "^4.11.0"
|
||||
"@material-ui/icons" "^4.9.1"
|
||||
"@material-ui/lab" "4.0.0-alpha.45"
|
||||
"@testing-library/react-hooks" "^3.4.2"
|
||||
"@types/dagre" "^0.7.44"
|
||||
"@types/prop-types" "^15.7.3"
|
||||
"@types/react" "^16.9"
|
||||
"@types/react-sparklines" "^1.7.0"
|
||||
classnames "^2.2.6"
|
||||
clsx "^1.1.0"
|
||||
d3-selection "^2.0.0"
|
||||
d3-shape "^2.0.0"
|
||||
d3-zoom "^2.0.0"
|
||||
dagre "^0.8.5"
|
||||
immer "^8.0.1"
|
||||
lodash "^4.17.15"
|
||||
material-table "^1.69.1"
|
||||
prop-types "^15.7.2"
|
||||
qs "^6.9.4"
|
||||
rc-progress "^3.0.0"
|
||||
react "^16.12.0"
|
||||
react-dom "^16.12.0"
|
||||
react-helmet "6.1.0"
|
||||
react-hook-form "^6.6.0"
|
||||
react-markdown "^5.0.2"
|
||||
react-router "6.0.0-beta.0"
|
||||
react-router-dom "6.0.0-beta.0"
|
||||
react-sparklines "^1.7.0"
|
||||
react-syntax-highlighter "^13.5.1"
|
||||
react-use "^15.3.3"
|
||||
remark-gfm "^1.0.0"
|
||||
zen-observable "^0.8.15"
|
||||
|
||||
"@backstage/plugin-catalog@^0.2.0", "@backstage/plugin-catalog@^0.2.1":
|
||||
version "0.2.14"
|
||||
resolved "https://registry.npmjs.org/@backstage/plugin-catalog/-/plugin-catalog-0.2.14.tgz#50a4176a55ffa543a426ec78cbc9deaecdbcf2b7"
|
||||
integrity sha512-lDmNcC+m1zbbzYATUp5yIZ5PUp+YyBc1KKu3CCgqjLWSbJ1aJrU1N4g59euel1l2+qSW+lH76Kkp6ZYpZbSO9A==
|
||||
version "0.3.0"
|
||||
dependencies:
|
||||
"@backstage/catalog-client" "^0.3.5"
|
||||
"@backstage/catalog-model" "^0.7.0"
|
||||
"@backstage/core" "^0.5.0"
|
||||
"@backstage/plugin-scaffolder" "^0.4.1"
|
||||
"@backstage/theme" "^0.2.2"
|
||||
"@backstage/catalog-model" "^0.7.1"
|
||||
"@backstage/core" "^0.6.0"
|
||||
"@backstage/plugin-catalog-react" "^0.0.2"
|
||||
"@backstage/plugin-scaffolder" "^0.4.2"
|
||||
"@backstage/theme" "^0.2.3"
|
||||
"@material-ui/core" "^4.11.0"
|
||||
"@material-ui/icons" "^4.9.1"
|
||||
"@material-ui/lab" "4.0.0-alpha.45"
|
||||
|
||||
Reference in New Issue
Block a user