Merge branch 'master' of github.com:spotify/backstage into migrate-to-msw
* 'master' of github.com:spotify/backstage: (139 commits) Cleanup Update PinButton.test.tsx feat: update github insights plugin version (#2973) Ignore IntelliJ *.iml files (#2971) chore(deps): bump rollup-plugin-dts from 1.4.11 to 1.4.13 fix the plugin card on plugins page align 'Add to Marketplace' button on plugins page fix the PluginGrid on mobiles sizes use getBy query instead of queryBy when asserting for elements present in document (#2951) Update PinButton.tsx Add test case for Progress component (#2953) fix the styling of footer copy on mobile add changeset handle the case where no entities are available to show core-api: work around issue with ApiRef export const declarations core-api: move utility api system implementation into apis/system Update docs regarding npm config ignore-scripts flag Another try Fix Core Features configuration id (#2948) Fix test? ...
This commit is contained in:
@@ -7,16 +7,38 @@ Website: [https://newrelic.com](https://newrelic.com)
|
||||
|
||||
## Getting Started
|
||||
|
||||
Add New Relic REST API Key to `app-config.yaml`
|
||||
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:
|
||||
|
||||
```yaml
|
||||
newrelic:
|
||||
api:
|
||||
baseUrl: 'https://api.newrelic.com/v2'
|
||||
key: <NEW_RELIC_REST_API_KEY>
|
||||
proxy:
|
||||
'/newrelic/apm/api':
|
||||
target: https://api.newrelic.com/v2
|
||||
headers:
|
||||
X-Api-Key:
|
||||
$env: NEW_RELIC_REST_API_KEY
|
||||
```
|
||||
|
||||
New Relic Plugin Path: [/newrelic](http://localhost:3000/newrelic)
|
||||
In your production deployment of Backstage, you would also need to ensure that
|
||||
you've set the `NEW_RELIC_REST_API_KEY` environment variable before starting
|
||||
the backend.
|
||||
|
||||
While working locally, you may wish to hard-code your API key in your
|
||||
`app-config.local.yaml` like this:
|
||||
|
||||
```yaml
|
||||
# app-config.local.yaml
|
||||
proxy:
|
||||
'/newrelic/apm/api':
|
||||
headers:
|
||||
X-Api-Key: NRRA-YourActualApiKey
|
||||
```
|
||||
|
||||
Read more about how to find or generate this key in
|
||||
[New Relic's Documentation](https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys#rest-api-key).
|
||||
|
||||
See if it's working by visiting the New Relic Plugin Path:
|
||||
[/newrelic](http://localhost:3000/newrelic)
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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, DiscoveryApi } from '@backstage/core';
|
||||
|
||||
export type NewRelicApplication = {
|
||||
id: number;
|
||||
application_summary: NewRelicApplicationSummary;
|
||||
name: string;
|
||||
language: string;
|
||||
health_status: string;
|
||||
reporting: boolean;
|
||||
settings: NewRelicApplicationSettings;
|
||||
links?: NewRelicApplicationLinks;
|
||||
};
|
||||
|
||||
export type NewRelicApplicationSummary = {
|
||||
apdex_score: number;
|
||||
error_rate: number;
|
||||
host_count: number;
|
||||
instance_count: number;
|
||||
response_time: number;
|
||||
throughput: number;
|
||||
};
|
||||
|
||||
export type NewRelicApplicationSettings = {
|
||||
app_apdex_threshold: number;
|
||||
end_user_apdex_threshold: number;
|
||||
enable_real_user_monitoring: boolean;
|
||||
use_server_side_config: boolean;
|
||||
};
|
||||
|
||||
export type NewRelicApplicationLinks = {
|
||||
application_instances: Array<any>;
|
||||
servers: Array<any>;
|
||||
application_hosts: Array<any>;
|
||||
};
|
||||
|
||||
export type NewRelicApplications = {
|
||||
applications: NewRelicApplication[];
|
||||
};
|
||||
|
||||
export const newRelicApiRef = createApiRef<NewRelicApi>({
|
||||
id: 'plugin.newrelic.service',
|
||||
description: 'Used by the NewRelic plugin to make requests',
|
||||
});
|
||||
|
||||
const DEFAULT_PROXY_PATH_BASE = '/newrelic';
|
||||
|
||||
type Options = {
|
||||
discoveryApi: DiscoveryApi;
|
||||
/**
|
||||
* Path to use for requests via the proxy, defaults to /newrelic
|
||||
*/
|
||||
proxyPathBase?: string;
|
||||
};
|
||||
|
||||
export interface NewRelicApi {
|
||||
getApplications(): Promise<NewRelicApplications>;
|
||||
}
|
||||
|
||||
export class NewRelicClient implements NewRelicApi {
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
private readonly proxyPathBase: string;
|
||||
|
||||
constructor(options: Options) {
|
||||
this.discoveryApi = options.discoveryApi;
|
||||
this.proxyPathBase = options.proxyPathBase ?? DEFAULT_PROXY_PATH_BASE;
|
||||
}
|
||||
|
||||
async getApplications(): Promise<NewRelicApplications> {
|
||||
const url = await this.getApiUrl('apm', 'applications.json');
|
||||
const response = await fetch(url);
|
||||
let responseJson;
|
||||
|
||||
try {
|
||||
responseJson = await response.json();
|
||||
} catch (e) {
|
||||
responseJson = { applications: [] };
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(
|
||||
`Error communicating with New Relic: ${
|
||||
responseJson?.error?.title || response.statusText
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
return responseJson;
|
||||
}
|
||||
|
||||
private async getApiUrl(product: string, path: string) {
|
||||
const proxyUrl = await this.discoveryApi.getBaseUrl('proxy');
|
||||
return `${proxyUrl}${this.proxyPathBase}/${product}/api/${path}`;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import { Grid } from '@material-ui/core';
|
||||
import {
|
||||
Header,
|
||||
Page,
|
||||
pageTheme,
|
||||
Content,
|
||||
ContentHeader,
|
||||
HeaderLabel,
|
||||
@@ -28,7 +27,7 @@ import {
|
||||
import NewRelicFetchComponent from '../NewRelicFetchComponent';
|
||||
|
||||
const NewRelicComponent: FC<{}> = () => (
|
||||
<Page theme={pageTheme.tool}>
|
||||
<Page themeId="tool">
|
||||
<Header title="New Relic">
|
||||
<HeaderLabel label="Owner" value="Engineering" />
|
||||
</Header>
|
||||
|
||||
@@ -15,52 +15,10 @@
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import {
|
||||
configApiRef,
|
||||
Progress,
|
||||
Table,
|
||||
TableColumn,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import { Progress, Table, TableColumn, useApi } from '@backstage/core';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
type NewRelicApplication = {
|
||||
id: number;
|
||||
application_summary: NewRelicApplicationSummary;
|
||||
name: string;
|
||||
language: string;
|
||||
health_status: string;
|
||||
reporting: boolean;
|
||||
settings: NewRelicApplicationSettings;
|
||||
links?: NewRelicApplicationLinks;
|
||||
};
|
||||
|
||||
type NewRelicApplicationSummary = {
|
||||
apdex_score: number;
|
||||
error_rate: number;
|
||||
host_count: number;
|
||||
instance_count: number;
|
||||
response_time: number;
|
||||
throughput: number;
|
||||
};
|
||||
|
||||
type NewRelicApplicationSettings = {
|
||||
app_apdex_threshold: number;
|
||||
end_user_apdex_threshold: number;
|
||||
enable_real_user_monitoring: boolean;
|
||||
use_server_side_config: boolean;
|
||||
};
|
||||
|
||||
type NewRelicApplicationLinks = {
|
||||
application_instances: Array<any>;
|
||||
servers: Array<any>;
|
||||
application_hosts: Array<any>;
|
||||
};
|
||||
|
||||
type NewRelicApplications = {
|
||||
applications: NewRelicApplication[];
|
||||
};
|
||||
import { newRelicApiRef, NewRelicApplications } from '../../api';
|
||||
|
||||
export const NewRelicAPMTable: FC<NewRelicApplications> = ({
|
||||
applications,
|
||||
@@ -73,7 +31,7 @@ export const NewRelicAPMTable: FC<NewRelicApplications> = ({
|
||||
{ title: 'Instance Count', field: 'instanceCount' },
|
||||
{ title: 'Apdex', field: 'apdexScore' },
|
||||
];
|
||||
const data = applications.map((app: NewRelicApplication) => {
|
||||
const data = applications.map(app => {
|
||||
const { name, application_summary: applicationSummary } = app;
|
||||
const {
|
||||
response_time: responseTime,
|
||||
@@ -104,20 +62,11 @@ export const NewRelicAPMTable: FC<NewRelicApplications> = ({
|
||||
};
|
||||
|
||||
const NewRelicFetchComponent: FC<{}> = () => {
|
||||
const configApi = useApi(configApiRef);
|
||||
const apiBaseUrl = configApi.getString('newrelic.api.baseUrl');
|
||||
const apiKey = configApi.getString('newrelic.api.key');
|
||||
const api = useApi(newRelicApiRef);
|
||||
|
||||
const { value, loading, error } = useAsync(async (): Promise<
|
||||
NewRelicApplication[]
|
||||
> => {
|
||||
const response = await fetch(`${apiBaseUrl}/applications.json`, {
|
||||
headers: {
|
||||
'X-Api-Key': apiKey,
|
||||
},
|
||||
});
|
||||
const data: NewRelicApplications = await response.json();
|
||||
return data.applications.filter((application: NewRelicApplication) => {
|
||||
const { value, loading, error } = useAsync(async () => {
|
||||
const data = await api.getApplications();
|
||||
return data.applications.filter(application => {
|
||||
return application.hasOwnProperty('application_summary');
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import {
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/core';
|
||||
import { NewRelicClient, newRelicApiRef } from './api';
|
||||
import NewRelicComponent from './components/NewRelicComponent';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
@@ -24,6 +30,13 @@ export const rootRouteRef = createRouteRef({
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'newrelic',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: newRelicApiRef,
|
||||
deps: { discoveryApi: discoveryApiRef },
|
||||
factory: ({ discoveryApi }) => new NewRelicClient({ discoveryApi }),
|
||||
}),
|
||||
],
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, NewRelicComponent);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user