diff --git a/.changeset/modern-planets-fry.md b/.changeset/modern-planets-fry.md
new file mode 100644
index 0000000000..2da39473a1
--- /dev/null
+++ b/.changeset/modern-planets-fry.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-newrelic-dashboard': patch
+---
+
+Created a New Plugin , which imports dashboard links and snapshots from New Relic
diff --git a/app-config.yaml b/app-config.yaml
index 6cd05f44e8..2837d97d2f 100644
--- a/app-config.yaml
+++ b/app-config.yaml
@@ -74,6 +74,11 @@ proxy:
headers:
X-Api-Key: ${NEW_RELIC_REST_API_KEY}
+ '/newrelic/api':
+ target: https://api.newrelic.com
+ headers:
+ X-Api-Key: ${NEW_RELIC_USER_KEY}
+
'/pagerduty':
target: https://api.pagerduty.com
headers:
diff --git a/packages/app/package.json b/packages/app/package.json
index a9e8e8ddc8..1f609858f6 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -33,6 +33,7 @@
"@backstage/plugin-kubernetes": "^0.5.1",
"@backstage/plugin-lighthouse": "^0.2.31",
"@backstage/plugin-newrelic": "^0.3.10",
+ "@backstage/plugin-newrelic-dashboard": "^0.1.0",
"@backstage/plugin-org": "^0.3.31",
"@backstage/plugin-pagerduty": "0.3.19",
"@backstage/plugin-rollbar": "^0.3.20",
diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index a7de64e89a..455cd188fb 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -124,6 +124,12 @@ import {
EntityTravisCIOverviewCard,
isTravisciAvailable,
} from '@roadiehq/backstage-plugin-travis-ci';
+import {
+ isNewRelicDashboardAvailable,
+ EntityNewRelicDashboardContent,
+ EntityNewRelicDashboardCard,
+} from '@backstage/plugin-newrelic-dashboard';
+
import React, { ReactNode, useMemo, useState } from 'react';
const customEntityFilterKind = ['Component', 'API', 'System'];
@@ -291,6 +297,14 @@ const overviewContent = (
+
+
+
+
+
+
+
+
@@ -371,6 +385,14 @@ const serviceEntityPage = (
+
+
+
+
@@ -429,6 +451,13 @@ const websiteEntityPage = (
+
+
+
diff --git a/plugins/newrelic-dashboard/.eslintrc.js b/plugins/newrelic-dashboard/.eslintrc.js
new file mode 100644
index 0000000000..13573efa9c
--- /dev/null
+++ b/plugins/newrelic-dashboard/.eslintrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: [require.resolve('@backstage/cli/config/eslint')],
+};
diff --git a/plugins/newrelic-dashboard/README.md b/plugins/newrelic-dashboard/README.md
new file mode 100644
index 0000000000..a31042b3fe
--- /dev/null
+++ b/plugins/newrelic-dashboard/README.md
@@ -0,0 +1,79 @@
+# New Relic Dashboard Plugin
+
+Welcome to the newrelic-dashboard plugin!
+
+## Features
+
+- Adds New Relic Dashboard Pages Links to Overview section of the catalog
+- Shows Snapshots of dashboards in New Relic
+
+## Getting started
+
+This plugin uses the Backstage proxy to securely communicate with New Relic's APIs. We use NerdGraph (New Relic's GraphQL API)
+
+To generate a New Relic API Key , you can visit this [link](https://one.newrelic.com/launcher/api-keys-ui.api-keys-launcher)
+
+1. Add the following to your app-config.yaml to enable this configuration:
+
+```
+// app-config.yaml
+proxy:
+ '/newrelic/api':
+ target: https://api.newrelic.com
+ headers:
+ X-Api-Key: ${NEW_RELIC_USER_KEY}
+```
+
+2. Add the following to `EntityPage.tsx` to display New Relic Dashboard Tab
+
+```
+// In packages/app/src/components/catalog/EntityPage.tsx
+import {
+ isNewRelicDashboardAvailable,
+ EntityNewRelicDashboardContent,
+ EntityNewRelicDashboardCard,
+} from '@backstage/plugin-newrelic-dashboard';
+
+const serviceEntityPage = (
+
+ {/* other tabs... */}
+
+
+
+```
+
+3. Add the following in `EntityPage.tsx` to display dashboard links in overview page
+
+```
+const overviewContent = (
+ {/* other tabs... */}
+
+
+
+
+
+
+
+```
+
+4. Add `newrelic.com/dashboard-guid` annotation in catalog descriptor file
+
+To Obtain the dashboard's GUID: Click the info icon by the dashboard's name to access the See metadata and manage tags modal and see the dashboard's GUID.
+
+```
+// catalog-info.yaml
+apiVersion: backstage.io/v1alpha1
+kind: Component
+metadata:
+ # ...
+ annotations:
+ newrelic.com/dashboard-guid:
+spec:
+ type: service
+```
+
+All set , you will be able to see the plugin in action!
diff --git a/plugins/newrelic-dashboard/api-report.md b/plugins/newrelic-dashboard/api-report.md
new file mode 100644
index 0000000000..ffdd5a2833
--- /dev/null
+++ b/plugins/newrelic-dashboard/api-report.md
@@ -0,0 +1,38 @@
+## API Report File for "@backstage/plugin-newrelic-dashboard"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+///
+
+import { BackstagePlugin } from '@backstage/core-plugin-api';
+import { Entity } from '@backstage/catalog-model';
+import { RouteRef } from '@backstage/core-plugin-api';
+
+// Warning: (ae-missing-release-tag) "EntityNewRelicDashboardCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const EntityNewRelicDashboardCard: () => JSX.Element;
+
+// Warning: (ae-missing-release-tag) "EntityNewRelicDashboardContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const EntityNewRelicDashboardContent: () => JSX.Element;
+
+// Warning: (ae-missing-release-tag) "isNewRelicDashboardAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const isNewRelicDashboardAvailable: (entity: Entity) => boolean;
+
+// Warning: (ae-missing-release-tag) "newRelicDashboardPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const newRelicDashboardPlugin: BackstagePlugin<
+ {
+ root: RouteRef;
+ },
+ {}
+>;
+
+// (No @packageDocumentation comment for this package)
+```
diff --git a/plugins/newrelic-dashboard/dev/index.tsx b/plugins/newrelic-dashboard/dev/index.tsx
new file mode 100644
index 0000000000..d0db8c4d96
--- /dev/null
+++ b/plugins/newrelic-dashboard/dev/index.tsx
@@ -0,0 +1,19 @@
+/*
+ * 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 { createDevApp } from '@backstage/dev-utils';
+import { newRelicDashboardPlugin } from './../src/plugin';
+
+createDevApp().registerPlugin(newRelicDashboardPlugin).render();
diff --git a/plugins/newrelic-dashboard/package.json b/plugins/newrelic-dashboard/package.json
new file mode 100644
index 0000000000..3da22d1c9e
--- /dev/null
+++ b/plugins/newrelic-dashboard/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@backstage/plugin-newrelic-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.8",
+ "@backstage/core-components": "^0.8.1",
+ "@backstage/core-plugin-api": "^0.3.1",
+ "@backstage/errors": "^0.1.5",
+ "@backstage/plugin-catalog-react": "^0.6.7",
+ "@material-ui/core": "^4.12.2",
+ "@material-ui/icons": "^4.9.1",
+ "@material-ui/lab": "4.0.0-alpha.57",
+ "react-use": "^17.2.4"
+ },
+ "devDependencies": {
+ "@backstage/cli": "^0.10.2",
+ "@backstage/dev-utils": "^0.2.14",
+ "@testing-library/jest-dom": "^5.10.1",
+ "@types/react": "^16.13.1 || ^17.0.0",
+ "cross-fetch": "^3.0.6"
+ },
+ "files": [
+ "dist"
+ ],
+ "peerDependencies": {
+ "react": "^16.13.1 || ^17.0.0"
+ }
+}
diff --git a/plugins/newrelic-dashboard/src/Router.tsx b/plugins/newrelic-dashboard/src/Router.tsx
new file mode 100644
index 0000000000..24a5a1037f
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/Router.tsx
@@ -0,0 +1,46 @@
+/*
+ * 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 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_ANNOTATION } from './constants';
+
+export const isNewRelicDashboardAvailable = (entity: Entity) =>
+ Boolean(entity?.metadata?.annotations?.[NEWRELIC_GUID_ANNOTATION]);
+
+export const Router = () => {
+ const { entity } = useEntity();
+
+ if (isNewRelicDashboardAvailable(entity)) {
+ return ;
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/plugins/newrelic-dashboard/src/api/NewRelicDashboardApi.ts b/plugins/newrelic-dashboard/src/api/NewRelicDashboardApi.ts
new file mode 100644
index 0000000000..48c7a44c3a
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/api/NewRelicDashboardApi.ts
@@ -0,0 +1,42 @@
+/*
+ * 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',
+});
+
+export type NewRelicDashboardApi = {
+ getDashboardEntity(guid: string): Promise;
+ getDashboardSnapshot(
+ guid: string,
+ duration: number,
+ ): Promise;
+};
diff --git a/plugins/newrelic-dashboard/src/api/NewRelicDashboardClient.ts b/plugins/newrelic-dashboard/src/api/NewRelicDashboardClient.ts
new file mode 100644
index 0000000000..b79cceeeda
--- /dev/null
+++ b/plugins/newrelic-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';
+import { ResponseError } from '@backstage/errors';
+
+export class NewRelicDashboardClient implements NewRelicDashboardApi {
+ private readonly discoveryApi: DiscoveryApi;
+ constructor({
+ discoveryApi,
+ }: {
+ discoveryApi: DiscoveryApi;
+ baseUrl?: string;
+ }) {
+ this.discoveryApi = discoveryApi;
+ }
+
+ private async callApi(
+ query: string,
+ variables: { [key in string]: string | number },
+ ): 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;
+ }
+ if (!response.ok) {
+ throw await ResponseError.fromResponse(response);
+ } else {
+ return undefined;
+ }
+ }
+
+ async getDashboardEntity(
+ guid: string,
+ ): Promise {
+ const DashboardEntityList = await this.callApi(
+ getDashboardParentGuidQuery,
+ {
+ query: `parentId ='${guid}'`,
+ },
+ );
+ return {
+ getDashboardEntity: DashboardEntityList!,
+ };
+ }
+
+ async getDashboardSnapshot(
+ guid: string,
+ duration: number,
+ ): Promise {
+ const DashboardSnapshotValue = await this.callApi(
+ getDashboardSnapshotQuery,
+ {
+ guid: guid,
+ duration: duration,
+ },
+ );
+ return {
+ getDashboardSnapshot: DashboardSnapshotValue!,
+ };
+ }
+}
diff --git a/plugins/newrelic-dashboard/src/api/index.ts b/plugins/newrelic-dashboard/src/api/index.ts
new file mode 100644
index 0000000000..9b14160bcf
--- /dev/null
+++ b/plugins/newrelic-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/newrelic-dashboard/src/components/NewRelicDashboard/DashboardEntityList.tsx b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardEntityList.tsx
new file mode 100644
index 0000000000..005bc42151
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardEntityList.tsx
@@ -0,0 +1,89 @@
+/*
+ * 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, 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,
+ ErrorPanel,
+} from '@backstage/core-components';
+import DesktopMac from '@material-ui/icons/DesktopMac';
+import { DashboardEntitySummary } from '../../api/NewRelicDashboardApi';
+import { ResultEntity } from '../../types/DashboardEntity';
+import { useEntity } from '@backstage/plugin-catalog-react';
+import { NEWRELIC_GUID_ANNOTATION } from './../../constants';
+
+const useStyles = makeStyles({
+ svgIcon: {
+ display: 'inline-block',
+ '& svg': {
+ display: 'inline-block',
+ fontSize: 'inherit',
+ verticalAlign: 'baseline',
+ },
+ },
+});
+export const DashboardEntityList = () => {
+ const { entity } = useEntity();
+ const classes = useStyles();
+ const newRelicDashboardAPI = useApi(newRelicDashboardApiRef);
+ const { value, loading, error } = useAsync(async (): Promise<
+ DashboardEntitySummary | undefined
+ > => {
+ const dashboardObject: Promise =
+ newRelicDashboardAPI.getDashboardEntity(
+ String(entity.metadata.annotations?.[NEWRELIC_GUID_ANNOTATION]),
+ );
+ return dashboardObject;
+ }, [entity.metadata.annotations?.[NEWRELIC_GUID_ANNOTATION]]);
+ if (loading) {
+ return ;
+ }
+ if (error) {
+ return ;
+ }
+ return (
+
+ {value?.getDashboardEntity === undefined &&
+ 'Unauthorized Request , please check API Key'}
+ {value?.getDashboardEntity !== undefined &&
+ value?.getDashboardEntity?.data.actor.entitySearch.results?.entities
+ ?.length <= 0 && (
+ <>No Dashboard Pages found with the specified Dashboard GUID>
+ )}
+ {value?.getDashboardEntity?.data.actor.entitySearch.results.entities?.map(
+ (entityResult: ResultEntity) => {
+ return (
+
+
+
+
+
+
+
+ {entityResult.name}
+
+
+ );
+ },
+ )}
+
+ );
+};
diff --git a/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshot.tsx b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshot.tsx
new file mode 100644
index 0000000000..95de46df2c
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshot.tsx
@@ -0,0 +1,82 @@
+/*
+ * 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 } from '@material-ui/core';
+import { useApi } from '@backstage/core-plugin-api';
+import { useAsync } from 'react-use';
+import {
+ InfoCard,
+ Progress,
+ ErrorPanel,
+ Link,
+} from '@backstage/core-components';
+import { newRelicDashboardApiRef } from '../../../api';
+import { DashboardSnapshotSummary } from './../../../api/NewRelicDashboardApi';
+
+type Props = {
+ guid: string;
+ name: string;
+ permalink: string;
+ duration: number;
+};
+
+export const DashboardSnapshot = ({
+ guid,
+ name,
+ permalink,
+ duration,
+}: Props) => {
+ const newRelicDashboardAPI = useApi(newRelicDashboardApiRef);
+ const { value, loading, error } = useAsync(async (): Promise<
+ DashboardSnapshotSummary | undefined
+ > => {
+ const dashboardObject: Promise =
+ newRelicDashboardAPI.getDashboardSnapshot(guid, duration);
+ return dashboardObject;
+ }, [guid, duration]);
+ if (loading) {
+ return ;
+ }
+ if (error) {
+ return ;
+ }
+ const url =
+ value?.getDashboardSnapshot?.data?.dashboardCreateSnapshotUrl?.replace(
+ /\pdf$/i,
+ 'png',
+ );
+ return (
+
+
+
+
+
+ {url ? (
+
+ ) : (
+ 'Dashboard loading... , click here to open if it didnt render correctly'
+ )}
+
+
+
+
+
+ );
+};
diff --git a/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshotList.tsx b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshotList.tsx
new file mode 100644
index 0000000000..c839a43ac2
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshotList.tsx
@@ -0,0 +1,148 @@
+/*
+ * 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, { useState } 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, ErrorPanel } from '@backstage/core-components';
+import { DashboardSnapshot } from './DashboardSnapshot';
+import { DashboardEntitySummary } from '../../../api/NewRelicDashboardApi';
+import { ResultEntity } from '../../../types/DashboardEntity';
+
+interface TabPanelProps {
+ children?: React.ReactNode;
+ index: number;
+ value1: number;
+}
+
+function TabPanel(props: TabPanelProps) {
+ const { children, value1, index, ...other } = props;
+
+ return (
+
+ {children}
+
+ );
+}
+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<
+ DashboardEntitySummary | undefined
+ > => {
+ const dashboardObject: Promise =
+ newRelicDashboardAPI.getDashboardEntity(guid);
+ return dashboardObject;
+ }, [guid]);
+ const [value1, setValue1] = useState(0);
+ const handleChange = ({}: React.ChangeEvent<{}>, newValue: number) => {
+ setValue1(newValue);
+ };
+
+ if (loading) {
+ return ;
+ }
+ if (error) {
+ return ;
+ }
+ return (
+
+
+ {value?.getDashboardEntity?.data?.actor.entitySearch.results.entities?.map(
+ (Entity: ResultEntity, index: number) => {
+ return (
+
+ );
+ },
+ )}
+
+ {value?.getDashboardEntity?.data?.actor.entitySearch.results.entities?.map(
+ (Entity: ResultEntity, index: number) => {
+ return (
+
+
+
+ );
+ },
+ )}
+
+ );
+};
diff --git a/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/index.ts b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/index.ts
new file mode 100644
index 0000000000..6b17582515
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/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/newrelic-dashboard/src/components/NewRelicDashboard/NewRelicDashboard.tsx b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/NewRelicDashboard.tsx
new file mode 100644
index 0000000000..c9cc9bf611
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/NewRelicDashboard.tsx
@@ -0,0 +1,44 @@
+/*
+ * 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 { DashboardEntityList } from './DashboardEntityList';
+import { DashboardSnapshotList } from './DashboardSnapshotList';
+import { useEntity } from '@backstage/plugin-catalog-react';
+import { NEWRELIC_GUID_ANNOTATION } from '../../constants';
+
+export const NewRelicDashboard = () => {
+ const { entity } = useEntity();
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/plugins/newrelic-dashboard/src/components/NewRelicDashboard/index.ts b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/index.ts
new file mode 100644
index 0000000000..668d76a059
--- /dev/null
+++ b/plugins/newrelic-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/newrelic-dashboard/src/constants.ts b/plugins/newrelic-dashboard/src/constants.ts
new file mode 100644
index 0000000000..dd0dcff2d8
--- /dev/null
+++ b/plugins/newrelic-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_ANNOTATION = 'newrelic.com/dashboard-guid';
diff --git a/plugins/newrelic-dashboard/src/index.ts b/plugins/newrelic-dashboard/src/index.ts
new file mode 100644
index 0000000000..821ccd4bda
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/index.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 {
+ newRelicDashboardPlugin,
+ EntityNewRelicDashboardCard,
+ EntityNewRelicDashboardContent,
+} from './plugin';
+export { isNewRelicDashboardAvailable } from './Router';
diff --git a/plugins/newrelic-dashboard/src/plugin.test.ts b/plugins/newrelic-dashboard/src/plugin.test.ts
new file mode 100644
index 0000000000..e5eccd930b
--- /dev/null
+++ b/plugins/newrelic-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/newrelic-dashboard/src/plugin.ts b/plugins/newrelic-dashboard/src/plugin.ts
new file mode 100644
index 0000000000..18397902f7
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/plugin.ts
@@ -0,0 +1,62 @@
+/*
+ * 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,
+ configApiRef,
+ createApiFactory,
+ discoveryApiRef,
+ createComponentExtension,
+} 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 EntityNewRelicDashboardContent = newRelicDashboardPlugin.provide(
+ createComponentExtension({
+ name: 'EntityNewRelicDashboardPage',
+ component: {
+ lazy: () => import('./Router').then(m => m.Router),
+ },
+ }),
+);
+
+export const EntityNewRelicDashboardCard = newRelicDashboardPlugin.provide(
+ createComponentExtension({
+ name: 'EntityNewRelicDashboardListComponent',
+ component: {
+ lazy: () =>
+ import('./components/NewRelicDashboard/DashboardEntityList').then(
+ m => m.DashboardEntityList,
+ ),
+ },
+ }),
+);
diff --git a/plugins/newrelic-dashboard/src/queries/getDashboardParentGuidQuery.ts b/plugins/newrelic-dashboard/src/queries/getDashboardParentGuidQuery.ts
new file mode 100644
index 0000000000..3a06384539
--- /dev/null
+++ b/plugins/newrelic-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/newrelic-dashboard/src/queries/getDashboardSnapshotQuery.ts b/plugins/newrelic-dashboard/src/queries/getDashboardSnapshotQuery.ts
new file mode 100644
index 0000000000..be683ecd7b
--- /dev/null
+++ b/plugins/newrelic-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! , ,$duration: Milliseconds) {\n dashboardCreateSnapshotUrl(guid: $guid , params: {timeWindow: {duration: $duration}})\n}';
diff --git a/plugins/newrelic-dashboard/src/routes.ts b/plugins/newrelic-dashboard/src/routes.ts
new file mode 100644
index 0000000000..5bc8b24457
--- /dev/null
+++ b/plugins/newrelic-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({
+ id: 'new-relic-dashboard',
+});
diff --git a/plugins/newrelic-dashboard/src/setupTests.ts b/plugins/newrelic-dashboard/src/setupTests.ts
new file mode 100644
index 0000000000..fc6dbd98f8
--- /dev/null
+++ b/plugins/newrelic-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/newrelic-dashboard/src/types/DashboardEntity.ts b/plugins/newrelic-dashboard/src/types/DashboardEntity.ts
new file mode 100644
index 0000000000..78a1b7913d
--- /dev/null
+++ b/plugins/newrelic-dashboard/src/types/DashboardEntity.ts
@@ -0,0 +1,33 @@
+/*
+ * 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: Array;
+ };
+ };
+ };
+ };
+};
+
+export type ResultEntity = {
+ guid: string;
+ permalink: string;
+ name: string;
+};
diff --git a/plugins/newrelic-dashboard/src/types/DashboardSnapshot.ts b/plugins/newrelic-dashboard/src/types/DashboardSnapshot.ts
new file mode 100644
index 0000000000..6c11c6f6d7
--- /dev/null
+++ b/plugins/newrelic-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/newrelic-dashboard/src/types/SnapshotDetails.ts b/plugins/newrelic-dashboard/src/types/SnapshotDetails.ts
new file mode 100644
index 0000000000..e189ac52c4
--- /dev/null
+++ b/plugins/newrelic-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;
+};