diff --git a/plugins/new-relic-dashboard/.eslintrc.js b/plugins/new-relic-dashboard/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/new-relic-dashboard/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/new-relic-dashboard/README.md b/plugins/new-relic-dashboard/README.md new file mode 100644 index 0000000000..624b839be6 --- /dev/null +++ b/plugins/new-relic-dashboard/README.md @@ -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} +``` diff --git a/plugins/new-relic-dashboard/dev/index.tsx b/plugins/new-relic-dashboard/dev/index.tsx new file mode 100644 index 0000000000..98bc0ba79a --- /dev/null +++ b/plugins/new-relic-dashboard/dev/index.tsx @@ -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: , + title: 'Root Page', + path: '/new-relic-dashboard', + }) + .render(); diff --git a/plugins/new-relic-dashboard/package.json b/plugins/new-relic-dashboard/package.json new file mode 100644 index 0000000000..bcb73e616d --- /dev/null +++ b/plugins/new-relic-dashboard/package.json @@ -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" + ] +} diff --git a/plugins/new-relic-dashboard/src/Router.tsx b/plugins/new-relic-dashboard/src/Router.tsx new file mode 100644 index 0000000000..6d8340505b --- /dev/null +++ b/plugins/new-relic-dashboard/src/Router.tsx @@ -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 ( + + } /> + + ); + } + + return ( + <> + + + + ); +}; diff --git a/plugins/new-relic-dashboard/src/api/NewRelicDashboardApi.ts b/plugins/new-relic-dashboard/src/api/NewRelicDashboardApi.ts new file mode 100644 index 0000000000..7ec0c5623a --- /dev/null +++ b/plugins/new-relic-dashboard/src/api/NewRelicDashboardApi.ts @@ -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({ + id: 'plugin.newrelicdashboard.service', + description: 'Used by the New Relic Dashboard plugin to make requests', +}); + +export type NewRelicDashboardApi = { + getDashboardEntity(guid: String): Promise; + getDashboardSnapshot( + guid: String, + duration: String, + ): Promise; +}; diff --git a/plugins/new-relic-dashboard/src/api/NewRelicDashboardClient.ts b/plugins/new-relic-dashboard/src/api/NewRelicDashboardClient.ts new file mode 100644 index 0000000000..20ff3962af --- /dev/null +++ b/plugins/new-relic-dashboard/src/api/NewRelicDashboardClient.ts @@ -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( + query: string, + variables: { [key in string]: any }, + ): Promise { + 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 { + // let query = "parentId ='"+guid+"'" + // query = `parentId ='${guid}'` + const DashboardEntityList = await this.callApi( + getDashboardParentGuidQuery, + { + query: `parentId ='${guid}'`, + }, + ); + return { + getDashboardEntity: DashboardEntityList!, + }; + } + + async getDashboardSnapshot( + guid: String, + duration: String, + ): Promise { + const DashboardSnapshotValue = await this.callApi( + getDashboardSnapshotQuery, + { + guid: guid, + duration: duration, + }, + ); + return { + getDashboardSnapshot: DashboardSnapshotValue!, + }; + } +} diff --git a/plugins/new-relic-dashboard/src/api/index.ts b/plugins/new-relic-dashboard/src/api/index.ts new file mode 100644 index 0000000000..a210967052 --- /dev/null +++ b/plugins/new-relic-dashboard/src/api/index.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/components/ExampleComponents/ExampleComponent.test.tsx b/plugins/new-relic-dashboard/src/components/ExampleComponents/ExampleComponent.test.tsx new file mode 100644 index 0000000000..22cb0c9fca --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/ExampleComponents/ExampleComponent.test.tsx @@ -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( + + + , + ); + expect( + rendered.getByText('Welcome to new-relic-dashboard!'), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/new-relic-dashboard/src/components/ExampleComponents/ExampleComponent.tsx b/plugins/new-relic-dashboard/src/components/ExampleComponents/ExampleComponent.tsx new file mode 100644 index 0000000000..5d7d84845d --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/ExampleComponents/ExampleComponent.tsx @@ -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 = () => ( + +
+ + +
+ + + This page will load a list of dashboards + + + + + + All content should be wrapped in a card like this. + + + + + + + + +
+); diff --git a/plugins/new-relic-dashboard/src/components/ExampleComponents/NewRelicExample.tsx b/plugins/new-relic-dashboard/src/components/ExampleComponents/NewRelicExample.tsx new file mode 100644 index 0000000000..162dc3e42e --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/ExampleComponents/NewRelicExample.tsx @@ -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 => { + // 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 ( + + + New Relic Dashboards loaded via custom APIs + + + {/* {data.getTodos.map((todo: ITodo) => ( */} + {/* <>{JSON.stringify(value)} + <>{JSON.stringify(dashboardUrl?.getDashboardSnapshot.data.dashboardCreateSnapshotUrl)} */} + {/* {value?.getDashboardSnapshot?.map(contributor => ( + + <>{contributor} + + ))} */} + {/* */} + {/* ))} */} + + + ); +}; diff --git a/plugins/new-relic-dashboard/src/components/ExampleComponents/index.ts b/plugins/new-relic-dashboard/src/components/ExampleComponents/index.ts new file mode 100644 index 0000000000..212740dcdb --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/ExampleComponents/index.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardEntityList.tsx b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardEntityList.tsx new file mode 100644 index 0000000000..5fc17e4fd4 --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardEntityList.tsx @@ -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 => { + const dashboardObject: any = NewRelicDashboardAPI.getDashboardEntity( + String(DashboardEntity?.integrationKey), + ); + return dashboardObject; + }, []); + if (loading) { + return ; + } + if (error) { + return {error.message}; + } + return ( + + + + {value.getDashboardEntity.data.actor.entitySearch.results.entities.map( + (entity: any) => { + return ( + + + + + + + + + {entity.name} + + + + ); + }, + )} + + + + ); +}; diff --git a/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/DashboardSnapshot.tsx b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/DashboardSnapshot.tsx new file mode 100644 index 0000000000..dd2f4622c6 --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/DashboardSnapshot.tsx @@ -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 => { + const dashboardObject: any = NewRelicDashboardAPI.getDashboardSnapshot( + guid, + duration, + ); + return dashboardObject; + }, []); + if (loading) { + return ; + } + if (error) { + return {error.message}; + } + return ( + + + {name} + + + + + {`${name} + + {/* {JSON.stringify( + value.getDashboardSnapshot.data.dashboardCreateSnapshotUrl.replace( + /...$/, + 'png', + ), + )} */} + + + ); +}; diff --git a/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/DashboardSnapshotList.tsx b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/DashboardSnapshotList.tsx new file mode 100644 index 0000000000..78684f7a55 --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/DashboardSnapshotList.tsx @@ -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 ( + + ); +} +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 => { + 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 ; + } + if (error) { + return {error.message}; + } + return ( + <> + + {/* + {value.getDashboardEntity.data.actor.entitySearch.results.entities?.map( + (Entity: any) => { + return ( + + ); + }, + )} + */} + + + {value.getDashboardEntity.data.actor.entitySearch.results.entities?.map( + (Entity: any, index: any) => { + return ( + + ); + }, + )} + + {value.getDashboardEntity.data.actor.entitySearch.results.entities?.map( + (Entity: any, index: any) => { + return ( + + + + ); + }, + )} + + ); +}; diff --git a/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/index.ts b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/index.ts new file mode 100644 index 0000000000..6b17582515 --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/DashboardSnapshot/index.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/components/NewRelicDashboard/NewRelicDashboard.tsx b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/NewRelicDashboard.tsx new file mode 100644 index 0000000000..9cc1764b99 --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/NewRelicDashboard.tsx @@ -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 ( + + + + + + + + + ); +}; diff --git a/plugins/new-relic-dashboard/src/components/NewRelicDashboard/index.ts b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/index.ts new file mode 100644 index 0000000000..668d76a059 --- /dev/null +++ b/plugins/new-relic-dashboard/src/components/NewRelicDashboard/index.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/constants.ts b/plugins/new-relic-dashboard/src/constants.ts new file mode 100644 index 0000000000..fd83a72132 --- /dev/null +++ b/plugins/new-relic-dashboard/src/constants.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/hooks/index.ts b/plugins/new-relic-dashboard/src/hooks/index.ts new file mode 100644 index 0000000000..3739607b82 --- /dev/null +++ b/plugins/new-relic-dashboard/src/hooks/index.ts @@ -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 }; +} diff --git a/plugins/new-relic-dashboard/src/index.ts b/plugins/new-relic-dashboard/src/index.ts new file mode 100644 index 0000000000..0c02a223d1 --- /dev/null +++ b/plugins/new-relic-dashboard/src/index.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/plugin.test.ts b/plugins/new-relic-dashboard/src/plugin.test.ts new file mode 100644 index 0000000000..e5eccd930b --- /dev/null +++ b/plugins/new-relic-dashboard/src/plugin.test.ts @@ -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(); + }); +}); diff --git a/plugins/new-relic-dashboard/src/plugin.ts b/plugins/new-relic-dashboard/src/plugin.ts new file mode 100644 index 0000000000..cca9cbd7e1 --- /dev/null +++ b/plugins/new-relic-dashboard/src/plugin.ts @@ -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, + }), +); diff --git a/plugins/new-relic-dashboard/src/queries/getDashboardParentGuidQuery.ts b/plugins/new-relic-dashboard/src/queries/getDashboardParentGuidQuery.ts new file mode 100644 index 0000000000..3a06384539 --- /dev/null +++ b/plugins/new-relic-dashboard/src/queries/getDashboardParentGuidQuery.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/queries/getDashboardSnapshotQuery.ts b/plugins/new-relic-dashboard/src/queries/getDashboardSnapshotQuery.ts new file mode 100644 index 0000000000..7f96bf46ad --- /dev/null +++ b/plugins/new-relic-dashboard/src/queries/getDashboardSnapshotQuery.ts @@ -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}'; diff --git a/plugins/new-relic-dashboard/src/routes.ts b/plugins/new-relic-dashboard/src/routes.ts new file mode 100644 index 0000000000..733cdfb571 --- /dev/null +++ b/plugins/new-relic-dashboard/src/routes.ts @@ -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', +}); diff --git a/plugins/new-relic-dashboard/src/setupTests.ts b/plugins/new-relic-dashboard/src/setupTests.ts new file mode 100644 index 0000000000..fc6dbd98f8 --- /dev/null +++ b/plugins/new-relic-dashboard/src/setupTests.ts @@ -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'; diff --git a/plugins/new-relic-dashboard/src/types/DashboardEntity.ts b/plugins/new-relic-dashboard/src/types/DashboardEntity.ts new file mode 100644 index 0000000000..93bcc24bd9 --- /dev/null +++ b/plugins/new-relic-dashboard/src/types/DashboardEntity.ts @@ -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; + }, + ]; + }; + }; + }; + }; +}; diff --git a/plugins/new-relic-dashboard/src/types/DashboardSnapshot.ts b/plugins/new-relic-dashboard/src/types/DashboardSnapshot.ts new file mode 100644 index 0000000000..6c11c6f6d7 --- /dev/null +++ b/plugins/new-relic-dashboard/src/types/DashboardSnapshot.ts @@ -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; + }; +}; diff --git a/plugins/new-relic-dashboard/src/types/SnapshotDetails.ts b/plugins/new-relic-dashboard/src/types/SnapshotDetails.ts new file mode 100644 index 0000000000..e189ac52c4 --- /dev/null +++ b/plugins/new-relic-dashboard/src/types/SnapshotDetails.ts @@ -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; +};