From 4d5182484cb9f404d6a841a5792ee881d77ae8a6 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 22 Dec 2021 14:59:19 +0000 Subject: [PATCH] Generate the initial plugin code Signed-off-by: Karan Shah --- microsite/data/plugins/airbrake.yaml | 4 +- packages/app/package.json | 1 + 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 +++ 18 files changed, 487 insertions(+), 2 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/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 1dd48eb7bd..d5c8297db6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -11,6 +11,7 @@ "@backstage/core-components": "^0.8.2", "@backstage/core-plugin-api": "^0.4.0", "@backstage/integration-react": "^0.1.16", + "@backstage/plugin-airbrake": "^0.0.0", "@backstage/plugin-api-docs": "^0.6.19", "@backstage/plugin-azure-devops": "^0.1.8", "@backstage/plugin-apache-airflow": "^0.1.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';