Merge pull request #8690 from moltenice-forks/airbrake-initial

Initial Airbrake Plugin Setup
This commit is contained in:
Ben Lambert
2022-01-10 11:17:18 +01:00
committed by GitHub
20 changed files with 509 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-airbrake': minor
---
A plugin for Airbrake (https://airbrake.io/) has been created
+1
View File
@@ -1,5 +1,6 @@
abc
accessors
Airbrake
Anddddd
Apdex
api
+9
View File
@@ -0,0 +1,9 @@
---
title: Airbrake
author: Simply Business
authorUrl: https://github.com/simplybusiness/
category: Monitoring
description: Access Airbrake error monitoring and other integrations from within Backstage
documentation: https://github.com/backstage/backstage/blob/master/plugins/airbrake/README.md
iconUrl: https://wp-assets.airbrake.io/wp-content/uploads/2020/10/05222904/Square-white-A-on-Orange.png
npmPackageName: '@backstage/plugin-airbrake'
+1
View File
@@ -11,6 +11,7 @@
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/integration-react": "^0.1.16",
"@backstage/plugin-airbrake": "^0.0.0",
"@backstage/plugin-api-docs": "^0.6.21",
"@backstage/plugin-azure-devops": "^0.1.9",
"@backstage/plugin-apache-airflow": "^0.1.2",
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint')],
};
+13
View File
@@ -0,0 +1,13 @@
# Airbrake
Welcome to the Airbrake plugin!
This is a plugin providing connectivity between Backstage and Airbrake (https://airbrake.io/).
_This plugin is currently not fit for use as it is work in progress_
## Getting started
You can serve the plugin in isolation by running `yarn start` in the plugin directory.
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
+27
View File
@@ -0,0 +1,27 @@
## API Report File for "@backstage/plugin-airbrake"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="react" />
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { RouteRef } from '@backstage/core-plugin-api';
// Warning: (ae-missing-release-tag) "airbrakePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const airbrakePlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{}
>;
// Warning: (ae-missing-release-tag) "EntityAirbrakeContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EntityAirbrakeContent: () => JSX.Element;
// (No @packageDocumentation comment for this package)
```
+53
View File
@@ -0,0 +1,53 @@
/*
* 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 { EntityAirbrakeContent, airbrakePlugin } from '../src/plugin';
import {
Content,
ContentHeader,
Header,
HeaderLabel,
Page,
SupportButton,
} from '@backstage/core-components';
createDevApp()
.registerPlugin(airbrakePlugin)
.addPage({
element: (
<Page themeId="tool">
<Header
title="Airbrake demo application"
subtitle="Test the plugin below"
>
<HeaderLabel label="Owner" value="Owner" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Airbrake">
<SupportButton>
A description of your plugin goes here.
</SupportButton>
</ContentHeader>
<EntityAirbrakeContent />
</Content>
</Page>
),
title: 'Root Page',
path: '/airbrake',
})
.render();
+63
View File
@@ -0,0 +1,63 @@
{
"name": "@backstage/plugin-airbrake",
"version": "0.0.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/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/theme": "^0.2.14",
"@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",
"object-hash": "^2.2.0"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@types/object-hash": "^2.2.1",
"@backstage/app-defaults": "^0.1.2",
"@backstage/cli": "^0.10.5",
"@backstage/core-app-api": "^0.3.1",
"@backstage/dev-utils": "^0.2.16",
"@backstage/test-utils": "^0.2.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32",
"msw": "^0.35.0",
"cross-fetch": "^3.0.6",
"react-router": "6.0.0-beta.0"
},
"files": [
"dist"
],
"jest": {
"coverageThreshold": {
"global": {
"functions": 100,
"lines": 100,
"statements": 100
}
}
}
}
@@ -0,0 +1,25 @@
/*
* 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 { EntityAirbrakeContent } from './plugin';
import { renderInTestApp } from '@backstage/test-utils';
describe('The Airbrake entity', () => {
it('should render the content properly', async () => {
const rendered = await renderInTestApp(<EntityAirbrakeContent />);
expect(rendered.getByText('ChunkLoadError')).toBeInTheDocument();
});
});
@@ -0,0 +1,32 @@
/*
* Copyright 2020 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 { EntityAirbrakeContent } from './EntityAirbrakeContent';
import exampleData from './example-data.json';
import { renderInTestApp } from '@backstage/test-utils';
describe('EntityAirbrakeContent', () => {
it('renders all errors sent from Airbrake', async () => {
const table = await renderInTestApp(<EntityAirbrakeContent />);
expect(exampleData.groups.length).toBeGreaterThan(0);
for (const group of exampleData.groups) {
expect(
await table.getByText(group.errors[0].message),
).toBeInTheDocument();
}
});
});
@@ -0,0 +1,48 @@
/*
* 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 { InfoCard } from '@backstage/core-components';
import exampleData from './example-data.json';
import hash from 'object-hash';
import { makeStyles } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
const useStyles = makeStyles<BackstageTheme>(() => ({
multilineText: {
whiteSpace: 'pre-wrap',
},
}));
export const EntityAirbrakeContent = () => {
const classes = useStyles();
return (
<Grid container spacing={3} direction="column">
{exampleData.groups.map(group => (
<Grid item key={group.id}>
{group.errors.map(error => (
<InfoCard title={error.type} key={hash(error)}>
<Typography variant="body1" className={classes.multilineText}>
{error.message}
</Typography>
</InfoCard>
))}
</Grid>
))}
</Grid>
);
};
@@ -0,0 +1,85 @@
{
"count": 2,
"end": "",
"groups": [
{
"id": "1",
"projectId": 123,
"resolved": false,
"muted": false,
"mutedBy": 0,
"mutedAt": null,
"errors": [
{
"type": "Error",
"message": "useSearch must be used within a SearchContextProvider",
"backtrace": [
{
"file": "webpack-internal:///../../node_modules/@backstage/plugin-search/dist/esm/index-893ec2f5.esm.js",
"function": "useSearch",
"line": 303,
"column": 11,
"code": null
}
]
}
],
"attributes": null,
"context": {
"action": "",
"component": "",
"environment": "local",
"severity": "error"
},
"lastDeployId": "0",
"lastDeployAt": null,
"lastNoticeId": "234",
"lastNoticeAt": "2021-12-19T09:59:00.124Z",
"noticeCount": 5,
"noticeTotalCount": 5,
"commentCount": 0,
"createdAt": "2021-12-19T09:44:30.067447Z"
},
{
"id": "2",
"projectId": 123,
"resolved": true,
"muted": false,
"mutedBy": 0,
"mutedAt": null,
"errors": [
{
"type": "ChunkLoadError",
"message": "Loading chunk 7764 failed.",
"backtrace": [
{
"file": "/PROJECT_ROOT/static/runtime.069c874d.js",
"function": "Object._.f.j",
"line": 1,
"column": 19465,
"code": null
}
]
}
],
"attributes": null,
"context": {
"action": "",
"component": "",
"environment": "local",
"severity": "error"
},
"lastDeployId": "0",
"lastDeployAt": null,
"lastNoticeId": "345",
"lastNoticeAt": "2021-12-15T17:16:38.419Z",
"noticeCount": 1,
"noticeTotalCount": 1,
"commentCount": 0,
"createdAt": "2021-12-15T17:16:38.41983Z"
}
],
"page": 1,
"resolvedCount": 1,
"unresolvedCount": 1
}
+23
View File
@@ -0,0 +1,23 @@
/*
* 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 { EntityAirbrakeContent, airbrakePlugin } from './index';
describe('The Airbrake plugin index file', () => {
it('exports the plugin and content', async () => {
expect(EntityAirbrakeContent).toBeTruthy();
expect(airbrakePlugin).toBeTruthy();
});
});
+16
View File
@@ -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 { airbrakePlugin, EntityAirbrakeContent } from './plugin';
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2020 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 { airbrakePlugin } from './plugin';
describe('catalog', () => {
it('should export plugin', () => {
expect(airbrakePlugin).toBeDefined();
});
});
+39
View File
@@ -0,0 +1,39 @@
/*
* 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,
} from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';
export const airbrakePlugin = createPlugin({
id: 'airbrake',
routes: {
root: rootRouteRef,
},
});
export const EntityAirbrakeContent = airbrakePlugin.provide(
createRoutableExtension({
name: 'EntityAirbrakeContent',
component: () =>
import('./components/EntityAirbrakeContent/EntityAirbrakeContent').then(
m => m.EntityAirbrakeContent,
),
mountPoint: rootRouteRef,
}),
);
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createRouteRef } from '@backstage/core-plugin-api';
export const rootRouteRef = createRouteRef({
id: 'airbrake',
});
+17
View File
@@ -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';
+6 -1
View File
@@ -7879,6 +7879,11 @@
dependencies:
"@types/node" "*"
"@types/object-hash@^2.2.1":
version "2.2.1"
resolved "https://registry.npmjs.org/@types/object-hash/-/object-hash-2.2.1.tgz#67c169f8f033e0b62abbf81df2d00f4598d540b9"
integrity sha512-i/rtaJFCsPljrZvP/akBqEwUP2y5cZLOmvO+JaYnz01aPknrQ+hB5MRcO7iqCUsFaYfTG8kGfKUyboA07xeDHQ==
"@types/overlayscrollbars@^1.12.0":
version "1.12.0"
resolved "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz#98456caceca8ad73bd5bb572632a585074e70764"
@@ -21790,7 +21795,7 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"
object-hash@^2.0.1, object-hash@^2.1.1:
object-hash@^2.0.1, object-hash@^2.1.1, object-hash@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5"
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==