Added DevTools plugin

Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com>
This commit is contained in:
Andre Wanlin
2023-04-14 13:46:42 -05:00
parent 1ebb0a3944
commit 347aeca204
67 changed files with 3405 additions and 15 deletions
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { createApiRef } from '@backstage/core-plugin-api';
import {
ConfigInfo,
DevToolsInfo,
ExternalDependency,
} from '@backstage/plugin-devtools-common';
export const devToolsApiRef = createApiRef<DevToolsApi>({
id: 'plugin.devtools.service',
});
export interface DevToolsApi {
getConfig(): Promise<ConfigInfo | undefined>;
getExternalDependencies(): Promise<ExternalDependency[] | undefined>;
getInfo(): Promise<DevToolsInfo | undefined>;
}
@@ -0,0 +1,78 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
import {
ConfigInfo,
DevToolsInfo,
ExternalDependency,
} from '@backstage/plugin-devtools-common';
import { ResponseError } from '@backstage/errors';
import { DevToolsApi } from './DevToolsApi';
export class DevToolsClient implements DevToolsApi {
private readonly discoveryApi: DiscoveryApi;
private readonly identityApi: IdentityApi;
public constructor(options: {
discoveryApi: DiscoveryApi;
identityApi: IdentityApi;
}) {
this.discoveryApi = options.discoveryApi;
this.identityApi = options.identityApi;
}
public async getConfig(): Promise<ConfigInfo | undefined> {
const urlSegment = 'config';
const configInfo = await this.get<ConfigInfo | undefined>(urlSegment);
return configInfo;
}
public async getExternalDependencies(): Promise<
ExternalDependency[] | undefined
> {
const urlSegment = 'external-dependencies';
const externalDependencies = await this.get<
ExternalDependency[] | undefined
>(urlSegment);
return externalDependencies;
}
public async getInfo(): Promise<DevToolsInfo | undefined> {
const urlSegment = 'info';
const info = await this.get<DevToolsInfo | undefined>(urlSegment);
return info;
}
private async get<T>(path: string): Promise<T> {
const baseUrl = `${await this.discoveryApi.getBaseUrl('devtools')}/`;
const url = new URL(path, baseUrl);
const { token } = await this.identityApi.getCredentials();
const response = await fetch(url.toString(), {
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
if (!response.ok) {
throw await ResponseError.fromResponse(response);
}
return response.json() as Promise<T>;
}
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './DevToolsApi';
export * from './DevToolsClient';
@@ -0,0 +1,95 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { Progress, WarningPanel } from '@backstage/core-components';
import {
Box,
createStyles,
makeStyles,
Paper,
Theme,
Typography,
useTheme,
} from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import React from 'react';
import ReactJson from 'react-json-view';
import { useConfig } from '../../../hooks';
import { ConfigError } from '@backstage/plugin-devtools-common';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
warningStyle: {
paddingBottom: theme.spacing(2),
},
paperStyle: {
padding: theme.spacing(2),
},
}),
);
export const WarningContent = ({ error }: { error: ConfigError }) => {
if (!error.messages) {
return <Typography>{error.message}</Typography>;
}
const messages = error.messages as string[];
return (
<Box>
{messages.map(message => (
<Typography>{message}</Typography>
))}
</Box>
);
};
/** @public */
export const ConfigContent = () => {
const classes = useStyles();
const theme = useTheme();
const { configInfo, loading, error } = useConfig();
if (loading) {
return <Progress />;
} else if (error) {
return <Alert severity="error">{error.message}</Alert>;
}
if (!configInfo) {
return <Alert severity="error">Unable to load config data</Alert>;
}
return (
<Box>
{configInfo && configInfo.error && (
<Box className={classes.warningStyle}>
<WarningPanel title="Config validation failed">
<WarningContent error={configInfo.error} />
</WarningPanel>
</Box>
)}
<Paper className={classes.paperStyle}>
<ReactJson
src={configInfo.config as object}
name="config"
enableClipboard={false}
theme={theme.palette.type === 'dark' ? 'monokai' : 'rjv-default'}
/>
</Paper>
</Box>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { ConfigContent } from './ConfigContent';
@@ -0,0 +1,140 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 {
Progress,
StatusError,
StatusOK,
StatusWarning,
Table,
TableColumn,
} from '@backstage/core-components';
import { ExternalDependency } from '@backstage/plugin-devtools-common';
import {
Box,
createStyles,
Grid,
makeStyles,
Paper,
Theme,
Typography,
} from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import React from 'react';
import { useExternalDependencies } from '../../../hooks';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
paperStyle: {
padding: theme.spacing(2),
},
}),
);
export const getExternalDependencyStatus = (
result: Partial<ExternalDependency> | undefined,
) => {
switch (result?.status) {
case 'Healthy':
return (
<Typography component="span">
<StatusOK /> {result.status}
</Typography>
);
case 'Unhealthy':
return (
<Typography component="span">
<StatusError /> {`${result.status}`}
</Typography>
);
case undefined:
default:
return (
<Typography component="span">
<StatusWarning /> Unknown
</Typography>
);
}
};
const columns: TableColumn[] = [
{
title: 'Name',
width: 'auto',
field: 'name',
},
{
title: 'Target',
width: 'auto',
field: 'target',
},
{
title: 'Type',
width: 'auto',
field: 'type',
},
{
title: 'Status',
width: 'auto',
render: (row: Partial<ExternalDependency>) => (
<Grid container direction="column">
<Grid item>
<Typography variant="button">
{getExternalDependencyStatus(row)}
</Typography>
</Grid>
<Grid item>{row.error && <Typography>{row.error}</Typography>}</Grid>
</Grid>
),
},
];
/** @public */
export const ExternalDependenciesContent = () => {
const classes = useStyles();
const { externalDependencies, loading, error } = useExternalDependencies();
if (loading) {
return <Progress />;
} else if (error) {
return <Alert severity="error">{error.message}</Alert>;
}
if (!externalDependencies || externalDependencies.length === 0) {
return (
<Box>
<Paper className={classes.paperStyle}>
<Typography>No external dependencies found</Typography>
</Paper>
</Box>
);
}
return (
<Table
title="Status"
options={{
paging: true,
pageSize: 20,
pageSizeOptions: [20, 50, 100],
loadingType: 'linear',
showEmptyDataSourceMessage: !loading,
}}
columns={columns}
data={externalDependencies || []}
/>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { ExternalDependenciesContent } from './ExternalDependenciesContent';
@@ -0,0 +1,25 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { SvgIcon, SvgIconProps } from '@material-ui/core';
import React from 'react';
export const BackstageLogoIcon = (props: SvgIconProps) => (
<SvgIcon {...props} viewBox="0 0 337.46 428.5">
<path d="M303 166.05a80.69 80.69 0 0013.45-10.37c.79-.77 1.55-1.53 2.3-2.3a83.12 83.12 0 007.93-9.38 63.69 63.69 0 006.32-10.77 48.58 48.58 0 004.35-16.4c1.49-19.39-10-38.67-35.62-54.22L198.56 0 78.3 115.23 0 190.25l108.6 65.91a111.59 111.59 0 0057.76 16.41c24.92 0 48.8-8.8 66.42-25.69 19.16-18.36 25.52-42.12 13.7-61.87a49.22 49.22 0 00-6.8-8.87 89.17 89.17 0 0019.32 2.15h.15a85.08 85.08 0 0031-5.79 80.88 80.88 0 0012.85-6.45zm-100.55 59.81c-19.32 18.51-50.4 21.23-75.7 5.9l-75.14-45.61 67.45-64.64 76.41 46.38c27.53 16.69 26.02 39.72 6.98 57.97zm8.93-82.22l-70.65-42.89L205.14 39l69.37 42.1c25.94 15.72 29.31 37 10.55 55a60.69 60.69 0 01-73.68 7.54zm29.86 190c-19.57 18.75-46.17 29.09-74.88 29.09a123.73 123.73 0 01-64.1-18.2L0 282.52v24.67l108.6 65.91a111.6 111.6 0 0057.76 16.42c24.92 0 48.8-8.81 66.42-25.69 12.88-12.34 20-27.13 19.68-41.49v-1.79a87.27 87.27 0 01-11.22 13.13zm0-39c-19.57 18.75-46.17 29.08-74.88 29.08a123.81 123.81 0 01-64.1-18.19L0 243.53v24.68l108.6 65.91a111.6 111.6 0 0057.76 16.42c24.92 0 48.8-8.81 66.42-25.69 12.88-12.34 20-27.13 19.68-41.5v-1.78a87.27 87.27 0 01-11.22 13.13zm0-39c-19.57 18.76-46.17 29.09-74.88 29.09a123.81 123.81 0 01-64.1-18.19L0 204.55v24.68l108.6 65.91a111.59 111.59 0 0057.76 16.41c24.92 0 48.8-8.8 66.42-25.68 12.88-12.35 20-27.13 19.68-41.5v-1.82a86.09 86.09 0 01-11.22 13.16zm83.7 25.74a94.15 94.15 0 01-60.2 25.86V334a81.6 81.6 0 0051.74-22.37c14-13.38 21.14-28.11 21-42.64v-2.19a94.92 94.92 0 01-12.54 14.65zm-83.7 91.21c-19.57 18.76-46.17 29.09-74.88 29.09a123.73 123.73 0 01-64.1-18.2L0 321.5v24.68l108.6 65.9a111.6 111.6 0 0057.76 16.42c24.92 0 48.8-8.8 66.42-25.69 12.88-12.34 20-27.13 19.68-41.49v-1.79a86.29 86.29 0 01-11.22 13.13zM327 162.45c-.68.69-1.35 1.38-2.05 2.06a94.37 94.37 0 01-10.64 8.65 91.35 91.35 0 01-11.6 7 94.53 94.53 0 01-26.24 8.71 97.69 97.69 0 01-14.16 1.57c.5 1.61.9 3.25 1.25 4.9a53.27 53.27 0 011.14 12V217h.05a84.41 84.41 0 0025.35-5.55 81 81 0 0026.39-16.82c.8-.77 1.5-1.56 2.26-2.34a82.08 82.08 0 007.93-9.38 63.76 63.76 0 006.32-10.74 48.55 48.55 0 004.32-16.45c.09-1.23.2-2.47.19-3.7V150q-1.08 1.54-2.25 3.09a96.73 96.73 0 01-8.26 9.36zm0 77.92c-.69.7-1.31 1.41-2 2.1a94.2 94.2 0 01-60.2 25.86V295a81.6 81.6 0 0051.74-22.37 73.51 73.51 0 0016.46-22.5 48.56 48.56 0 004.32-16.44c.09-1.24.2-2.47.19-3.71v-2.19c-.74 1.07-1.46 2.15-2.27 3.21a95.68 95.68 0 01-8.24 9.37zm0-39c-.69.7-1.31 1.41-2 2.1a93.18 93.18 0 01-10.63 8.65 91.63 91.63 0 01-11.63 7 95.47 95.47 0 01-37.94 10.18V256a81.65 81.65 0 0051.74-22.37c.8-.77 1.5-1.56 2.26-2.34a82.08 82.08 0 007.93-9.38 63.76 63.76 0 006.27-10.76 48.56 48.56 0 004.32-16.44c.09-1.24.2-2.48.19-3.71v-2.2c-.74 1.08-1.46 2.16-2.27 3.22a95.68 95.68 0 01-8.24 9.37z" />
</SvgIcon>
);
@@ -0,0 +1,138 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { Progress } from '@backstage/core-components';
import {
Avatar,
Box,
createStyles,
Divider,
List,
ListItem,
ListItemAvatar,
ListItemText,
makeStyles,
Paper,
Theme,
} from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import React from 'react';
import { useInfo } from '../../../hooks';
import { InfoDependenciesTable } from './InfoDependenciesTable';
import DescriptionIcon from '@material-ui/icons/Description';
import DeveloperBoardIcon from '@material-ui/icons/DeveloperBoard';
import { BackstageLogoIcon } from './BackstageLogoIcon';
import FileCopyIcon from '@material-ui/icons/FileCopy';
import { DevToolsInfo } from '@backstage/plugin-devtools-common';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
paperStyle: {
marginBottom: theme.spacing(2),
},
flexContainer: {
display: 'flex',
flexDirection: 'row',
padding: 0,
},
copyButton: {
float: 'left',
margin: theme.spacing(2),
},
}),
);
const copyToClipboard = ({ about }: { about: DevToolsInfo | undefined }) => {
if (about) {
let formatted = `OS: ${about.operatingSystem}\nnode: ${about.nodeJsVersion}\nbackstage: ${about.backstageVersion}\nDependencies:\n`;
const deps = about.dependencies;
for (const key in deps) {
if (Object.prototype.hasOwnProperty.call(deps, key)) {
formatted = `${formatted} ${deps[key].name}: ${deps[key].versions}\n`;
}
}
window.navigator.clipboard.writeText(formatted);
}
};
/** @public */
export const InfoContent = () => {
const classes = useStyles();
const { about, loading, error } = useInfo();
if (loading) {
return <Progress />;
} else if (error) {
return <Alert severity="error">{error.message}</Alert>;
}
return (
<Box>
<Paper className={classes.paperStyle}>
<List className={classes.flexContainer}>
<ListItem>
<ListItemAvatar>
<Avatar>
<DeveloperBoardIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Operating System"
secondary={about?.operatingSystem}
/>
</ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<DescriptionIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="NodeJS Version"
secondary={about?.nodeJsVersion}
/>
</ListItem>
<ListItem>
<ListItemAvatar>
<Avatar>
<BackstageLogoIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Backstage Version"
secondary={about?.backstageVersion}
/>
</ListItem>
<Divider orientation="vertical" variant="middle" flexItem />
<ListItem
button
onClick={() => {
copyToClipboard({ about });
}}
className={classes.copyButton}
>
<ListItemAvatar>
<Avatar>
<FileCopyIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Copy Info to Clipboard" />
</ListItem>
</List>
</Paper>
<InfoDependenciesTable infoDependencies={about?.dependencies} />
</Box>
);
};
@@ -0,0 +1,55 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { Table, TableColumn } from '@backstage/core-components';
import { PackageDependency } from '@backstage/plugin-devtools-common';
import React from 'react';
const columns: TableColumn[] = [
{
title: 'Name',
width: 'auto',
field: 'name',
defaultSort: 'asc',
},
{
title: 'Versions',
width: 'auto',
field: 'versions',
},
];
export const InfoDependenciesTable = ({
infoDependencies,
}: {
infoDependencies: PackageDependency[] | undefined;
}) => {
return (
<Table
title="Package Dependencies"
options={{
paging: true,
pageSize: 15,
pageSizeOptions: [15, 30, 100],
loadingType: 'linear',
padding: 'dense',
}}
columns={columns}
data={infoDependencies || []}
/>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { InfoContent } from './InfoContent';
@@ -0,0 +1,19 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './ConfigContent';
export * from './InfoContent';
export * from './ExternalDependenciesContent';
@@ -0,0 +1,42 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 {
devToolsConfigReadPermission,
devToolsInfoReadPermission,
} from '@backstage/plugin-devtools-common';
import { ConfigContent } from '../Content/ConfigContent';
import { DevToolsLayout } from '../DevToolsLayout';
import { InfoContent } from '../Content/InfoContent';
import React from 'react';
import { RequirePermission } from '@backstage/plugin-permission-react';
/** @public */
export const DefaultDevToolsPage = () => (
<DevToolsLayout>
<DevToolsLayout.Route path="info" title="Info">
<RequirePermission permission={devToolsInfoReadPermission}>
<InfoContent />
</RequirePermission>
</DevToolsLayout.Route>
<DevToolsLayout.Route path="config" title="Config">
<RequirePermission permission={devToolsConfigReadPermission}>
<ConfigContent />
</RequirePermission>
</DevToolsLayout.Route>
</DevToolsLayout>
);
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DefaultDevToolsPage } from './DefaultDevToolsPage';
@@ -0,0 +1,79 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { Header, Page, RoutedTabs } from '@backstage/core-components';
import {
attachComponentData,
useElementFilter,
} from '@backstage/core-plugin-api';
import { TabProps } from '@material-ui/core';
import { default as React } from 'react';
/** @public */
export type SubRoute = {
path: string;
title: string;
children: JSX.Element;
tabProps?: TabProps<React.ElementType, { component?: React.ElementType }>;
};
const dataKey = 'plugin.devtools.devtoolsLayoutRoute';
const Route: (props: SubRoute) => null = () => null;
attachComponentData(Route, dataKey, true);
// This causes all mount points that are discovered within this route to use the path of the route itself
attachComponentData(Route, 'core.gatherMountPoints', true);
/** @public */
export type DevToolsLayoutProps = {
children?: React.ReactNode;
};
/**
* DevTools is a compound component, which allows you to define a custom layout
*
* @example
* ```jsx
* <DevToolsLayout>
* <DevToolsLayout.Route path="/example" title="Example tab">
* <div>This is rendered under /example/anything-here route</div>
* </DevToolsLayout.Route>
* </DevToolsLayout>
* ```
* @public
*/
export const DevToolsLayout = ({ children }: DevToolsLayoutProps) => {
const routes = useElementFilter(children, elements =>
elements
.selectByComponentData({
key: dataKey,
withStrictError:
'Child of DevToolsLayout must be an DevToolsLayout.Route',
})
.getElements<SubRoute>()
.map(child => child.props),
);
return (
<Page themeId="home">
<Header title="Backstage DevTools" />
<RoutedTabs routes={routes} />
</Page>
);
};
DevToolsLayout.Route = Route;
@@ -0,0 +1,18 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type { DevToolsLayoutProps, SubRoute } from './DevToolsLayout';
export { DevToolsLayout } from './DevToolsLayout';
@@ -0,0 +1,25 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { useOutlet } from 'react-router-dom';
import { DefaultDevToolsPage } from '../DefaultDevToolsPage';
export const DevToolsPage = () => {
const outlet = useOutlet();
return <>{outlet || <DefaultDevToolsPage />}</>;
};
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DevToolsPage } from './DevToolsPage';
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './Content';
export * from './DevToolsLayout';
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { useConfig } from './useConfig';
export { useExternalDependencies } from './useExternalDependencies';
export { useInfo } from './useInfo';
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { devToolsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
import { ConfigInfo } from '@backstage/plugin-devtools-common';
export function useConfig(): {
configInfo?: ConfigInfo;
loading: boolean;
error?: Error;
} {
const api = useApi(devToolsApiRef);
const { value, loading, error } = useAsync(() => {
return api.getConfig();
}, [api]);
return {
configInfo: value,
loading,
error,
};
}
@@ -0,0 +1,37 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { devToolsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
import { ExternalDependency } from '@backstage/plugin-devtools-common';
export function useExternalDependencies(): {
externalDependencies?: ExternalDependency[];
loading: boolean;
error?: Error;
} {
const api = useApi(devToolsApiRef);
const { value, loading, error } = useAsync(() => {
return api.getExternalDependencies();
}, [api]);
return {
externalDependencies: value,
loading,
error,
};
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { devToolsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
import { DevToolsInfo } from '@backstage/plugin-devtools-common';
export function useInfo(): {
about?: DevToolsInfo;
loading: boolean;
error?: Error;
} {
const api = useApi(devToolsApiRef);
const { value, loading, error } = useAsync(() => {
return api.getInfo();
}, [api]);
return {
about: value,
loading,
error,
};
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { devToolsPlugin, DevToolsPage } from './plugin';
export * from './components';
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { devToolsPlugin } from './plugin';
describe('devtools', () => {
it('should export plugin', () => {
expect(devToolsPlugin).toBeDefined();
});
});
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 {
createApiFactory,
createPlugin,
createRoutableExtension,
discoveryApiRef,
identityApiRef,
} from '@backstage/core-plugin-api';
import { devToolsApiRef, DevToolsClient } from './api';
import { rootRouteRef } from './routes';
/** @public */
export const devToolsPlugin = createPlugin({
id: 'devtools',
apis: [
createApiFactory({
api: devToolsApiRef,
deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
factory: ({ discoveryApi, identityApi }) =>
new DevToolsClient({ discoveryApi, identityApi }),
}),
],
routes: {
root: rootRouteRef,
},
});
/** @public */
export const DevToolsPage = devToolsPlugin.provide(
createRoutableExtension({
name: 'DevToolsPage',
component: () =>
import('./components/DevToolsPage').then(m => m.DevToolsPage),
mountPoint: rootRouteRef,
}),
);
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { createRouteRef } from '@backstage/core-plugin-api';
export const rootRouteRef = createRouteRef({
id: 'devtools',
});
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 '@testing-library/jest-dom';