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:
blam
2020-10-19 23:57:40 +02:00
332 changed files with 4838 additions and 2569 deletions
+28 -6
View File
@@ -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
+110
View File
@@ -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 -1
View File
@@ -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);
},