Merge remote-tracking branch 'upstream/master' into org-repo

This commit is contained in:
Nir Gazit
2021-02-08 11:24:38 +02:00
68 changed files with 493 additions and 148 deletions
@@ -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]);
});
}
+2 -2
View File
@@ -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();
+7 -1
View File
@@ -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';
+2 -2
View File
@@ -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();
});
});
+32 -2
View File
@@ -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,
}),
);
+1
View File
@@ -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({
+2 -2
View File
@@ -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();
+18 -5
View File
@@ -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}`}
+10 -2
View File
@@ -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';
+2 -2
View File
@@ -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();
});
});
+12 -1
View File
@@ -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,
}),
);
+2 -2
View File
@@ -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();
+1
View File
@@ -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",
+9 -2
View File
@@ -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];
+5 -1
View File
@@ -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';
+2 -2
View File
@@ -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();
});
});
+12 -1
View File
@@ -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,
}),
);
+2 -2
View File
@@ -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();
+5 -1
View File
@@ -14,4 +14,8 @@
* limitations under the License.
*/
export { plugin } from './plugin';
export {
newRelicPlugin,
newRelicPlugin as plugin,
NewRelicPage,
} from './plugin';
+2 -2
View File
@@ -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();
});
});
+13 -1
View File
@@ -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,
}),
);
+2 -2
View File
@@ -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();
+1
View File
@@ -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);
+6 -1
View File
@@ -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 {
+2 -2
View File
@@ -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();
});
});
+11 -1
View File
@@ -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),
},
}),
);
+2 -2
View File
@@ -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();
+5 -1
View File
@@ -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();
});
});
+24 -3
View File
@@ -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,
}),
);
+2 -2
View File
@@ -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();
+10 -2
View File
@@ -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';
+2 -2
View File
@@ -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();
});
});
+18 -4
View File
@@ -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,
}),
);
+2 -2
View File
@@ -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();
+9 -1
View File
@@ -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) {
+17 -8
View File
@@ -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();
+6 -1
View File
@@ -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';
+2 -2
View File
@@ -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();
});
});
+20 -1
View File
@@ -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}
/>