Adding New Relic Dashboard

Signed-off-by: mufaddal motiwala <mufaddalmm.52@gmail.com>
This commit is contained in:
mufaddalWh
2021-11-18 13:14:16 +05:30
committed by mufaddal motiwala
parent cd646b55a9
commit 5f35e28900
30 changed files with 1181 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint')],
};
+20
View File
@@ -0,0 +1,20 @@
# New Relic Dashboard Plugin
Welcome to the new-relic-dashboard plugin!
## Features
- Adds New Relic Dashboard Pages Links to Overview section of the catalog
- Shows snapshot images of dashboards
## Getting started
This plugin uses the Backstage proxy to securely communicate with New Relic's APIs. Add the following to your app-config.yaml to enable this configuration:
```
proxy:
'/newrelic/apm/api':
target: https://api.newrelic.com/v2
headers:
X-Api-Key: ${NEW_RELIC_REST_API_KEY}
```
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2021 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 { createDevApp } from '@backstage/dev-utils';
import { newRelicDashboardPlugin, NewRelicDashboardPage } from '../src/plugin';
createDevApp()
.registerPlugin(newRelicDashboardPlugin)
.addPage({
element: <NewRelicDashboardPage />,
title: 'Root Page',
path: '/new-relic-dashboard',
})
.render();
+52
View File
@@ -0,0 +1,52 @@
{
"name": "@backstage/plugin-new-relic-dashboard",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"scripts": {
"build": "backstage-cli plugin:build",
"start": "backstage-cli plugin:serve",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"diff": "backstage-cli plugin:diff",
"prepack": "backstage-cli prepack",
"postpack": "backstage-cli postpack",
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.9.6",
"@backstage/core-components": "^0.7.2",
"@backstage/core-plugin-api": "^0.1.12",
"@backstage/plugin-catalog-react": "^0.6.3",
"@backstage/theme": "^0.2.12",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^6.0.2",
"react-use": "^17.2.4"
},
"devDependencies": {
"@backstage/cli": "^0.8.1",
"@backstage/core-app-api": "^0.1.19",
"@backstage/dev-utils": "^0.2.12",
"@backstage/test-utils": "^0.1.20",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
"@types/jest": "*",
"@types/node": "*",
"cross-fetch": "^3.0.6",
"msw": "^0.35.0"
},
"files": [
"dist"
]
}
@@ -0,0 +1,52 @@
/*
* Copyright 2021 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 { Entity } from '@backstage/catalog-model';
import { Route, Routes } from 'react-router-dom';
import React from 'react';
import { MissingAnnotationEmptyState } from '@backstage/core-components';
import { Button } from '@material-ui/core';
import { NewRelicDashboard } from './components/NewRelicDashboard';
import { useEntity } from '@backstage/plugin-catalog-react';
import { NEWRELIC_GUID } from './constants';
export const isNewRelicDashboardAvailable = (entity: Entity) =>
Boolean(entity.metadata.annotations?.[NEWRELIC_GUID]);
type Props = {
/** @deprecated The entity is now grabbed from context instead */
entity?: Entity;
};
export const Router = (_props: Props) => {
const { entity } = useEntity();
if (isNewRelicDashboardAvailable(entity)) {
return (
<Routes>
<Route path="/" element={<NewRelicDashboard />} />
</Routes>
);
}
return (
<>
<MissingAnnotationEmptyState annotation={NEWRELIC_GUID} />
<Button variant="contained" color="primary" href="#">
Read NewRelic Dashboard Plugin Docs
</Button>
</>
);
};
@@ -0,0 +1,43 @@
/*
* Copyright 2021 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 { DashboardEntity } from '../types/DashboardEntity';
import { DashboardSnapshot } from '../types/DashboardSnapshot';
import { SnapshotDetails } from '../types/SnapshotDetails';
export interface DashboardEntitySummary {
getDashboardEntity: DashboardEntity;
}
export interface DashboardSnapshotSummary {
getDashboardSnapshot: DashboardSnapshot;
}
export interface SnapshotDetailsSummary {
getSnapshotDetails: SnapshotDetails[];
}
export const NewRelicDashboardApiRef = createApiRef<NewRelicDashboardApi>({
id: 'plugin.newrelicdashboard.service',
description: 'Used by the New Relic Dashboard plugin to make requests',
});
export type NewRelicDashboardApi = {
getDashboardEntity(guid: String): Promise<DashboardEntitySummary | undefined>;
getDashboardSnapshot(
guid: String,
duration: String,
): Promise<DashboardSnapshotSummary | undefined>;
};
@@ -0,0 +1,99 @@
/*
* Copyright 2021 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 {
DashboardEntitySummary,
DashboardSnapshotSummary,
NewRelicDashboardApi,
} from './NewRelicDashboardApi';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { DashboardEntity } from '../types/DashboardEntity';
import { DashboardSnapshot } from '../types/DashboardSnapshot';
import { getDashboardParentGuidQuery } from '../queries/getDashboardParentGuidQuery';
import { getDashboardSnapshotQuery } from '../queries/getDashboardSnapshotQuery';
export class NewRelicDashboardClient implements NewRelicDashboardApi {
discoveryApi: DiscoveryApi;
baseUrl: string;
constructor({
discoveryApi,
baseUrl = 'https://api.newrelic.com/graphql/',
}: {
discoveryApi: DiscoveryApi;
baseUrl?: string;
}) {
this.discoveryApi = discoveryApi;
this.baseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
}
private async callApi<T>(
query: string,
variables: { [key in string]: any },
): Promise<T | undefined> {
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'application/json');
const graphql = JSON.stringify({
query: query,
variables: variables,
});
const requestOptions: RequestInit = {
method: 'POST',
headers: myHeaders,
body: graphql,
redirect: 'follow',
};
const apiUrl = `${await this.discoveryApi.getBaseUrl(
'proxy',
)}/newrelic/api/graphql`;
const response = await fetch(apiUrl, requestOptions);
if (response.status === 200) {
return (await response.json()) as T;
}
return undefined;
}
async getDashboardEntity(
guid: String,
): Promise<DashboardEntitySummary | undefined> {
// let query = "parentId ='"+guid+"'"
// query = `parentId ='${guid}'`
const DashboardEntityList = await this.callApi<DashboardEntity>(
getDashboardParentGuidQuery,
{
query: `parentId ='${guid}'`,
},
);
return {
getDashboardEntity: DashboardEntityList!,
};
}
async getDashboardSnapshot(
guid: String,
duration: String,
): Promise<DashboardSnapshotSummary | undefined> {
const DashboardSnapshotValue = await this.callApi<DashboardSnapshot>(
getDashboardSnapshotQuery,
{
guid: guid,
duration: duration,
},
);
return {
getDashboardSnapshot: DashboardSnapshotValue!,
};
}
}
@@ -0,0 +1,18 @@
/*
* Copyright 2021 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 { NewRelicDashboardApi } from './NewRelicDashboardApi';
export { NewRelicDashboardApiRef } from './NewRelicDashboardApi';
export { NewRelicDashboardClient } from './NewRelicDashboardClient';
@@ -0,0 +1,49 @@
/*
* Copyright 2021 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 { ExampleComponent } from './ExampleComponent';
import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import {
setupRequestMockHandlers,
renderInTestApp,
} from '@backstage/test-utils';
describe('ExampleComponent', () => {
const server = setupServer();
// Enable sane handlers for network requests
setupRequestMockHandlers(server);
// setup mock response
beforeEach(() => {
server.use(
rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))),
);
});
it('should render', async () => {
const rendered = await renderInTestApp(
<ThemeProvider theme={lightTheme}>
<ExampleComponent />
</ThemeProvider>,
);
expect(
rendered.getByText('Welcome to new-relic-dashboard!'),
).toBeInTheDocument();
});
});
@@ -0,0 +1,56 @@
/*
* Copyright 2021 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 { Typography, Grid } from '@material-ui/core';
import {
InfoCard,
Header,
Page,
Content,
ContentHeader,
HeaderLabel,
SupportButton,
} from '@backstage/core-components';
import { NewRelicExample } from './NewRelicExample';
export const ExampleComponent = () => (
<Page themeId="tool">
<Header
title="Welcome to new-relic-dashboard!"
subtitle="View all dashboards at a glance"
>
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="New Relic Plugin">
<SupportButton>This page will load a list of dashboards</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Information card">
<Typography variant="body1">
All content should be wrapped in a card like this.
</Typography>
</InfoCard>
</Grid>
<Grid item>
<NewRelicExample />
</Grid>
</Grid>
</Content>
</Page>
);
@@ -0,0 +1,65 @@
/*
* Copyright 2021 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 { Typography, Grid } from '@material-ui/core';
export const NewRelicExample = () => {
// const [dashboardUrl, setDashboardUrl] = React.useState<
// DashboardSnapshotSummary | undefined
// >();
// const NewRelicDashboardAPI = useApi(NewRelicDashboardApiRef);
// const { value } = useAsync(async (): Promise<any> => {
// let dashboardObject: any = NewRelicDashboardAPI.getDashboardEntity(
// 'MjY2NjU3MnxWSVp8REFTSEJPQVJEfGRhOjQ0OTc1MA',
// );
// return dashboardObject;
// }, []);
// useEffect(() => {
// if (value) {
// NewRelicDashboardAPI.getDashboardSnapshot(
// value?.getDashboardEntity?.data?.actor.entitySearch.results.entities[0]
// .guid,
// '2000000000',
// );
// }
// }, [value]);
// console.log(
// value?.getDashboardEntity.data.actor.entitySearch.results.entities[0].guid,
// );
return (
<Grid container spacing={3} direction="column">
<Typography variant="h4">
New Relic Dashboards loaded via custom APIs
</Typography>
<Grid item>
{/* {data.getTodos.map((todo: ITodo) => ( */}
{/* <>{JSON.stringify(value)}</>
<>{JSON.stringify(dashboardUrl?.getDashboardSnapshot.data.dashboardCreateSnapshotUrl)}</> */}
{/* {value?.getDashboardSnapshot?.map(contributor => (
<Grid item>
<>{contributor}</>
</Grid>
))} */}
{/* <img src={String(dashboardUrl?.dashboardCreateSnapshotUrl)}></img> */}
{/* ))} */}
</Grid>
</Grid>
);
};
@@ -0,0 +1,16 @@
/*
* Copyright 2021 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 { ExampleComponent } from './ExampleComponent';
@@ -0,0 +1,80 @@
/*
* Copyright 2021 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 { Box, Grid, makeStyles, Typography } from '@material-ui/core';
import { NewRelicDashboardApiRef } from '../../api';
import { useApi } from '@backstage/core-plugin-api';
import { useAsync } from 'react-use';
import { Progress, InfoCard, Link } from '@backstage/core-components';
import Alert from '@material-ui/lab/Alert';
import DesktopMac from '@material-ui/icons/DesktopMac';
import { useNewRelicDashboardEntity } from '../../hooks';
const useStyles = makeStyles({
svgIcon: {
display: 'inline-block',
'& svg': {
display: 'inline-block',
fontSize: 'inherit',
verticalAlign: 'baseline',
},
},
});
export const DashboardEntityList = () => {
const DashboardEntity = useNewRelicDashboardEntity();
const classes = useStyles();
const NewRelicDashboardAPI = useApi(NewRelicDashboardApiRef);
const { value, loading, error } = useAsync(async (): Promise<any> => {
const dashboardObject: any = NewRelicDashboardAPI.getDashboardEntity(
String(DashboardEntity?.integrationKey),
);
return dashboardObject;
}, []);
if (loading) {
return <Progress />;
}
if (error) {
return <Alert severity="error">{error.message}</Alert>;
}
return (
<Grid container spacing={3} direction="column">
<Grid item xs={12}>
<InfoCard title="New Relic Dashboard Pages" variant="gridItem">
{value.getDashboardEntity.data.actor.entitySearch.results.entities.map(
(entity: any) => {
return (
<Box style={{ margin: '10px' }} display="flex">
<Box mr={1} className={classes.svgIcon}>
<Typography component="div">
<DesktopMac />
</Typography>
</Box>
<Box flexGrow="1">
<Link to={entity.permalink} target="_blank" rel="noopener">
{entity.name}
</Link>
</Box>
</Box>
);
},
)}
</InfoCard>
</Grid>
</Grid>
);
};
@@ -0,0 +1,77 @@
/*
* Copyright 2021 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 { Grid, Typography } from '@material-ui/core';
import { useApi } from '@backstage/core-plugin-api';
import { useAsync } from 'react-use';
import { Progress } from '@backstage/core-components';
import { NewRelicDashboardApiRef } from '../../../api';
import Alert from '@material-ui/lab/Alert';
type Props = {
guid: String;
name: String;
permalink: string;
duration: String;
};
export const DashboardSnapshot = ({
guid,
name,
permalink,
duration,
}: Props) => {
const NewRelicDashboardAPI = useApi(NewRelicDashboardApiRef);
const { value, loading, error } = useAsync(async (): Promise<any> => {
const dashboardObject: any = NewRelicDashboardAPI.getDashboardSnapshot(
guid,
duration,
);
return dashboardObject;
}, []);
if (loading) {
return <Progress />;
}
if (error) {
return <Alert severity="error">{error.message}</Alert>;
}
return (
<Grid container spacing={3} direction="column">
<Grid item>
<Typography variant="h3">{name}</Typography>
</Grid>
<Grid item xs={8}>
<a target="_blank" href={permalink}>
<img
alt={`${name} Dashbord`}
style={{ border: 'solid 1px black' }}
src={value?.getDashboardSnapshot?.data?.dashboardCreateSnapshotUrl?.replace(
/...$/,
'png',
)}
/>
</a>
{/* {JSON.stringify(
value.getDashboardSnapshot.data.dashboardCreateSnapshotUrl.replace(
/...$/,
'png',
),
)} */}
</Grid>
</Grid>
);
};
@@ -0,0 +1,157 @@
/*
* Copyright 2021 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 { Grid, Tab, Tabs, makeStyles } from '@material-ui/core';
import { NewRelicDashboardApiRef } from '../../../api';
import { useApi } from '@backstage/core-plugin-api';
import { useAsync } from 'react-use';
import { Progress } from '@backstage/core-components';
import Alert from '@material-ui/lab/Alert';
import { DashboardSnapshot } from './DashboardSnapshot';
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value1: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value1, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value1 !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value1 === index && children}
</div>
);
}
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
type Props = {
guid: String;
};
const useStyles = makeStyles(
theme => ({
tabsWrapper: {
gridArea: 'pageSubheader',
backgroundColor: theme.palette.background.paper,
paddingLeft: theme.spacing(3),
},
defaultTab: {
padding: theme.spacing(3, 3),
...theme.typography.caption,
textTransform: 'uppercase',
fontWeight: 'bold',
color: theme.palette.text.secondary,
},
selected: {
color: theme.palette.text.primary,
},
tabRoot: {
'&:hover': {
backgroundColor: theme.palette.background.default,
color: theme.palette.text.primary,
},
},
}),
{ name: 'DashboardHeaderTabs' },
);
export const DashboardSnapshotList = ({ guid }: Props) => {
const styles = useStyles();
const NewRelicDashboardAPI = useApi(NewRelicDashboardApiRef);
const { value, loading, error } = useAsync(async (): Promise<any> => {
const dashboardObject: any = NewRelicDashboardAPI.getDashboardEntity(guid);
return dashboardObject;
}, []);
const [value1, setValue1] = React.useState(0);
const handleChange = ({}: React.ChangeEvent<{}>, newValue: number) => {
setValue1(newValue);
};
if (loading) {
return <Progress />;
}
if (error) {
return <Alert severity="error">{error.message}</Alert>;
}
return (
<>
<Grid container direction="column">
{/* <Grid item xs={8}>
{value.getDashboardEntity.data.actor.entitySearch.results.entities?.map(
(Entity: any) => {
return (
<DashboardSnapshot
name={Entity.name}
permalink={Entity.permalink}
guid={Entity.guid}
/>
);
},
)}
</Grid> */}
</Grid>
<Tabs
selectionFollowsFocus
indicatorColor="primary"
textColor="inherit"
variant="scrollable"
scrollButtons="auto"
aria-label="scrollable auto tabs example"
onChange={handleChange}
value={value1}
>
{value.getDashboardEntity.data.actor.entitySearch.results.entities?.map(
(Entity: any, index: any) => {
return (
<Tab
label={Entity.name}
className={styles.defaultTab}
classes={{
selected: styles.selected,
root: styles.tabRoot,
}}
{...a11yProps(index)}
/>
);
},
)}
</Tabs>
{value.getDashboardEntity.data.actor.entitySearch.results.entities?.map(
(Entity: any, index: any) => {
return (
<TabPanel value1={value1} index={index}>
<DashboardSnapshot
name={Entity.name}
permalink={Entity.permalink}
guid={Entity.guid}
duration="2000000000"
/>
</TabPanel>
);
},
)}
</>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2021 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 { DashboardSnapshot } from './DashboardSnapshot';
export { DashboardSnapshotList } from './DashboardSnapshotList';
@@ -0,0 +1,35 @@
/*
* Copyright 2021 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 { Grid } from '@material-ui/core';
import { Page, Content } from '@backstage/core-components';
import { useNewRelicDashboardEntity } from '../../hooks';
import { DashboardEntityList } from './DashboardEntityList';
import { DashboardSnapshotList } from './DashboardSnapshot';
export const NewRelicDashboard = () => {
const entity = useNewRelicDashboardEntity();
return (
<Page themeId="home">
<Content>
<Grid container spacing={6} direction="row" alignItems="stretch">
<DashboardEntityList />
<DashboardSnapshotList guid={String(entity.integrationKey)} />
</Grid>
</Content>
</Page>
);
};
@@ -0,0 +1,16 @@
/*
* Copyright 2021 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 { NewRelicDashboard } from './NewRelicDashboard';
@@ -0,0 +1,16 @@
/*
* Copyright 2021 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 const NEWRELIC_GUID = 'newrelic.com/dashboard-guid';
@@ -0,0 +1,27 @@
/*
* Copyright 2021 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 { useEntity } from '@backstage/plugin-catalog-react';
import { NEWRELIC_GUID } from '../constants';
export function useNewRelicDashboardEntity() {
const { entity } = useEntity();
const integrationKey: string | undefined =
entity.metadata.annotations?.[NEWRELIC_GUID];
const name: string | undefined = entity.metadata.name;
return { integrationKey, name };
}
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2021 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 {
newRelicDashboardPlugin,
NewRelicDashboardPage,
EntityNewRelicDashboard,
EntityPageNewRelicDashboard,
} from './plugin';
export { isNewRelicDashboardAvailable } from './Router';
@@ -0,0 +1,22 @@
/*
* Copyright 2021 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 { newRelicDashboardPlugin } from './plugin';
describe('new-relic-dashboard', () => {
it('should export plugin', () => {
expect(newRelicDashboardPlugin).toBeDefined();
});
});
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright 2021 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 {
createPlugin,
createRoutableExtension,
configApiRef,
createApiFactory,
discoveryApiRef,
} from '@backstage/core-plugin-api';
import { NewRelicDashboardApiRef, NewRelicDashboardClient } from './api';
import { rootRouteRef } from './routes';
export const newRelicDashboardPlugin = createPlugin({
id: 'new-relic-dashboard',
routes: {
root: rootRouteRef,
},
apis: [
createApiFactory({
api: NewRelicDashboardApiRef,
deps: { configApi: configApiRef, discoveryApi: discoveryApiRef },
factory: ({ configApi, discoveryApi }) =>
new NewRelicDashboardClient({
discoveryApi,
baseUrl: configApi.getOptionalString('newrelicdashboard.baseUrl'),
}),
}),
],
});
export const NewRelicDashboardPage = newRelicDashboardPlugin.provide(
createRoutableExtension({
name: 'NewRelicDashboardPage',
component: () =>
import('./components/ExampleComponents').then(m => m.ExampleComponent),
mountPoint: rootRouteRef,
}),
);
export const EntityNewRelicDashboard = newRelicDashboardPlugin.provide(
createRoutableExtension({
component: () => import('./Router').then(m => m.Router),
mountPoint: rootRouteRef,
}),
);
export const EntityPageNewRelicDashboard = newRelicDashboardPlugin.provide(
createRoutableExtension({
component: () =>
import('./components/NewRelicDashboard/DashboardEntityList').then(
m => m.DashboardEntityList,
),
mountPoint: rootRouteRef,
}),
);
@@ -0,0 +1,17 @@
/*
* Copyright 2021 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 const getDashboardParentGuidQuery =
'query ($query: String) {\n actor {\n entitySearch(query: $query) {\n results {\n entities {\n name\n ... on DashboardEntityOutline {\n name\n guid\n }\n permalink\n }\n }\n }\n }\n}\n';
@@ -0,0 +1,17 @@
/*
* Copyright 2021 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 const getDashboardSnapshotQuery =
'mutation($guid: EntityGuid!) {\n dashboardCreateSnapshotUrl(guid: $guid , params: {timeWindow: {duration: $duration}})\n}';
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2021 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({
title: 'new-relic-dashboard',
});
@@ -0,0 +1,17 @@
/*
* Copyright 2021 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';
import 'cross-fetch/polyfill';
@@ -0,0 +1,32 @@
/*
* Copyright 2021 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 DashboardEntity = {
data: {
actor: {
entitySearch: {
results: {
entities: [
{
guid: string;
permalink: string;
},
];
};
};
};
};
};
@@ -0,0 +1,21 @@
/*
* Copyright 2021 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 DashboardSnapshot = {
data: {
dashboardCreateSnapshotUrl: string;
};
};
@@ -0,0 +1,20 @@
/*
* Copyright 2021 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 SnapshotDetails = {
dashboardSnapshotUrl: string;
permalink: string;
};