From e90a2f49e1c7f25c6b98e7d696e86cc098e422ff Mon Sep 17 00:00:00 2001 From: moltenice Date: Wed, 22 Dec 2021 14:22:11 +0000 Subject: [PATCH 01/18] Add in the plugin marketplace entry for Airbrake Signed-off-by: Karan Shah --- microsite/data/plugins/airbrake.yaml | 9 +++++++++ microsite/static/img/airbrake.svg | 1 + 2 files changed, 10 insertions(+) create mode 100644 microsite/data/plugins/airbrake.yaml create mode 100644 microsite/static/img/airbrake.svg diff --git a/microsite/data/plugins/airbrake.yaml b/microsite/data/plugins/airbrake.yaml new file mode 100644 index 0000000000..0ebb5b6950 --- /dev/null +++ b/microsite/data/plugins/airbrake.yaml @@ -0,0 +1,9 @@ +--- +title: Airbrake +author: Simply Business +authorUrl: https://sbtech.simplybusiness.co.uk/ +category: Monitoring +description: Access Airbrake error monitoring and other integrations from within Backstage +documentation: https://github.com/backstage/backstage/blob/master/plugins/api-docs/README.md +iconUrl: img/airbrake.svg +npmPackageName: '@backstage/plugin-airbrake' diff --git a/microsite/static/img/airbrake.svg b/microsite/static/img/airbrake.svg new file mode 100644 index 0000000000..8241e375bc --- /dev/null +++ b/microsite/static/img/airbrake.svg @@ -0,0 +1 @@ + From 5cd6880dbe3de998562a43e4cda28b2959334c40 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 22 Dec 2021 14:59:19 +0000 Subject: [PATCH 02/18] Generate the initial plugin code Signed-off-by: Karan Shah --- .github/CODEOWNERS | 79 ++++++------- microsite/data/plugins/airbrake.yaml | 4 +- packages/app/package.json | 5 +- packages/app/src/App.tsx | 2 + plugins/airbrake/.eslintrc.js | 3 + plugins/airbrake/README.md | 13 +++ plugins/airbrake/dev/index.tsx | 27 +++++ plugins/airbrake/package.json | 50 +++++++++ .../ExampleComponent.test.tsx | 47 ++++++++ .../ExampleComponent/ExampleComponent.tsx | 53 +++++++++ .../src/components/ExampleComponent/index.ts | 16 +++ .../ExampleFetchComponent.test.tsx | 40 +++++++ .../ExampleFetchComponent.tsx | 105 ++++++++++++++++++ .../components/ExampleFetchComponent/index.ts | 16 +++ plugins/airbrake/src/index.ts | 16 +++ plugins/airbrake/src/plugin.test.ts | 22 ++++ plugins/airbrake/src/plugin.ts | 37 ++++++ plugins/airbrake/src/routes.ts | 20 ++++ plugins/airbrake/src/setupTests.ts | 17 +++ 19 files changed, 529 insertions(+), 43 deletions(-) create mode 100644 plugins/airbrake/.eslintrc.js create mode 100644 plugins/airbrake/README.md create mode 100644 plugins/airbrake/dev/index.tsx create mode 100644 plugins/airbrake/package.json create mode 100644 plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx create mode 100644 plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx create mode 100644 plugins/airbrake/src/components/ExampleComponent/index.ts create mode 100644 plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx create mode 100644 plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx create mode 100644 plugins/airbrake/src/components/ExampleFetchComponent/index.ts create mode 100644 plugins/airbrake/src/index.ts create mode 100644 plugins/airbrake/src/plugin.test.ts create mode 100644 plugins/airbrake/src/plugin.ts create mode 100644 plugins/airbrake/src/routes.ts create mode 100644 plugins/airbrake/src/setupTests.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9cc5a2378e..e47ddc8262 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,42 +4,43 @@ # The last matching pattern takes precedence. # https://help.github.com/articles/about-codeowners/ -* @backstage/reviewers -/docs/features/techdocs @backstage/techdocs-core -/docs/features/search @backstage/techdocs-core -/docs/assets/search @backstage/techdocs-core -/plugins/allure @backstage/reviewers @deepak-bhardwaj-ps -/plugins/circleci @backstage/reviewers @adamdmharvey -/plugins/code-coverage @backstage/reviewers @alde @nissayeva -/plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva -/plugins/cost-insights @backstage/silver-lining -/plugins/cloudbuild @backstage/reviewers @trivago/ebarrios -/plugins/search @backstage/techdocs-core -/plugins/search-* @backstage/techdocs-core -/plugins/techdocs @backstage/techdocs-core -/plugins/techdocs-backend @backstage/techdocs-core -/plugins/ilert @backstage/reviewers @yacut -/plugins/home @backstage/techdocs-core -/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin -/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin -/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin -/plugins/jenkins @backstage/reviewers @timja -/plugins/jenkins-backend @backstage/reviewers @timja -/plugins/kafka @backstage/reviewers @nirga -/plugins/kafka-backend @backstage/reviewers @nirga -/plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka -/plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski -/plugins/git-release-manager @backstage/reviewers @erikengervall -/tech-insights-backend @backstage/reviewers @xantier @iain-b -/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b -/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b -/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b -/packages/embedded-techdocs-app @backstage/techdocs-core -/packages/search-common @backstage/techdocs-core -/packages/techdocs-cli @backstage/techdocs-core -/packages/techdocs-common @backstage/techdocs-core -/.changeset/cost-insights-* @backstage/reviewers @backstage/silver-lining -/.changeset/search-* @backstage/techdocs-core -/.changeset/techdocs-* @backstage/techdocs-core -/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core -/plugins/apache-airflow @backstage/reviewers @cmpadden +* @backstage/maintainers +/.changeset/cost-insights-* @backstage/reviewers @backstage/silver-lining +/.changeset/search-* @backstage/techdocs-core +/.changeset/techdocs-* @backstage/techdocs-core +/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core +/docs/assets/search @backstage/techdocs-core +/docs/features/search @backstage/techdocs-core +/docs/features/techdocs @backstage/techdocs-core +/packages/embedded-techdocs-app @backstage/techdocs-core +/packages/search-common @backstage/techdocs-core +/packages/techdocs-cli @backstage/techdocs-core +/packages/techdocs-common @backstage/techdocs-core +/plugins/airbrake @simplybusiness/silversmiths +/plugins/allure @backstage/reviewers @deepak-bhardwaj-ps +/plugins/apache-airflow @backstage/reviewers @cmpadden +/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin +/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin +/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin +/plugins/circleci @backstage/reviewers @adamdmharvey +/plugins/cloudbuild @backstage/reviewers @trivago/ebarrios +/plugins/code-coverage @backstage/reviewers @alde @nissayeva +/plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva +/plugins/cost-insights @backstage/silver-lining +/plugins/git-release-manager @backstage/reviewers @erikengervall +/plugins/home @backstage/techdocs-core +/plugins/ilert @backstage/reviewers @yacut +/plugins/jenkins @backstage/reviewers @timja +/plugins/jenkins-backend @backstage/reviewers @timja +/plugins/kafka @backstage/reviewers @nirga +/plugins/kafka-backend @backstage/reviewers @nirga +/plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski +/plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka +/plugins/search @backstage/techdocs-core +/plugins/search-* @backstage/techdocs-core +/plugins/techdocs @backstage/techdocs-core +/plugins/techdocs-backend @backstage/techdocs-core +/tech-insights-backend @backstage/reviewers @xantier @iain-b +/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b +/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b +/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b diff --git a/microsite/data/plugins/airbrake.yaml b/microsite/data/plugins/airbrake.yaml index 0ebb5b6950..d22639fc20 100644 --- a/microsite/data/plugins/airbrake.yaml +++ b/microsite/data/plugins/airbrake.yaml @@ -1,9 +1,9 @@ --- title: Airbrake author: Simply Business -authorUrl: https://sbtech.simplybusiness.co.uk/ +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/api-docs/README.md +documentation: https://github.com/backstage/backstage/blob/master/plugins/airbrake/README.md iconUrl: img/airbrake.svg npmPackageName: '@backstage/plugin-airbrake' diff --git a/packages/app/package.json b/packages/app/package.json index 1f609858f6..14f3fa0594 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -11,9 +11,10 @@ "@backstage/core-components": "^0.8.1", "@backstage/core-plugin-api": "^0.3.1", "@backstage/integration-react": "^0.1.15", + "@backstage/plugin-airbrake": "^0.0.0", + "@backstage/plugin-apache-airflow": "^0.1.0", "@backstage/plugin-api-docs": "^0.6.18", "@backstage/plugin-azure-devops": "^0.1.7", - "@backstage/plugin-apache-airflow": "^0.1.0", "@backstage/plugin-badges": "^0.2.16", "@backstage/plugin-catalog": "^0.7.4", "@backstage/plugin-catalog-graph": "^0.2.3", @@ -41,12 +42,12 @@ "@backstage/plugin-search": "^0.5.1", "@backstage/plugin-sentry": "^0.3.30", "@backstage/plugin-shortcuts": "^0.1.15", + "@backstage/plugin-tech-insights": "^0.1.1", "@backstage/plugin-tech-radar": "^0.4.13", "@backstage/plugin-techdocs": "^0.12.10", "@backstage/plugin-todo": "^0.1.16", "@backstage/plugin-user-settings": "^0.3.13", "@backstage/search-common": "^0.2.0", - "@backstage/plugin-tech-insights": "^0.1.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 59f107c70d..487918faa8 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -88,6 +88,7 @@ import * as plugins from './plugins'; import { techDocsPage } from './components/techdocs/TechDocsPage'; import { ApacheAirflowPage } from '@backstage/plugin-apache-airflow'; +import { AirbrakePage } from '@backstage/plugin-airbrake'; const app = createApp({ apis, @@ -220,6 +221,7 @@ const routes = ( } /> } /> } /> + } /> ); diff --git a/plugins/airbrake/.eslintrc.js b/plugins/airbrake/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/airbrake/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/airbrake/README.md b/plugins/airbrake/README.md new file mode 100644 index 0000000000..fe1cbfce13 --- /dev/null +++ b/plugins/airbrake/README.md @@ -0,0 +1,13 @@ +# airbrake + +Welcome to the airbrake plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/airbrake](http://localhost:3000/airbrake). + +You can also 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. diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx new file mode 100644 index 0000000000..67de050404 --- /dev/null +++ b/plugins/airbrake/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 { airbrakePlugin, AirbrakePage } from '../src/plugin'; + +createDevApp() + .registerPlugin(airbrakePlugin) + .addPage({ + element: , + title: 'Root Page', + path: '/airbrake', + }) + .render(); diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json new file mode 100644 index 0000000000..d9b957803b --- /dev/null +++ b/plugins/airbrake/package.json @@ -0,0 +1,50 @@ +{ + "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.1", + "@backstage/core-plugin-api": "^0.3.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" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", + "@backstage/dev-utils": "^0.2.14", + "@backstage/test-utils": "^0.1.24", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^11.2.5", + "@testing-library/user-event": "^13.1.8", + "@types/jest": "*", + "@types/node": "*", + "msw": "^0.35.0", + "cross-fetch": "^3.0.6" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx new file mode 100644 index 0000000000..993a8e7a9e --- /dev/null +++ b/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx @@ -0,0 +1,47 @@ +/* + * 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 airbrake!')).toBeInTheDocument(); + }); +}); diff --git a/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx new file mode 100644 index 0000000000..bc6a9360a9 --- /dev/null +++ b/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx @@ -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 { Typography, Grid } from '@material-ui/core'; +import { + InfoCard, + Header, + Page, + Content, + ContentHeader, + HeaderLabel, + SupportButton, +} from '@backstage/core-components'; +import { ExampleFetchComponent } from '../ExampleFetchComponent'; + +export const ExampleComponent = () => ( + +
+ + +
+ + + A description of your plugin goes here. + + + + + + All content should be wrapped in a card like this. + + + + + + + + +
+); diff --git a/plugins/airbrake/src/components/ExampleComponent/index.ts b/plugins/airbrake/src/components/ExampleComponent/index.ts new file mode 100644 index 0000000000..212740dcdb --- /dev/null +++ b/plugins/airbrake/src/components/ExampleComponent/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/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx new file mode 100644 index 0000000000..5123bce8ed --- /dev/null +++ b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx @@ -0,0 +1,40 @@ +/* + * 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 { render } from '@testing-library/react'; +import { ExampleFetchComponent } from './ExampleFetchComponent'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; + +describe('ExampleFetchComponent', () => { + const server = setupServer(); + // Enable sane handlers for network requests + setupRequestMockHandlers(server); + + // setup mock response + beforeEach(() => { + server.use( + rest.get('https://randomuser.me/*', (_, res, ctx) => + res(ctx.status(200), ctx.delay(2000), ctx.json({})), + ), + ); + }); + it('should render', async () => { + const rendered = render(); + expect(await rendered.findByTestId('progress')).toBeInTheDocument(); + }); +}); diff --git a/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx new file mode 100644 index 0000000000..d7b9716439 --- /dev/null +++ b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx @@ -0,0 +1,105 @@ +/* + * 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 { makeStyles } from '@material-ui/core/styles'; +import { Table, TableColumn, Progress } from '@backstage/core-components'; +import Alert from '@material-ui/lab/Alert'; +import { useAsync } from 'react-use'; + +const useStyles = makeStyles({ + avatar: { + height: 32, + width: 32, + borderRadius: '50%', + }, +}); + +type User = { + gender: string; // "male" + name: { + title: string; // "Mr", + first: string; // "Duane", + last: string; // "Reed" + }; + location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} + email: string; // "duane.reed@example.com" + login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} + dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} + registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} + phone: string; // "07-2154-5651" + cell: string; // "0405-592-879" + id: { + name: string; // "TFN", + value: string; // "796260432" + }; + picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} + nat: string; // "AU" +}; + +type DenseTableProps = { + users: User[]; +}; + +export const DenseTable = ({ users }: DenseTableProps) => { + const classes = useStyles(); + + const columns: TableColumn[] = [ + { title: 'Avatar', field: 'avatar' }, + { title: 'Name', field: 'name' }, + { title: 'Email', field: 'email' }, + { title: 'Nationality', field: 'nationality' }, + ]; + + const data = users.map(user => { + return { + avatar: ( + {user.name.first} + ), + name: `${user.name.first} ${user.name.last}`, + email: user.email, + nationality: user.nat, + }; + }); + + return ( + + ); +}; + +export const ExampleFetchComponent = () => { + const { value, loading, error } = useAsync(async (): Promise => { + const response = await fetch('https://randomuser.me/api/?results=20'); + const data = await response.json(); + return data.results; + }, []); + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } + + return ; +}; diff --git a/plugins/airbrake/src/components/ExampleFetchComponent/index.ts b/plugins/airbrake/src/components/ExampleFetchComponent/index.ts new file mode 100644 index 0000000000..c17896a0f4 --- /dev/null +++ b/plugins/airbrake/src/components/ExampleFetchComponent/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 { ExampleFetchComponent } from './ExampleFetchComponent'; diff --git a/plugins/airbrake/src/index.ts b/plugins/airbrake/src/index.ts new file mode 100644 index 0000000000..9a75b3bbea --- /dev/null +++ b/plugins/airbrake/src/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 { airbrakePlugin, AirbrakePage } from './plugin'; diff --git a/plugins/airbrake/src/plugin.test.ts b/plugins/airbrake/src/plugin.test.ts new file mode 100644 index 0000000000..ab78fb2549 --- /dev/null +++ b/plugins/airbrake/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 { airbrakePlugin } from './plugin'; + +describe('airbrake', () => { + it('should export plugin', () => { + expect(airbrakePlugin).toBeDefined(); + }); +}); diff --git a/plugins/airbrake/src/plugin.ts b/plugins/airbrake/src/plugin.ts new file mode 100644 index 0000000000..63c9ec3acc --- /dev/null +++ b/plugins/airbrake/src/plugin.ts @@ -0,0 +1,37 @@ +/* + * 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 AirbrakePage = airbrakePlugin.provide( + createRoutableExtension({ + name: 'AirbrakePage', + component: () => + import('./components/ExampleComponent').then(m => m.ExampleComponent), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/airbrake/src/routes.ts b/plugins/airbrake/src/routes.ts new file mode 100644 index 0000000000..52672267fe --- /dev/null +++ b/plugins/airbrake/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: 'airbrake', +}); diff --git a/plugins/airbrake/src/setupTests.ts b/plugins/airbrake/src/setupTests.ts new file mode 100644 index 0000000000..fc6dbd98f8 --- /dev/null +++ b/plugins/airbrake/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'; From 073bd161df089b954dd104a58b158f47f1c2baab Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 10:08:17 +0000 Subject: [PATCH 03/18] Add index.test.ts and plugin.test.tsx Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 4 +- .../src/{plugin.test.ts => index.test.ts} | 9 +-- plugins/airbrake/src/plugin.test.tsx | 57 +++++++++++++++++++ 3 files changed, 65 insertions(+), 5 deletions(-) rename plugins/airbrake/src/{plugin.test.ts => index.test.ts} (73%) create mode 100644 plugins/airbrake/src/plugin.test.tsx diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index d9b957803b..26e2d5fded 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -32,6 +32,7 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { + "@backstage/app-defaults": "^0.1.2", "@backstage/cli": "^0.10.2", "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", @@ -42,7 +43,8 @@ "@types/jest": "*", "@types/node": "*", "msw": "^0.35.0", - "cross-fetch": "^3.0.6" + "cross-fetch": "^3.0.6", + "react-router": "6.0.0-beta.0" }, "files": [ "dist" diff --git a/plugins/airbrake/src/plugin.test.ts b/plugins/airbrake/src/index.test.ts similarity index 73% rename from plugins/airbrake/src/plugin.test.ts rename to plugins/airbrake/src/index.test.ts index ab78fb2549..ce7d493983 100644 --- a/plugins/airbrake/src/plugin.test.ts +++ b/plugins/airbrake/src/index.test.ts @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { airbrakePlugin } from './plugin'; +import { AirbrakePage, airbrakePlugin } from './index'; -describe('airbrake', () => { - it('should export plugin', () => { - expect(airbrakePlugin).toBeDefined(); +describe('The demo index page', () => { + it('exports the plugin and page', async () => { + expect(AirbrakePage).toBeTruthy(); + expect(airbrakePlugin).toBeTruthy(); }); }); diff --git a/plugins/airbrake/src/plugin.test.tsx b/plugins/airbrake/src/plugin.test.tsx new file mode 100644 index 0000000000..36696e4aaa --- /dev/null +++ b/plugins/airbrake/src/plugin.test.tsx @@ -0,0 +1,57 @@ +/* + * 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 { AirbrakePage, airbrakePlugin } from './plugin'; +import { renderWithEffects } from '@backstage/test-utils'; +import { createApp } from '@backstage/app-defaults'; +import { Route } from 'react-router'; + +describe('Airbrake', () => { + it('should export plugin', () => { + expect(airbrakePlugin).toBeDefined(); + }); + + it('should render page', async () => { + process.env = { + NODE_ENV: 'test', + APP_CONFIG: [ + { + data: { + app: { title: 'Test' }, + backend: { baseUrl: 'http://localhost:7000' }, + techdocs: { + storageUrl: 'http://localhost:7000/api/techdocs/static/docs', + }, + }, + context: 'test', + }, + ] as any, + }; + + const app = createApp(); + const AppProvider = app.getProvider(); + const AppRouter = app.getRouter(); + + const rendered = await renderWithEffects( + + + } /> + + , + ); + expect(rendered.baseElement).toBeInTheDocument(); + }); +}); From 1e69306d1b705fe93d77911e4dfbc58a78942756 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 10:42:12 +0000 Subject: [PATCH 04/18] Remove CODEOWNERS Signed-off-by: Karan Shah --- .github/CODEOWNERS | 79 +++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e47ddc8262..9cc5a2378e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,43 +4,42 @@ # The last matching pattern takes precedence. # https://help.github.com/articles/about-codeowners/ -* @backstage/maintainers -/.changeset/cost-insights-* @backstage/reviewers @backstage/silver-lining -/.changeset/search-* @backstage/techdocs-core -/.changeset/techdocs-* @backstage/techdocs-core -/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core -/docs/assets/search @backstage/techdocs-core -/docs/features/search @backstage/techdocs-core -/docs/features/techdocs @backstage/techdocs-core -/packages/embedded-techdocs-app @backstage/techdocs-core -/packages/search-common @backstage/techdocs-core -/packages/techdocs-cli @backstage/techdocs-core -/packages/techdocs-common @backstage/techdocs-core -/plugins/airbrake @simplybusiness/silversmiths -/plugins/allure @backstage/reviewers @deepak-bhardwaj-ps -/plugins/apache-airflow @backstage/reviewers @cmpadden -/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin -/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin -/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin -/plugins/circleci @backstage/reviewers @adamdmharvey -/plugins/cloudbuild @backstage/reviewers @trivago/ebarrios -/plugins/code-coverage @backstage/reviewers @alde @nissayeva -/plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva -/plugins/cost-insights @backstage/silver-lining -/plugins/git-release-manager @backstage/reviewers @erikengervall -/plugins/home @backstage/techdocs-core -/plugins/ilert @backstage/reviewers @yacut -/plugins/jenkins @backstage/reviewers @timja -/plugins/jenkins-backend @backstage/reviewers @timja -/plugins/kafka @backstage/reviewers @nirga -/plugins/kafka-backend @backstage/reviewers @nirga -/plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski -/plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka -/plugins/search @backstage/techdocs-core -/plugins/search-* @backstage/techdocs-core -/plugins/techdocs @backstage/techdocs-core -/plugins/techdocs-backend @backstage/techdocs-core -/tech-insights-backend @backstage/reviewers @xantier @iain-b -/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b -/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b -/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b +* @backstage/reviewers +/docs/features/techdocs @backstage/techdocs-core +/docs/features/search @backstage/techdocs-core +/docs/assets/search @backstage/techdocs-core +/plugins/allure @backstage/reviewers @deepak-bhardwaj-ps +/plugins/circleci @backstage/reviewers @adamdmharvey +/plugins/code-coverage @backstage/reviewers @alde @nissayeva +/plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva +/plugins/cost-insights @backstage/silver-lining +/plugins/cloudbuild @backstage/reviewers @trivago/ebarrios +/plugins/search @backstage/techdocs-core +/plugins/search-* @backstage/techdocs-core +/plugins/techdocs @backstage/techdocs-core +/plugins/techdocs-backend @backstage/techdocs-core +/plugins/ilert @backstage/reviewers @yacut +/plugins/home @backstage/techdocs-core +/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin +/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin +/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin +/plugins/jenkins @backstage/reviewers @timja +/plugins/jenkins-backend @backstage/reviewers @timja +/plugins/kafka @backstage/reviewers @nirga +/plugins/kafka-backend @backstage/reviewers @nirga +/plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka +/plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski +/plugins/git-release-manager @backstage/reviewers @erikengervall +/tech-insights-backend @backstage/reviewers @xantier @iain-b +/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b +/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b +/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b +/packages/embedded-techdocs-app @backstage/techdocs-core +/packages/search-common @backstage/techdocs-core +/packages/techdocs-cli @backstage/techdocs-core +/packages/techdocs-common @backstage/techdocs-core +/.changeset/cost-insights-* @backstage/reviewers @backstage/silver-lining +/.changeset/search-* @backstage/techdocs-core +/.changeset/techdocs-* @backstage/techdocs-core +/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core +/plugins/apache-airflow @backstage/reviewers @cmpadden From 483bdad7ece7fc0421995dd35e8bd96afa2375cc Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 10:48:30 +0000 Subject: [PATCH 05/18] Remove the plugin from App.tsx and update README Signed-off-by: Karan Shah --- packages/app/src/App.tsx | 2 -- plugins/airbrake/README.md | 10 ++++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 487918faa8..59f107c70d 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -88,7 +88,6 @@ import * as plugins from './plugins'; import { techDocsPage } from './components/techdocs/TechDocsPage'; import { ApacheAirflowPage } from '@backstage/plugin-apache-airflow'; -import { AirbrakePage } from '@backstage/plugin-airbrake'; const app = createApp({ apis, @@ -221,7 +220,6 @@ const routes = ( } /> } /> } /> - } /> ); diff --git a/plugins/airbrake/README.md b/plugins/airbrake/README.md index fe1cbfce13..28b816ed10 100644 --- a/plugins/airbrake/README.md +++ b/plugins/airbrake/README.md @@ -1,13 +1,11 @@ -# airbrake +# Airbrake -Welcome to the airbrake plugin! +Welcome to the Airbrake plugin! -_This plugin was created through the Backstage CLI_ +_This plugin is currently WIP_ ## Getting started -Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/airbrake](http://localhost:3000/airbrake). - -You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +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. From 6be5f036055294112e070397c1bf140c5209c237 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 10:51:43 +0000 Subject: [PATCH 06/18] Update the Airbrake Icon to use the one from their website Signed-off-by: Karan Shah --- microsite/data/plugins/airbrake.yaml | 2 +- microsite/static/img/airbrake.svg | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 microsite/static/img/airbrake.svg diff --git a/microsite/data/plugins/airbrake.yaml b/microsite/data/plugins/airbrake.yaml index d22639fc20..96de75c002 100644 --- a/microsite/data/plugins/airbrake.yaml +++ b/microsite/data/plugins/airbrake.yaml @@ -5,5 +5,5 @@ 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: img/airbrake.svg +iconUrl: https://wp-assets.airbrake.io/wp-content/uploads/2020/10/05222904/Square-white-A-on-Orange.png npmPackageName: '@backstage/plugin-airbrake' diff --git a/microsite/static/img/airbrake.svg b/microsite/static/img/airbrake.svg deleted file mode 100644 index 8241e375bc..0000000000 --- a/microsite/static/img/airbrake.svg +++ /dev/null @@ -1 +0,0 @@ - From 148029d9531d3ad1a0e784b3e8ebeedefbc045d4 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 10:59:22 +0000 Subject: [PATCH 07/18] Update ExampleFetchComponent.test.tsx Signed-off-by: Karan Shah --- .../ExampleFetchComponent.test.tsx | 101 ++++++++++++++++-- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx index 5123bce8ed..a9085d27d6 100644 --- a/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx @@ -25,16 +25,103 @@ describe('ExampleFetchComponent', () => { // Enable sane handlers for network requests setupRequestMockHandlers(server); - // setup mock response - beforeEach(() => { + it('renders progress bar when loading', async () => { server.use( - rest.get('https://randomuser.me/*', (_, res, ctx) => - res(ctx.status(200), ctx.delay(2000), ctx.json({})), - ), + rest.get('https://randomuser.me/*', (_, res, ctx) => { + return res(ctx.delay(2000)); + }), ); - }); - it('should render', async () => { + const rendered = render(); expect(await rendered.findByTestId('progress')).toBeInTheDocument(); }); + + it('renders data when fetch was successful', async () => { + server.use( + rest.get('https://randomuser.me/*', (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + results: [ + { + gender: 'male', + name: { + title: 'Mr', + first: 'Vedat', + last: 'Özbey', + }, + location: { + street: { + number: 7542, + name: 'Filistin Cd', + }, + city: 'Elazığ', + state: 'Karaman', + country: 'Turkey', + postcode: 29773, + coordinates: { + latitude: '73.4994', + longitude: '-44.5254', + }, + timezone: { + offset: '-8:00', + description: 'Pacific Time (US & Canada)', + }, + }, + email: 'vedat.ozbey@example.com', + login: { + uuid: 'ff2c94d3-65c9-43f2-97ae-afc39a5d4c1b', + username: 'redtiger713', + password: 'iforget', + salt: 'xuYMm0j2', + md5: '080fefd48bae64a2d3b952898ac3eca6', + sha1: '196b73795ba4529f85fdba815b20ce39cb96a7cc', + sha256: + '870a2ab050f8ce74dc44c4654c91ae94bd9921c4b4b5c4dd308529545f875adf', + }, + dob: { + date: '1996-12-31T19:07:00.102Z', + age: 25, + }, + registered: { + date: '2016-05-21T03:36:41.149Z', + age: 5, + }, + phone: '(563)-838-1728', + cell: '(443)-962-7009', + id: { + name: '', + value: null, + }, + picture: { + large: 'https://randomuser.me/api/portraits/men/21.jpg', + medium: 'https://randomuser.me/api/portraits/med/men/21.jpg', + thumbnail: + 'https://randomuser.me/api/portraits/thumb/men/21.jpg', + }, + nat: 'TR', + }, + ], + }), + ), + ), + ); + + const rendered = render(); + expect( + await rendered.findByRole('cell', { name: /Vedat Özbey/i }), + ).toBeInTheDocument(); + expect(await rendered.queryByRole('alert')).not.toBeInTheDocument(); + }); + + it('renders an alert if fetch failed', async () => { + server.use( + rest.get('https://randomuser.me/*', (_, res, ctx) => { + return res(ctx.status(500)); + }), + ); + + const rendered = render(); + expect(await rendered.findByRole('alert')).toBeInTheDocument(); + }); }); From 6868b9087d7aa079963d22ca0bc9f71ac2f66123 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 11:01:24 +0000 Subject: [PATCH 08/18] Make it very clear that this plugin is not fit for use Signed-off-by: Karan Shah --- plugins/airbrake/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/airbrake/README.md b/plugins/airbrake/README.md index 28b816ed10..f2b0b718fd 100644 --- a/plugins/airbrake/README.md +++ b/plugins/airbrake/README.md @@ -2,7 +2,7 @@ Welcome to the Airbrake plugin! -_This plugin is currently WIP_ +_This plugin is currently not fit for use as it is work in progress_ ## Getting started From ec2b38ae2c35a12e45fa71b8a11ccb3329d637a8 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 11:03:18 +0000 Subject: [PATCH 09/18] Improve the wording in index.test.ts Signed-off-by: Karan Shah --- plugins/airbrake/src/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/airbrake/src/index.test.ts b/plugins/airbrake/src/index.test.ts index ce7d493983..6776fa0558 100644 --- a/plugins/airbrake/src/index.test.ts +++ b/plugins/airbrake/src/index.test.ts @@ -15,7 +15,7 @@ */ import { AirbrakePage, airbrakePlugin } from './index'; -describe('The demo index page', () => { +describe('The Airbrake plugin index file', () => { it('exports the plugin and page', async () => { expect(AirbrakePage).toBeTruthy(); expect(airbrakePlugin).toBeTruthy(); From 367b5aac2ecd5999608d2fd9edd9c928a5174852 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 11:15:54 +0000 Subject: [PATCH 10/18] Created a Changeset and improved the Airbrake plugin README Signed-off-by: Karan Shah --- .changeset/thin-buckets-juggle.md | 6 ++++++ plugins/airbrake/README.md | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 .changeset/thin-buckets-juggle.md diff --git a/.changeset/thin-buckets-juggle.md b/.changeset/thin-buckets-juggle.md new file mode 100644 index 0000000000..4b4c9c8256 --- /dev/null +++ b/.changeset/thin-buckets-juggle.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-airbrake': minor +'example-app': patch +--- + +A plugin for Airbrake (https://airbrake.io/) has been created diff --git a/plugins/airbrake/README.md b/plugins/airbrake/README.md index f2b0b718fd..de041fb3ec 100644 --- a/plugins/airbrake/README.md +++ b/plugins/airbrake/README.md @@ -2,6 +2,8 @@ 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 From e5e3875ec99660d062c1343e86fb72c00208f626 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 23 Dec 2021 11:47:56 +0000 Subject: [PATCH 11/18] Add the generated api-report.md Signed-off-by: Karan Shah --- plugins/airbrake/api-report.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 plugins/airbrake/api-report.md diff --git a/plugins/airbrake/api-report.md b/plugins/airbrake/api-report.md new file mode 100644 index 0000000000..a5b1baf948 --- /dev/null +++ b/plugins/airbrake/api-report.md @@ -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 +/// + +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { RouteRef } from '@backstage/core-plugin-api'; + +// Warning: (ae-missing-release-tag) "AirbrakePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const AirbrakePage: () => JSX.Element; + +// 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; + }, + {} +>; + +// (No @packageDocumentation comment for this package) +``` From 66510a7d5b395f913d0c84c0c7c4995dffce8e1f Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 29 Dec 2021 09:26:28 +0000 Subject: [PATCH 12/18] Added in some ExampleData.ts Signed-off-by: Karan Shah --- plugins/airbrake/dev/ExampleData.ts | 101 ++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 plugins/airbrake/dev/ExampleData.ts diff --git a/plugins/airbrake/dev/ExampleData.ts b/plugins/airbrake/dev/ExampleData.ts new file mode 100644 index 0000000000..15588cd77f --- /dev/null +++ b/plugins/airbrake/dev/ExampleData.ts @@ -0,0 +1,101 @@ +/* + * 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 default { + 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: '345', + projectId: 123, + resolved: true, + muted: false, + mutedBy: 0, + mutedAt: null, + errors: [ + { + type: 'ChunkLoadError', + message: + 'Loading chunk 7764 failed.\n(error: https://example.com/static/react-syntax-highlighter/lowlight-import.9ac339f2.chunk.js)', + 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: '456', + 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, +}; From 0f8661f444648eb0d25a717bb10388f43c709161 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 29 Dec 2021 10:58:22 +0000 Subject: [PATCH 13/18] Add in AirbrakeWidget Signed-off-by: Karan Shah --- .../AirbrakeWidget.test.tsx} | 1 - .../AirbrakeWidget/AirbrakeWidget.tsx | 54 ++++++++ .../components/AirbrakeWidget}/ExampleData.ts | 4 +- .../ExampleComponent.test.tsx | 47 ------- .../ExampleComponent/ExampleComponent.tsx | 53 -------- .../ExampleFetchComponent.test.tsx | 127 ------------------ .../ExampleFetchComponent.tsx | 105 --------------- .../components/ExampleFetchComponent/index.ts | 16 --- plugins/airbrake/src/plugin.ts | 4 +- 9 files changed, 59 insertions(+), 352 deletions(-) rename plugins/airbrake/src/components/{ExampleComponent/index.ts => AirbrakeWidget/AirbrakeWidget.test.tsx} (91%) create mode 100644 plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx rename plugins/airbrake/{dev => src/components/AirbrakeWidget}/ExampleData.ts (98%) delete mode 100644 plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx delete mode 100644 plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx delete mode 100644 plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx delete mode 100644 plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx delete mode 100644 plugins/airbrake/src/components/ExampleFetchComponent/index.ts diff --git a/plugins/airbrake/src/components/ExampleComponent/index.ts b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx similarity index 91% rename from plugins/airbrake/src/components/ExampleComponent/index.ts rename to plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx index 212740dcdb..7f0d9bd499 100644 --- a/plugins/airbrake/src/components/ExampleComponent/index.ts +++ b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx @@ -13,4 +13,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { ExampleComponent } from './ExampleComponent'; diff --git a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx new file mode 100644 index 0000000000..7a150c5746 --- /dev/null +++ b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx @@ -0,0 +1,54 @@ +/* + * 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 exampleData from './ExampleData'; + +export const AirbrakeWidget = () => { + return ( + +
+ + +
+ + + A description of your plugin goes here. + + + {exampleData.groups.map(groupEntry => ( + + {groupEntry.errors.map(error => ( + + {error.message} + + ))} + + ))} + + +
+ ); +}; diff --git a/plugins/airbrake/dev/ExampleData.ts b/plugins/airbrake/src/components/AirbrakeWidget/ExampleData.ts similarity index 98% rename from plugins/airbrake/dev/ExampleData.ts rename to plugins/airbrake/src/components/AirbrakeWidget/ExampleData.ts index 15588cd77f..d1d2394ee6 100644 --- a/plugins/airbrake/dev/ExampleData.ts +++ b/plugins/airbrake/src/components/AirbrakeWidget/ExampleData.ts @@ -56,7 +56,7 @@ export default { createdAt: '2021-12-19T09:44:30.067447Z', }, { - id: '345', + id: '2', projectId: 123, resolved: true, muted: false, @@ -87,7 +87,7 @@ export default { }, lastDeployId: '0', lastDeployAt: null, - lastNoticeId: '456', + lastNoticeId: '345', lastNoticeAt: '2021-12-15T17:16:38.419Z', noticeCount: 1, noticeTotalCount: 1, diff --git a/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx deleted file mode 100644 index 993a8e7a9e..0000000000 --- a/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 airbrake!')).toBeInTheDocument(); - }); -}); diff --git a/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx deleted file mode 100644 index bc6a9360a9..0000000000 --- a/plugins/airbrake/src/components/ExampleComponent/ExampleComponent.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { ExampleFetchComponent } from '../ExampleFetchComponent'; - -export const ExampleComponent = () => ( - -
- - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - -
-); diff --git a/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx deleted file mode 100644 index a9085d27d6..0000000000 --- a/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 { render } from '@testing-library/react'; -import { ExampleFetchComponent } from './ExampleFetchComponent'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { setupRequestMockHandlers } from '@backstage/test-utils'; - -describe('ExampleFetchComponent', () => { - const server = setupServer(); - // Enable sane handlers for network requests - setupRequestMockHandlers(server); - - it('renders progress bar when loading', async () => { - server.use( - rest.get('https://randomuser.me/*', (_, res, ctx) => { - return res(ctx.delay(2000)); - }), - ); - - const rendered = render(); - expect(await rendered.findByTestId('progress')).toBeInTheDocument(); - }); - - it('renders data when fetch was successful', async () => { - server.use( - rest.get('https://randomuser.me/*', (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - results: [ - { - gender: 'male', - name: { - title: 'Mr', - first: 'Vedat', - last: 'Özbey', - }, - location: { - street: { - number: 7542, - name: 'Filistin Cd', - }, - city: 'Elazığ', - state: 'Karaman', - country: 'Turkey', - postcode: 29773, - coordinates: { - latitude: '73.4994', - longitude: '-44.5254', - }, - timezone: { - offset: '-8:00', - description: 'Pacific Time (US & Canada)', - }, - }, - email: 'vedat.ozbey@example.com', - login: { - uuid: 'ff2c94d3-65c9-43f2-97ae-afc39a5d4c1b', - username: 'redtiger713', - password: 'iforget', - salt: 'xuYMm0j2', - md5: '080fefd48bae64a2d3b952898ac3eca6', - sha1: '196b73795ba4529f85fdba815b20ce39cb96a7cc', - sha256: - '870a2ab050f8ce74dc44c4654c91ae94bd9921c4b4b5c4dd308529545f875adf', - }, - dob: { - date: '1996-12-31T19:07:00.102Z', - age: 25, - }, - registered: { - date: '2016-05-21T03:36:41.149Z', - age: 5, - }, - phone: '(563)-838-1728', - cell: '(443)-962-7009', - id: { - name: '', - value: null, - }, - picture: { - large: 'https://randomuser.me/api/portraits/men/21.jpg', - medium: 'https://randomuser.me/api/portraits/med/men/21.jpg', - thumbnail: - 'https://randomuser.me/api/portraits/thumb/men/21.jpg', - }, - nat: 'TR', - }, - ], - }), - ), - ), - ); - - const rendered = render(); - expect( - await rendered.findByRole('cell', { name: /Vedat Özbey/i }), - ).toBeInTheDocument(); - expect(await rendered.queryByRole('alert')).not.toBeInTheDocument(); - }); - - it('renders an alert if fetch failed', async () => { - server.use( - rest.get('https://randomuser.me/*', (_, res, ctx) => { - return res(ctx.status(500)); - }), - ); - - const rendered = render(); - expect(await rendered.findByRole('alert')).toBeInTheDocument(); - }); -}); diff --git a/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx deleted file mode 100644 index d7b9716439..0000000000 --- a/plugins/airbrake/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 { makeStyles } from '@material-ui/core/styles'; -import { Table, TableColumn, Progress } from '@backstage/core-components'; -import Alert from '@material-ui/lab/Alert'; -import { useAsync } from 'react-use'; - -const useStyles = makeStyles({ - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} - email: string; // "duane.reed@example.com" - login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} - dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} - registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} - phone: string; // "07-2154-5651" - cell: string; // "0405-592-879" - id: { - name: string; // "TFN", - value: string; // "796260432" - }; - picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable = ({ users }: DenseTableProps) => { - const classes = useStyles(); - - const columns: TableColumn[] = [ - { title: 'Avatar', field: 'avatar' }, - { title: 'Name', field: 'name' }, - { title: 'Email', field: 'email' }, - { title: 'Nationality', field: 'nationality' }, - ]; - - const data = users.map(user => { - return { - avatar: ( - {user.name.first} - ), - name: `${user.name.first} ${user.name.last}`, - email: user.email, - nationality: user.nat, - }; - }); - - return ( -
- ); -}; - -export const ExampleFetchComponent = () => { - const { value, loading, error } = useAsync(async (): Promise => { - const response = await fetch('https://randomuser.me/api/?results=20'); - const data = await response.json(); - return data.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return {error.message}; - } - - return ; -}; diff --git a/plugins/airbrake/src/components/ExampleFetchComponent/index.ts b/plugins/airbrake/src/components/ExampleFetchComponent/index.ts deleted file mode 100644 index c17896a0f4..0000000000 --- a/plugins/airbrake/src/components/ExampleFetchComponent/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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 { ExampleFetchComponent } from './ExampleFetchComponent'; diff --git a/plugins/airbrake/src/plugin.ts b/plugins/airbrake/src/plugin.ts index 63c9ec3acc..8eebd4e31c 100644 --- a/plugins/airbrake/src/plugin.ts +++ b/plugins/airbrake/src/plugin.ts @@ -31,7 +31,9 @@ export const AirbrakePage = airbrakePlugin.provide( createRoutableExtension({ name: 'AirbrakePage', component: () => - import('./components/ExampleComponent').then(m => m.ExampleComponent), + import('./components/AirbrakeWidget/AirbrakeWidget').then( + m => m.AirbrakeWidget, + ), mountPoint: rootRouteRef, }), ); From baa79f07241ff2524381607708207ae62ff8cc00 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 29 Dec 2021 16:02:09 +0000 Subject: [PATCH 14/18] Improve tests and keys in for loops for TSX Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 4 ++- .../AirbrakeWidget/AirbrakeWidget.test.tsx | 25 ++++++++++++++++++- .../AirbrakeWidget/AirbrakeWidget.tsx | 9 ++++--- plugins/airbrake/src/plugin.test.tsx | 4 ++- yarn.lock | 7 +++++- 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 26e2d5fded..2a4c822439 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -26,12 +26,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" + "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.2", "@backstage/core-app-api": "^0.2.1", diff --git a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx index 7f0d9bd499..e568ea526d 100644 --- a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * 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. @@ -13,3 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { ThemeProvider } from '@material-ui/core'; +import { lightTheme } from '@backstage/theme'; +import { AirbrakeWidget } from './AirbrakeWidget'; +import exampleData from './ExampleData'; + +describe('AirbrakeWidget', () => { + it('should render all messages sent from a payload', async () => { + const table = await render( + + + , + ); + expect(exampleData.groups.length).toBeGreaterThan(0); + for (const group of exampleData.groups) { + expect( + await table.findByText(group.errors[0].message), + ).toBeInTheDocument(); + } + }); +}); diff --git a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx index 7a150c5746..c7f1e1b626 100644 --- a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx +++ b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx @@ -25,6 +25,7 @@ import { SupportButton, } from '@backstage/core-components'; import exampleData from './ExampleData'; +import hash from 'object-hash'; export const AirbrakeWidget = () => { return ( @@ -38,10 +39,10 @@ export const AirbrakeWidget = () => { A description of your plugin goes here. - {exampleData.groups.map(groupEntry => ( - - {groupEntry.errors.map(error => ( - + {exampleData.groups.map(group => ( + + {group.errors.map(error => ( + {error.message} ))} diff --git a/plugins/airbrake/src/plugin.test.tsx b/plugins/airbrake/src/plugin.test.tsx index 36696e4aaa..267f29253d 100644 --- a/plugins/airbrake/src/plugin.test.tsx +++ b/plugins/airbrake/src/plugin.test.tsx @@ -52,6 +52,8 @@ describe('Airbrake', () => { , ); - expect(rendered.baseElement).toBeInTheDocument(); + expect(rendered.getAllByRole('heading', { name: 'Airbrake' }).length).toBe( + 2, + ); }); }); diff --git a/yarn.lock b/yarn.lock index 6ca47899b6..c9fb02d2c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7910,6 +7910,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" @@ -22083,7 +22088,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== From 96d560cd950dbca3fee5a6d6c1af3eff97a3b5b7 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 29 Dec 2021 16:07:48 +0000 Subject: [PATCH 15/18] Add 100% test coverage Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 2a4c822439..1e12bdd182 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -13,7 +13,7 @@ "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", "lint": "backstage-cli lint", - "test": "backstage-cli test", + "test": "backstage-cli test --coverage", "diff": "backstage-cli plugin:diff", "prepack": "backstage-cli prepack", "postpack": "backstage-cli postpack", @@ -50,5 +50,15 @@ }, "files": [ "dist" - ] + ], + "jest": { + "coverageThreshold": { + "global": { + "branches": 100, + "functions": 100, + "lines": 100, + "statements": 100 + } + } + } } From a5506610980f028a0a6c16adaa539aeb819db99f Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 29 Dec 2021 17:04:08 +0000 Subject: [PATCH 16/18] Fix AirbrakeWidget.test.tsx Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 27 +++++++++- .../AirbrakeWidget/AirbrakeWidget.test.tsx | 2 +- .../AirbrakeWidget/AirbrakeWidget.tsx | 53 ++++++++----------- .../components/AirbrakeWidget/ExampleData.ts | 3 +- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index 67de050404..a76b80a4e3 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -15,12 +15,35 @@ */ import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { airbrakePlugin, AirbrakePage } from '../src/plugin'; +import { AirbrakePage, airbrakePlugin } from '../src/plugin'; +import { + Content, + ContentHeader, + Header, + HeaderLabel, + Page, + SupportButton, +} from '@backstage/core-components'; createDevApp() .registerPlugin(airbrakePlugin) .addPage({ - element: , + element: ( + +
+ + +
+ + + + A description of your plugin goes here. + + + + +
+ ), title: 'Root Page', path: '/airbrake', }) diff --git a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx index e568ea526d..470064ed10 100644 --- a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.test.tsx @@ -31,7 +31,7 @@ describe('AirbrakeWidget', () => { expect(exampleData.groups.length).toBeGreaterThan(0); for (const group of exampleData.groups) { expect( - await table.findByText(group.errors[0].message), + await table.getByText(group.errors[0].message), ).toBeInTheDocument(); } }); diff --git a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx index c7f1e1b626..1bceb9c7e1 100644 --- a/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx +++ b/plugins/airbrake/src/components/AirbrakeWidget/AirbrakeWidget.tsx @@ -14,42 +14,35 @@ * 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 { Grid, Typography } from '@material-ui/core'; +import { InfoCard } from '@backstage/core-components'; import exampleData from './ExampleData'; import hash from 'object-hash'; +import { makeStyles } from '@material-ui/core/styles'; +import { BackstageTheme } from '@backstage/theme'; + +const useStyles = makeStyles(() => ({ + multilineText: { + whiteSpace: 'pre-wrap', + }, +})); export const AirbrakeWidget = () => { + const classes = useStyles(); + return ( - -
- - -
- - - A description of your plugin goes here. - - - {exampleData.groups.map(group => ( - - {group.errors.map(error => ( - - {error.message} - - ))} - + + {exampleData.groups.map(group => ( + + {group.errors.map(error => ( + + + {error.message} + + ))} - -
+ ))} +
); }; diff --git a/plugins/airbrake/src/components/AirbrakeWidget/ExampleData.ts b/plugins/airbrake/src/components/AirbrakeWidget/ExampleData.ts index d1d2394ee6..3d4793c011 100644 --- a/plugins/airbrake/src/components/AirbrakeWidget/ExampleData.ts +++ b/plugins/airbrake/src/components/AirbrakeWidget/ExampleData.ts @@ -65,8 +65,7 @@ export default { errors: [ { type: 'ChunkLoadError', - message: - 'Loading chunk 7764 failed.\n(error: https://example.com/static/react-syntax-highlighter/lowlight-import.9ac339f2.chunk.js)', + message: 'Loading chunk 7764 failed.', backtrace: [ { file: '/PROJECT_ROOT/static/runtime.069c874d.js', From 4ec1d27e26fdb8cad031d30ada38235a8ad424de Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 29 Dec 2021 17:10:45 +0000 Subject: [PATCH 17/18] Fix plugin.test.tsx as page elements were moved out Signed-off-by: Karan Shah --- plugins/airbrake/src/plugin.test.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/airbrake/src/plugin.test.tsx b/plugins/airbrake/src/plugin.test.tsx index 267f29253d..2d16004f8d 100644 --- a/plugins/airbrake/src/plugin.test.tsx +++ b/plugins/airbrake/src/plugin.test.tsx @@ -52,8 +52,6 @@ describe('Airbrake', () => { , ); - expect(rendered.getAllByRole('heading', { name: 'Airbrake' }).length).toBe( - 2, - ); + expect(rendered.getByText('ChunkLoadError')).toBeInTheDocument(); }); }); From b44a3240647773bda2595f50f823eede8a9060e0 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 29 Dec 2021 17:14:24 +0000 Subject: [PATCH 18/18] Small name refactoring and improvement to dev Signed-off-by: Karan Shah --- plugins/airbrake/api-report.md | 4 ++-- plugins/airbrake/dev/index.tsx | 9 ++++++--- plugins/airbrake/src/index.test.ts | 4 ++-- plugins/airbrake/src/index.ts | 2 +- plugins/airbrake/src/plugin.test.tsx | 4 ++-- plugins/airbrake/src/plugin.ts | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/plugins/airbrake/api-report.md b/plugins/airbrake/api-report.md index a5b1baf948..dd507ad30f 100644 --- a/plugins/airbrake/api-report.md +++ b/plugins/airbrake/api-report.md @@ -8,10 +8,10 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; -// Warning: (ae-missing-release-tag) "AirbrakePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "AirbrakeWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const AirbrakePage: () => JSX.Element; +export const AirbrakeWidget: () => JSX.Element; // Warning: (ae-missing-release-tag) "airbrakePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index a76b80a4e3..e2f971169b 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { AirbrakePage, airbrakePlugin } from '../src/plugin'; +import { AirbrakeWidget, airbrakePlugin } from '../src/plugin'; import { Content, ContentHeader, @@ -30,7 +30,10 @@ createDevApp() .addPage({ element: ( -
+
@@ -40,7 +43,7 @@ createDevApp() A description of your plugin goes here. - + ), diff --git a/plugins/airbrake/src/index.test.ts b/plugins/airbrake/src/index.test.ts index 6776fa0558..95cc4a95d9 100644 --- a/plugins/airbrake/src/index.test.ts +++ b/plugins/airbrake/src/index.test.ts @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AirbrakePage, airbrakePlugin } from './index'; +import { AirbrakeWidget, airbrakePlugin } from './index'; describe('The Airbrake plugin index file', () => { it('exports the plugin and page', async () => { - expect(AirbrakePage).toBeTruthy(); + expect(AirbrakeWidget).toBeTruthy(); expect(airbrakePlugin).toBeTruthy(); }); }); diff --git a/plugins/airbrake/src/index.ts b/plugins/airbrake/src/index.ts index 9a75b3bbea..77ca2f9867 100644 --- a/plugins/airbrake/src/index.ts +++ b/plugins/airbrake/src/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { airbrakePlugin, AirbrakePage } from './plugin'; +export { airbrakePlugin, AirbrakeWidget } from './plugin'; diff --git a/plugins/airbrake/src/plugin.test.tsx b/plugins/airbrake/src/plugin.test.tsx index 2d16004f8d..2747a5da67 100644 --- a/plugins/airbrake/src/plugin.test.tsx +++ b/plugins/airbrake/src/plugin.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import React from 'react'; -import { AirbrakePage, airbrakePlugin } from './plugin'; +import { AirbrakeWidget, airbrakePlugin } from './plugin'; import { renderWithEffects } from '@backstage/test-utils'; import { createApp } from '@backstage/app-defaults'; import { Route } from 'react-router'; @@ -48,7 +48,7 @@ describe('Airbrake', () => { const rendered = await renderWithEffects( - } /> + } /> , ); diff --git a/plugins/airbrake/src/plugin.ts b/plugins/airbrake/src/plugin.ts index 8eebd4e31c..a985724dfe 100644 --- a/plugins/airbrake/src/plugin.ts +++ b/plugins/airbrake/src/plugin.ts @@ -27,7 +27,7 @@ export const airbrakePlugin = createPlugin({ }, }); -export const AirbrakePage = airbrakePlugin.provide( +export const AirbrakeWidget = airbrakePlugin.provide( createRoutableExtension({ name: 'AirbrakePage', component: () =>