From 135df6accc042471807536659e79cec942212fb8 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Mon, 10 Jan 2022 10:52:02 +0000 Subject: [PATCH 01/34] Setup the API code Signed-off-by: Karan Shah --- plugins/airbrake/src/api/airbrake-api.ts | 26 ++++++ plugins/airbrake/src/api/airbrake-groups.d.ts | 65 ++++++++++++++ plugins/airbrake/src/api/index.ts | 21 +++++ .../api/mock/airbrake-groups-api-mock.json | 85 +++++++++++++++++++ plugins/airbrake/src/api/mock/index.ts | 17 ++++ plugins/airbrake/src/api/mock/mock-api.ts | 27 ++++++ plugins/airbrake/src/api/production-api.ts | 37 ++++++++ 7 files changed, 278 insertions(+) create mode 100644 plugins/airbrake/src/api/airbrake-api.ts create mode 100644 plugins/airbrake/src/api/airbrake-groups.d.ts create mode 100644 plugins/airbrake/src/api/index.ts create mode 100644 plugins/airbrake/src/api/mock/airbrake-groups-api-mock.json create mode 100644 plugins/airbrake/src/api/mock/index.ts create mode 100644 plugins/airbrake/src/api/mock/mock-api.ts create mode 100644 plugins/airbrake/src/api/production-api.ts diff --git a/plugins/airbrake/src/api/airbrake-api.ts b/plugins/airbrake/src/api/airbrake-api.ts new file mode 100644 index 0000000000..1a3cfa72e4 --- /dev/null +++ b/plugins/airbrake/src/api/airbrake-api.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Groups } from './airbrake-groups'; +import { createApiRef } from '@backstage/core-plugin-api'; + +export const airbrakeApiRef = createApiRef({ + id: 'plugin.airbrake.service', +}); + +export interface AirbrakeApi { + fetchGroups(project: string): Promise; +} diff --git a/plugins/airbrake/src/api/airbrake-groups.d.ts b/plugins/airbrake/src/api/airbrake-groups.d.ts new file mode 100644 index 0000000000..df4c5de120 --- /dev/null +++ b/plugins/airbrake/src/api/airbrake-groups.d.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2022 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 interface Groups { + count: number; + end: string; + groups?: Group[] | null; + page: number; + resolvedCount: number; + unresolvedCount: number; +} + +export interface Group { + id: string; + projectId: number; + resolved: boolean; + muted: boolean; + mutedBy: number; + mutedAt?: null; + errors?: Error[] | null; + attributes?: null; + context: Context; + lastDeployId: string; + lastDeployAt?: null; + lastNoticeId: string; + lastNoticeAt: string; + noticeCount: number; + noticeTotalCount: number; + commentCount: number; + createdAt: string; +} + +export interface Error { + type: string; + message: string; + backtrace?: Backtrace[] | null; +} + +export interface Backtrace { + file: string; + function: string; + line: number; + column: number; + code?: null; +} + +export interface Context { + action: string; + component: string; + environment: string; + severity: string; +} diff --git a/plugins/airbrake/src/api/index.ts b/plugins/airbrake/src/api/index.ts new file mode 100644 index 0000000000..7bad94e298 --- /dev/null +++ b/plugins/airbrake/src/api/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './mock'; +export type { AirbrakeApi } from './airbrake-api'; +export { airbrakeApiRef } from './airbrake-api'; +export type { Groups } from './airbrake-groups'; +export { ProductionAirbrakeApi } from './production-api'; diff --git a/plugins/airbrake/src/api/mock/airbrake-groups-api-mock.json b/plugins/airbrake/src/api/mock/airbrake-groups-api-mock.json new file mode 100644 index 0000000000..fa654ec50b --- /dev/null +++ b/plugins/airbrake/src/api/mock/airbrake-groups-api-mock.json @@ -0,0 +1,85 @@ +{ + "count": 2, + "end": "", + "groups": [ + { + "id": "1", + "projectId": 123, + "resolved": false, + "muted": false, + "mutedBy": 0, + "mutedAt": null, + "errors": [ + { + "type": "Error", + "message": "useSearch must be used within a SearchContextProvider", + "backtrace": [ + { + "file": "webpack-internal:///../../node_modules/@backstage/plugin-search/dist/esm/index-893ec2f5.esm.js", + "function": "useSearch", + "line": 303, + "column": 11, + "code": null + } + ] + } + ], + "attributes": null, + "context": { + "action": "", + "component": "", + "environment": "local", + "severity": "error" + }, + "lastDeployId": "0", + "lastDeployAt": null, + "lastNoticeId": "234", + "lastNoticeAt": "2021-12-19T09:59:00.124Z", + "noticeCount": 5, + "noticeTotalCount": 5, + "commentCount": 0, + "createdAt": "2021-12-19T09:44:30.067447Z" + }, + { + "id": "2", + "projectId": 123, + "resolved": true, + "muted": false, + "mutedBy": 0, + "mutedAt": null, + "errors": [ + { + "type": "ChunkLoadError", + "message": "Loading chunk 7764 failed.", + "backtrace": [ + { + "file": "/PROJECT_ROOT/static/runtime.069c874d.js", + "function": "Object._.f.j", + "line": 1, + "column": 19465, + "code": null + } + ] + } + ], + "attributes": null, + "context": { + "action": "", + "component": "", + "environment": "local", + "severity": "error" + }, + "lastDeployId": "0", + "lastDeployAt": null, + "lastNoticeId": "345", + "lastNoticeAt": "2021-12-15T17:16:38.419Z", + "noticeCount": 1, + "noticeTotalCount": 1, + "commentCount": 0, + "createdAt": "2021-12-15T17:16:38.41983Z" + } + ], + "page": 1, + "resolvedCount": 1, + "unresolvedCount": 1 +} diff --git a/plugins/airbrake/src/api/mock/index.ts b/plugins/airbrake/src/api/mock/index.ts new file mode 100644 index 0000000000..f084268951 --- /dev/null +++ b/plugins/airbrake/src/api/mock/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { MockAirbrakeApi } from './mock-api'; diff --git a/plugins/airbrake/src/api/mock/mock-api.ts b/plugins/airbrake/src/api/mock/mock-api.ts new file mode 100644 index 0000000000..df0293da7c --- /dev/null +++ b/plugins/airbrake/src/api/mock/mock-api.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Groups } from '../airbrake-groups'; +import { AirbrakeApi } from '../airbrake-api'; +import mockGroupsData from './airbrake-groups-api-mock.json'; + +export class MockAirbrakeApi implements AirbrakeApi { + fetchGroups(): Promise { + return new Promise(resolve => { + setTimeout(() => resolve(mockGroupsData), 800); + }); + } +} diff --git a/plugins/airbrake/src/api/production-api.ts b/plugins/airbrake/src/api/production-api.ts new file mode 100644 index 0000000000..37ac938752 --- /dev/null +++ b/plugins/airbrake/src/api/production-api.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Groups } from './airbrake-groups'; +import { AirbrakeApi } from './airbrake-api'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; + +export class ProductionAirbrakeApi implements AirbrakeApi { + constructor(private readonly discoveryApi: DiscoveryApi) {} + + async fetchGroups(project: string): Promise { + const apiUrl = `${await this.discoveryApi.getBaseUrl( + 'proxy', + )}/airbrake/api`; + + const response = await fetch(`${apiUrl}/v4/projects/${project}/groups`); + + if (response.status >= 400 && response.status < 600) { + throw new Error('Failed fetching Airbrake issues'); + } + + return (await response.json()) as Groups; + } +} From f3d7baaefa248681c8db3e0829f95cf02af40934 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Mon, 10 Jan 2022 11:49:23 +0000 Subject: [PATCH 02/34] Connect the API code to the components Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 1 + plugins/airbrake/src/api/airbrake-groups.d.ts | 12 +-- .../EntityAirbrakeContent.tsx | 65 ++++++++++++-- .../EntityAirbrakeContent/example-data.json | 85 ------------------- .../airbrake/src/components/useProjectSlug.ts | 23 +++++ 5 files changed, 87 insertions(+), 99 deletions(-) delete mode 100644 plugins/airbrake/src/components/EntityAirbrakeContent/example-data.json create mode 100644 plugins/airbrake/src/components/useProjectSlug.ts diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 7294c97413..7f96f0bc61 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -20,6 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-model": "^0.9.7", "@backstage/core-components": "^0.8.3", "@backstage/core-plugin-api": "^0.4.1", "@backstage/theme": "^0.2.14", diff --git a/plugins/airbrake/src/api/airbrake-groups.d.ts b/plugins/airbrake/src/api/airbrake-groups.d.ts index df4c5de120..e1887eca9b 100644 --- a/plugins/airbrake/src/api/airbrake-groups.d.ts +++ b/plugins/airbrake/src/api/airbrake-groups.d.ts @@ -29,12 +29,12 @@ export interface Group { resolved: boolean; muted: boolean; mutedBy: number; - mutedAt?: null; - errors?: Error[] | null; - attributes?: null; + mutedAt?: string | null; + errors?: Error[]; + attributes?: string | null; context: Context; lastDeployId: string; - lastDeployAt?: null; + lastDeployAt?: string | null; lastNoticeId: string; lastNoticeAt: string; noticeCount: number; @@ -46,7 +46,7 @@ export interface Group { export interface Error { type: string; message: string; - backtrace?: Backtrace[] | null; + backtrace?: Backtrace[]; } export interface Backtrace { @@ -54,7 +54,7 @@ export interface Backtrace { function: string; line: number; column: number; - code?: null; + code?: string | null; } export interface Context { diff --git a/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.tsx b/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.tsx index 062d9e898a..2bd3917443 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.tsx @@ -13,13 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import React, { useEffect } from 'react'; import { Grid, Typography } from '@material-ui/core'; -import { InfoCard } from '@backstage/core-components'; -import exampleData from './example-data.json'; +import { + EmptyState, + InfoCard, + MissingAnnotationEmptyState, + Progress, +} from '@backstage/core-components'; import hash from 'object-hash'; import { makeStyles } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; +import { ErrorApi, errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { airbrakeApiRef } from '../../api'; +import useAsync from 'react-use/lib/useAsync'; +import { + AIRBRAKE_PROJECT_SLUG_ANNOTATION, + useProjectSlug, +} from '../useProjectSlug'; const useStyles = makeStyles(() => ({ multilineText: { @@ -27,17 +39,54 @@ const useStyles = makeStyles(() => ({ }, })); -export const EntityAirbrakeContent = () => { +export const EntityAirbrakeContent = ({ entity }: { entity: Entity }) => { const classes = useStyles(); + const projectId = useProjectSlug(entity); + const errorApi = useApi(errorApiRef); + const airbrakeApi = useApi(airbrakeApiRef); + + const { loading, value, error } = useAsync( + () => airbrakeApi.fetchGroups(projectId), + [projectId], + ); + + useEffect(() => { + if (error) { + errorApi.post(error); + } + }, [error, errorApi]); + + if (loading || !projectId || error) { + return ( + + {loading && } + + {!loading && !projectId && ( + + )} + + {!loading && error && ( + + )} + + ); + } + return ( - {exampleData.groups.map(group => ( + {value?.groups?.map(group => ( - {group.errors.map(error => ( - + {group.errors?.map(groupError => ( + - {error.message} + {groupError.message} ))} diff --git a/plugins/airbrake/src/components/EntityAirbrakeContent/example-data.json b/plugins/airbrake/src/components/EntityAirbrakeContent/example-data.json deleted file mode 100644 index fa654ec50b..0000000000 --- a/plugins/airbrake/src/components/EntityAirbrakeContent/example-data.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "count": 2, - "end": "", - "groups": [ - { - "id": "1", - "projectId": 123, - "resolved": false, - "muted": false, - "mutedBy": 0, - "mutedAt": null, - "errors": [ - { - "type": "Error", - "message": "useSearch must be used within a SearchContextProvider", - "backtrace": [ - { - "file": "webpack-internal:///../../node_modules/@backstage/plugin-search/dist/esm/index-893ec2f5.esm.js", - "function": "useSearch", - "line": 303, - "column": 11, - "code": null - } - ] - } - ], - "attributes": null, - "context": { - "action": "", - "component": "", - "environment": "local", - "severity": "error" - }, - "lastDeployId": "0", - "lastDeployAt": null, - "lastNoticeId": "234", - "lastNoticeAt": "2021-12-19T09:59:00.124Z", - "noticeCount": 5, - "noticeTotalCount": 5, - "commentCount": 0, - "createdAt": "2021-12-19T09:44:30.067447Z" - }, - { - "id": "2", - "projectId": 123, - "resolved": true, - "muted": false, - "mutedBy": 0, - "mutedAt": null, - "errors": [ - { - "type": "ChunkLoadError", - "message": "Loading chunk 7764 failed.", - "backtrace": [ - { - "file": "/PROJECT_ROOT/static/runtime.069c874d.js", - "function": "Object._.f.j", - "line": 1, - "column": 19465, - "code": null - } - ] - } - ], - "attributes": null, - "context": { - "action": "", - "component": "", - "environment": "local", - "severity": "error" - }, - "lastDeployId": "0", - "lastDeployAt": null, - "lastNoticeId": "345", - "lastNoticeAt": "2021-12-15T17:16:38.419Z", - "noticeCount": 1, - "noticeTotalCount": 1, - "commentCount": 0, - "createdAt": "2021-12-15T17:16:38.41983Z" - } - ], - "page": 1, - "resolvedCount": 1, - "unresolvedCount": 1 -} diff --git a/plugins/airbrake/src/components/useProjectSlug.ts b/plugins/airbrake/src/components/useProjectSlug.ts new file mode 100644 index 0000000000..4c6337b49e --- /dev/null +++ b/plugins/airbrake/src/components/useProjectSlug.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; + +export const AIRBRAKE_PROJECT_SLUG_ANNOTATION = 'airbrake.io/project-slug'; + +export const useProjectSlug = (entity: Entity) => { + return entity?.metadata.annotations?.[AIRBRAKE_PROJECT_SLUG_ANNOTATION] ?? ''; +}; From b7e049377029e2fb699d1d2c9fd6be111e7bb89a Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Mon, 10 Jan 2022 13:51:33 +0000 Subject: [PATCH 03/34] Documentation should just point to the plugin folder, not the README file directly. Signed-off-by: Karan Shah --- microsite/data/plugins/airbrake.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/airbrake.yaml b/microsite/data/plugins/airbrake.yaml index 96de75c002..29c1f394d7 100644 --- a/microsite/data/plugins/airbrake.yaml +++ b/microsite/data/plugins/airbrake.yaml @@ -4,6 +4,6 @@ author: Simply Business authorUrl: https://github.com/simplybusiness/ category: Monitoring description: Access Airbrake error monitoring and other integrations from within Backstage -documentation: https://github.com/backstage/backstage/blob/master/plugins/airbrake/README.md +documentation: https://github.com/backstage/backstage/blob/master/plugins/airbrake iconUrl: https://wp-assets.airbrake.io/wp-content/uploads/2020/10/05222904/Square-white-A-on-Orange.png npmPackageName: '@backstage/plugin-airbrake' From d16c977381980614e529a65a28bc6b5dc5a2e352 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Mon, 10 Jan 2022 16:27:46 +0000 Subject: [PATCH 04/34] Incorporate the API changes to the rest of the code. Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 2 +- plugins/airbrake/package.json | 1 + .../EntityAirbrakeWidget.test.tsx} | 22 +++++++++-- .../EntityAirbrakeWidget.tsx} | 2 +- .../components/EntityAirbrakeWidget/index.ts | 17 +++++++++ ...keContent.test.tsx => extensions.test.tsx} | 2 +- plugins/airbrake/src/extensions.tsx | 38 +++++++++++++++++++ plugins/airbrake/src/index.ts | 3 +- plugins/airbrake/src/plugin.ts | 28 ++++++++------ 9 files changed, 96 insertions(+), 19 deletions(-) rename plugins/airbrake/src/components/{EntityAirbrakeContent/EntityAirbrakeContent.test.tsx => EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx} (61%) rename plugins/airbrake/src/components/{EntityAirbrakeContent/EntityAirbrakeContent.tsx => EntityAirbrakeWidget/EntityAirbrakeWidget.tsx} (97%) create mode 100644 plugins/airbrake/src/components/EntityAirbrakeWidget/index.ts rename plugins/airbrake/src/{EntityAirbrakeContent.test.tsx => extensions.test.tsx} (94%) create mode 100644 plugins/airbrake/src/extensions.tsx diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index e74ce68ee5..4c52e27094 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 { EntityAirbrakeContent, airbrakePlugin } from '../src/plugin'; +import { EntityAirbrakeContent, airbrakePlugin } from '../src'; import { Content, ContentHeader, diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 7f96f0bc61..5ebfae651f 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -23,6 +23,7 @@ "@backstage/catalog-model": "^0.9.7", "@backstage/core-components": "^0.8.3", "@backstage/core-plugin-api": "^0.4.1", + "@backstage/plugin-catalog-react": "^0.6.10", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx similarity index 61% rename from plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.test.tsx rename to plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index a621eb281c..d93c642e6c 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -15,13 +15,29 @@ */ import React from 'react'; -import { EntityAirbrakeContent } from './EntityAirbrakeContent'; -import exampleData from './example-data.json'; +import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; +import exampleData from '../../api/mock/airbrake-groups-api-mock.json'; import { renderInTestApp } from '@backstage/test-utils'; +import { AIRBRAKE_PROJECT_SLUG_ANNOTATION } from '../useProjectSlug'; +import { Entity } from '@backstage/catalog-model'; describe('EntityAirbrakeContent', () => { + const entity = (name?: string) => + ({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + annotations: { + [AIRBRAKE_PROJECT_SLUG_ANNOTATION]: name, + }, + name: name, + }, + } as Entity); + it('renders all errors sent from Airbrake', async () => { - const table = await renderInTestApp(); + const table = await renderInTestApp( + , + ); expect(exampleData.groups.length).toBeGreaterThan(0); for (const group of exampleData.groups) { expect( diff --git a/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx similarity index 97% rename from plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.tsx rename to plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx index 2bd3917443..53f7d63f13 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeContent/EntityAirbrakeContent.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx @@ -39,7 +39,7 @@ const useStyles = makeStyles(() => ({ }, })); -export const EntityAirbrakeContent = ({ entity }: { entity: Entity }) => { +export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => { const classes = useStyles(); const projectId = useProjectSlug(entity); diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/index.ts b/plugins/airbrake/src/components/EntityAirbrakeWidget/index.ts new file mode 100644 index 0000000000..0a85263c5f --- /dev/null +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; diff --git a/plugins/airbrake/src/EntityAirbrakeContent.test.tsx b/plugins/airbrake/src/extensions.test.tsx similarity index 94% rename from plugins/airbrake/src/EntityAirbrakeContent.test.tsx rename to plugins/airbrake/src/extensions.test.tsx index f0afef425f..db8670ac50 100644 --- a/plugins/airbrake/src/EntityAirbrakeContent.test.tsx +++ b/plugins/airbrake/src/extensions.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import React from 'react'; -import { EntityAirbrakeContent } from './plugin'; +import { EntityAirbrakeContent } from './extensions'; import { renderInTestApp } from '@backstage/test-utils'; describe('The Airbrake entity', () => { diff --git a/plugins/airbrake/src/extensions.tsx b/plugins/airbrake/src/extensions.tsx new file mode 100644 index 0000000000..582088b250 --- /dev/null +++ b/plugins/airbrake/src/extensions.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useEntity } from '@backstage/plugin-catalog-react'; +import React from 'react'; +import { airbrakePlugin } from './plugin'; +import { createRoutableExtension } from '@backstage/core-plugin-api'; +import { rootRouteRef } from './routes'; + +export const EntityAirbrakeContent = airbrakePlugin.provide( + createRoutableExtension({ + name: 'EntityAirbrakeContent', + mountPoint: rootRouteRef, + component: () => + import('./components/EntityAirbrakeWidget').then( + ({ EntityAirbrakeWidget }) => { + const AirbrakePage = () => { + const { entity } = useEntity(); + return ; + }; + return AirbrakePage; + }, + ), + }), +); diff --git a/plugins/airbrake/src/index.ts b/plugins/airbrake/src/index.ts index 8f319e02fd..d5aedd9ba6 100644 --- a/plugins/airbrake/src/index.ts +++ b/plugins/airbrake/src/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { airbrakePlugin, EntityAirbrakeContent } from './plugin'; +export { airbrakePlugin } from './plugin'; +export { EntityAirbrakeContent } from './extensions'; diff --git a/plugins/airbrake/src/plugin.ts b/plugins/airbrake/src/plugin.ts index 0a3679baa4..30dc6749c5 100644 --- a/plugins/airbrake/src/plugin.ts +++ b/plugins/airbrake/src/plugin.ts @@ -14,26 +14,30 @@ * limitations under the License. */ import { + configApiRef, + createApiFactory, createPlugin, - createRoutableExtension, + discoveryApiRef, + identityApiRef, } from '@backstage/core-plugin-api'; import { rootRouteRef } from './routes'; +import { airbrakeApiRef, ProductionAirbrakeApi } from './api'; export const airbrakePlugin = createPlugin({ id: 'airbrake', + apis: [ + createApiFactory({ + api: airbrakeApiRef, + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + }, + factory: ({ discoveryApi }) => new ProductionAirbrakeApi(discoveryApi), + }), + ], routes: { root: rootRouteRef, }, }); - -export const EntityAirbrakeContent = airbrakePlugin.provide( - createRoutableExtension({ - name: 'EntityAirbrakeContent', - component: () => - import('./components/EntityAirbrakeContent/EntityAirbrakeContent').then( - m => m.EntityAirbrakeContent, - ), - mountPoint: rootRouteRef, - }), -); From 32ab08a4b14e4a2ef1b538fee7a5f58d368367cd Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 11 Jan 2022 09:35:22 +0000 Subject: [PATCH 05/34] Register the mock API for the dev app Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 6 ++++++ plugins/airbrake/src/plugin.ts | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index 4c52e27094..ec6c4fd0b6 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -24,9 +24,15 @@ import { Page, SupportButton, } from '@backstage/core-components'; +import { airbrakeApiRef, MockAirbrakeApi } from '../src/api'; createDevApp() .registerPlugin(airbrakePlugin) + .registerApi({ + api: airbrakeApiRef, + deps: {}, + factory: () => new MockAirbrakeApi(), + }) .addPage({ element: ( diff --git a/plugins/airbrake/src/plugin.ts b/plugins/airbrake/src/plugin.ts index 30dc6749c5..5a58f5b4b3 100644 --- a/plugins/airbrake/src/plugin.ts +++ b/plugins/airbrake/src/plugin.ts @@ -14,11 +14,9 @@ * limitations under the License. */ import { - configApiRef, createApiFactory, createPlugin, discoveryApiRef, - identityApiRef, } from '@backstage/core-plugin-api'; import { rootRouteRef } from './routes'; @@ -30,9 +28,7 @@ export const airbrakePlugin = createPlugin({ createApiFactory({ api: airbrakeApiRef, deps: { - configApi: configApiRef, discoveryApi: discoveryApiRef, - identityApi: identityApiRef, }, factory: ({ discoveryApi }) => new ProductionAirbrakeApi(discoveryApi), }), From 87ffa4698ddc6bd1a8b3c655a831ea8a1db36567 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 11 Jan 2022 10:14:21 +0000 Subject: [PATCH 06/34] Create a mock entity Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 8 +++-- plugins/airbrake/src/api/mock/mock-entity.ts | 29 +++++++++++++++++++ .../EntityAirbrakeWidget.test.tsx | 15 +--------- plugins/airbrake/src/extensions.tsx | 3 +- 4 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 plugins/airbrake/src/api/mock/mock-entity.ts diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index ec6c4fd0b6..cd980408ae 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 { EntityAirbrakeContent, airbrakePlugin } from '../src'; +import { airbrakePlugin, EntityAirbrakeContent } from '../src'; import { Content, ContentHeader, @@ -25,6 +25,8 @@ import { SupportButton, } from '@backstage/core-components'; import { airbrakeApiRef, MockAirbrakeApi } from '../src/api'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { entity } from '../src/api/mock/mock-entity'; createDevApp() .registerPlugin(airbrakePlugin) @@ -49,7 +51,9 @@ createDevApp() A description of your plugin goes here. - + + + ), diff --git a/plugins/airbrake/src/api/mock/mock-entity.ts b/plugins/airbrake/src/api/mock/mock-entity.ts new file mode 100644 index 0000000000..7e5b897cca --- /dev/null +++ b/plugins/airbrake/src/api/mock/mock-entity.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2022 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 { AIRBRAKE_PROJECT_SLUG_ANNOTATION } from '../../components/useProjectSlug'; +import { Entity } from '@backstage/catalog-model'; + +export const entity = (name?: string) => + ({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + annotations: { + [AIRBRAKE_PROJECT_SLUG_ANNOTATION]: name, + }, + name: name, + }, + } as Entity); diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index d93c642e6c..000bd8f230 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -18,22 +18,9 @@ import React from 'react'; import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; import exampleData from '../../api/mock/airbrake-groups-api-mock.json'; import { renderInTestApp } from '@backstage/test-utils'; -import { AIRBRAKE_PROJECT_SLUG_ANNOTATION } from '../useProjectSlug'; -import { Entity } from '@backstage/catalog-model'; +import { entity } from '../../api/mock/mock-entity'; describe('EntityAirbrakeContent', () => { - const entity = (name?: string) => - ({ - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - annotations: { - [AIRBRAKE_PROJECT_SLUG_ANNOTATION]: name, - }, - name: name, - }, - } as Entity); - it('renders all errors sent from Airbrake', async () => { const table = await renderInTestApp( , diff --git a/plugins/airbrake/src/extensions.tsx b/plugins/airbrake/src/extensions.tsx index 582088b250..6fbea68409 100644 --- a/plugins/airbrake/src/extensions.tsx +++ b/plugins/airbrake/src/extensions.tsx @@ -27,11 +27,10 @@ export const EntityAirbrakeContent = airbrakePlugin.provide( component: () => import('./components/EntityAirbrakeWidget').then( ({ EntityAirbrakeWidget }) => { - const AirbrakePage = () => { + return () => { const { entity } = useEntity(); return ; }; - return AirbrakePage; }, ), }), From 35ab9befbaddb8aa8832df66613a16f7091d96c9 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 11 Jan 2022 10:45:29 +0000 Subject: [PATCH 07/34] Rename entity() to createEntity() Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 4 ++-- plugins/airbrake/src/api/mock/mock-entity.ts | 2 +- .../EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index cd980408ae..8c3dd2f643 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -26,7 +26,7 @@ import { } from '@backstage/core-components'; import { airbrakeApiRef, MockAirbrakeApi } from '../src/api'; import { EntityProvider } from '@backstage/plugin-catalog-react'; -import { entity } from '../src/api/mock/mock-entity'; +import { createEntity } from '../src/api/mock/mock-entity'; createDevApp() .registerPlugin(airbrakePlugin) @@ -51,7 +51,7 @@ createDevApp() A description of your plugin goes here. - + diff --git a/plugins/airbrake/src/api/mock/mock-entity.ts b/plugins/airbrake/src/api/mock/mock-entity.ts index 7e5b897cca..b169938004 100644 --- a/plugins/airbrake/src/api/mock/mock-entity.ts +++ b/plugins/airbrake/src/api/mock/mock-entity.ts @@ -16,7 +16,7 @@ import { AIRBRAKE_PROJECT_SLUG_ANNOTATION } from '../../components/useProjectSlug'; import { Entity } from '@backstage/catalog-model'; -export const entity = (name?: string) => +export const createEntity = (name?: string) => ({ apiVersion: 'backstage.io/v1alpha1', kind: 'Component', diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 000bd8f230..3c63f84f19 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -18,12 +18,12 @@ import React from 'react'; import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; import exampleData from '../../api/mock/airbrake-groups-api-mock.json'; import { renderInTestApp } from '@backstage/test-utils'; -import { entity } from '../../api/mock/mock-entity'; +import { createEntity } from '../../api/mock/mock-entity'; describe('EntityAirbrakeContent', () => { it('renders all errors sent from Airbrake', async () => { const table = await renderInTestApp( - , + , ); expect(exampleData.groups.length).toBeGreaterThan(0); for (const group of exampleData.groups) { From 4b794662c184fe1af07e3b413e33cc32cc8dd301 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 25 Jan 2022 10:40:40 +0000 Subject: [PATCH 08/34] Added the UI ability to change APIs Signed-off-by: Karan Shah --- .../airbrake/dev/components/ApiBar/ApiBar.tsx | 62 +++++++++++++++++++ .../airbrake/dev/components/ApiBar/index.ts | 17 +++++ plugins/airbrake/dev/index.tsx | 35 +++++------ 3 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 plugins/airbrake/dev/components/ApiBar/ApiBar.tsx create mode 100644 plugins/airbrake/dev/components/ApiBar/index.ts diff --git a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx new file mode 100644 index 0000000000..f86cb348ba --- /dev/null +++ b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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, { useState } from 'react'; +import { Select, SelectItem } from '@backstage/core-components'; +import { makeStyles, TextField } from '@material-ui/core'; + +const useStyles = makeStyles({ + root: { + display: 'flex', + gap: '1em', + flexWrap: 'wrap', + }, +}); + +export const ApiBar = () => { + const classes = useStyles(); + const apiOptions: SelectItem[] = [ + { label: 'Fake', value: 'fake' }, + { label: 'Real', value: 'real' }, + ]; + const [api, setApi] = useState('fake'); + const [projectId, setProjectId] = useState(); + const [apiKey, setApiKey] = useState(''); + + return ( +
+ setApi(newValue as string)} - /> + value={api} + onChange={e => setApi(e.target.value)} + > + {apiOptions.map(option => ( + + {option.label} + + ))} + {api === 'real' && ( <> Date: Wed, 26 Jan 2022 10:27:54 +0000 Subject: [PATCH 10/34] Start moving towards 2 pages Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index 17bbcf3d18..36f271a995 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -21,6 +21,8 @@ import { ApiBar } from './components/ApiBar'; import { Content, Header, Page } from '@backstage/core-components'; import { EntityProvider } from '@backstage/plugin-catalog-react'; import { createEntity } from '../src/api/mock/mock-entity'; +import AirplanemodeActiveIcon from '@material-ui/icons/AirplanemodeActive'; +import AirplanemodeInactiveIcon from '@material-ui/icons/AirplanemodeInactive'; createDevApp() .registerPlugin(airbrakePlugin) @@ -29,6 +31,32 @@ createDevApp() deps: {}, factory: () => new MockAirbrakeApi(), }) + .addPage({ + element: ( + +
+ +
+ + + +
+
+ + ), + title: 'Mock API', + path: '/airbrake-mock-api', + icon: AirplanemodeActiveIcon, + }) .addPage({ element: ( @@ -52,7 +80,8 @@ createDevApp() ), - title: 'Root Page', - path: '/airbrake', + title: 'Real API', + path: '/airbrake-real-api', + icon: AirplanemodeInactiveIcon, }) .render(); From de5133035009e4d7b3f05f40566315b80802442d Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 26 Jan 2022 10:42:20 +0000 Subject: [PATCH 11/34] Added in FlexColumn.tsx Signed-off-by: Karan Shah --- .../airbrake/dev/components/ApiBar/ApiBar.tsx | 41 +++++-------------- .../dev/components/FlexColumn/FlexColumn.tsx | 34 +++++++++++++++ .../dev/components/FlexColumn/index.ts | 18 ++++++++ plugins/airbrake/dev/index.tsx | 21 +++------- 4 files changed, 67 insertions(+), 47 deletions(-) create mode 100644 plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx create mode 100644 plugins/airbrake/dev/components/FlexColumn/index.ts diff --git a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx index d782113dc0..5c2b71ca14 100644 --- a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx +++ b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import React, { useState } from 'react'; -import { makeStyles, MenuItem, TextField } from '@material-ui/core'; +import { makeStyles, TextField } from '@material-ui/core'; const useStyles = makeStyles({ root: { @@ -26,42 +26,21 @@ const useStyles = makeStyles({ export const ApiBar = () => { const classes = useStyles(); - const apiOptions = [ - { label: 'Fake', value: 'fake' }, - { label: 'Real', value: 'real' }, - ]; - const [api, setApi] = useState('fake'); const [projectId, setProjectId] = useState(); const [apiKey, setApiKey] = useState(''); return (
setApi(e.target.value)} - > - {apiOptions.map(option => ( - - {option.label} - - ))} - - {api === 'real' && ( - <> - setProjectId(parseInt(e.target.value, 10))} - /> - setApiKey(e.target.value)} - /> - - )} + label="Project ID" + value={projectId} + onChange={e => setProjectId(parseInt(e.target.value, 10))} + /> + setApiKey(e.target.value)} + />
); }; diff --git a/plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx b/plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx new file mode 100644 index 0000000000..aaf64d3513 --- /dev/null +++ b/plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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, { PropsWithChildren, ReactNode } from 'react'; + +export declare type FlexColumnProps = { + children?: ReactNode; +}; + +export const FlexColumn = (props: PropsWithChildren) => { + return ( +
+ {props.children} +
+ ); +}; diff --git a/plugins/airbrake/dev/components/FlexColumn/index.ts b/plugins/airbrake/dev/components/FlexColumn/index.ts new file mode 100644 index 0000000000..5926c3cb28 --- /dev/null +++ b/plugins/airbrake/dev/components/FlexColumn/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { FlexColumn } from './FlexColumn'; +export type { FlexColumnProps } from './FlexColumn'; diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index 36f271a995..ba520892cb 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -23,6 +23,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-react'; import { createEntity } from '../src/api/mock/mock-entity'; import AirplanemodeActiveIcon from '@material-ui/icons/AirplanemodeActive'; import AirplanemodeInactiveIcon from '@material-ui/icons/AirplanemodeInactive'; +import { FlexColumn } from './components/FlexColumn'; createDevApp() .registerPlugin(airbrakePlugin) @@ -39,17 +40,11 @@ createDevApp() subtitle="This uses a fake API" /> -
+ -
+
), @@ -65,18 +60,12 @@ createDevApp() subtitle="Test the plugin below" /> -
+ -
+
), From 39a5bfb958f74f2a6d891c1946a1ec77decaa747 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 26 Jan 2022 10:53:42 +0000 Subject: [PATCH 12/34] Improved the icons Signed-off-by: Karan Shah --- .../dev/components/FlexColumn/FlexColumn.tsx | 34 ----------------- .../dev/components/FlexColumn/index.ts | 18 --------- plugins/airbrake/dev/index.tsx | 38 +++++++------------ 3 files changed, 14 insertions(+), 76 deletions(-) delete mode 100644 plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx delete mode 100644 plugins/airbrake/dev/components/FlexColumn/index.ts diff --git a/plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx b/plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx deleted file mode 100644 index aaf64d3513..0000000000 --- a/plugins/airbrake/dev/components/FlexColumn/FlexColumn.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2022 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, { PropsWithChildren, ReactNode } from 'react'; - -export declare type FlexColumnProps = { - children?: ReactNode; -}; - -export const FlexColumn = (props: PropsWithChildren) => { - return ( -
- {props.children} -
- ); -}; diff --git a/plugins/airbrake/dev/components/FlexColumn/index.ts b/plugins/airbrake/dev/components/FlexColumn/index.ts deleted file mode 100644 index 5926c3cb28..0000000000 --- a/plugins/airbrake/dev/components/FlexColumn/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { FlexColumn } from './FlexColumn'; -export type { FlexColumnProps } from './FlexColumn'; diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index ba520892cb..4163590069 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -21,9 +21,8 @@ import { ApiBar } from './components/ApiBar'; import { Content, Header, Page } from '@backstage/core-components'; import { EntityProvider } from '@backstage/plugin-catalog-react'; import { createEntity } from '../src/api/mock/mock-entity'; -import AirplanemodeActiveIcon from '@material-ui/icons/AirplanemodeActive'; -import AirplanemodeInactiveIcon from '@material-ui/icons/AirplanemodeInactive'; -import { FlexColumn } from './components/FlexColumn'; +import CloudOffIcon from '@material-ui/icons/CloudOff'; +import CloudIcon from '@material-ui/icons/Cloud'; createDevApp() .registerPlugin(airbrakePlugin) @@ -35,42 +34,33 @@ createDevApp() .addPage({ element: ( -
+
- - - - - + + + ), title: 'Mock API', path: '/airbrake-mock-api', - icon: AirplanemodeActiveIcon, + icon: CloudOffIcon, }) .addPage({ element: ( -
+
+ +
- - - - - - + + + ), title: 'Real API', path: '/airbrake-real-api', - icon: AirplanemodeInactiveIcon, + icon: CloudIcon, }) .render(); From 1c2ba8ccdeeb83298aa601878ea7d246bb1837a2 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 27 Jan 2022 10:47:41 +0000 Subject: [PATCH 13/34] Added state to the top level for the real API Signed-off-by: Karan Shah --- .../airbrake/dev/components/ApiBar/ApiBar.tsx | 33 +++++++------ .../ContextProvider/ContextProvider.tsx | 48 +++++++++++++++++++ .../dev/components/ContextProvider/index.ts | 17 +++++++ plugins/airbrake/dev/index.tsx | 29 +++++++---- 4 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx create mode 100644 plugins/airbrake/dev/components/ContextProvider/index.ts diff --git a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx index 5c2b71ca14..bd8079420f 100644 --- a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx +++ b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState } from 'react'; +import React from 'react'; import { makeStyles, TextField } from '@material-ui/core'; +import { Context } from '../ContextProvider'; const useStyles = makeStyles({ root: { @@ -26,21 +27,23 @@ const useStyles = makeStyles({ export const ApiBar = () => { const classes = useStyles(); - const [projectId, setProjectId] = useState(); - const [apiKey, setApiKey] = useState(''); return ( -
- setProjectId(parseInt(e.target.value, 10))} - /> - setApiKey(e.target.value)} - /> -
+ + {value => ( +
+ value.setProjectId?.(parseInt(e.target.value, 10))} + /> + value.setApiKey?.(e.target.value)} + /> +
+ )} +
); }; diff --git a/plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx b/plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx new file mode 100644 index 0000000000..bbf72f3b99 --- /dev/null +++ b/plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx @@ -0,0 +1,48 @@ +/* + * Copyright 2022 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, { + Dispatch, + PropsWithChildren, + SetStateAction, + useState, +} from 'react'; + +interface ContextInterface { + projectId?: number; + setProjectId?: Dispatch>; + apiKey?: String; + setApiKey?: Dispatch>; +} + +export const Context = React.createContext({}); + +export const ContextProvider = ({ children }: PropsWithChildren<{}>) => { + const [projectId, setProjectId] = useState(); + const [apiKey, setApiKey] = useState(''); + + return ( + + {children} + + ); +}; diff --git a/plugins/airbrake/dev/components/ContextProvider/index.ts b/plugins/airbrake/dev/components/ContextProvider/index.ts new file mode 100644 index 0000000000..f1e350868a --- /dev/null +++ b/plugins/airbrake/dev/components/ContextProvider/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ContextProvider, Context } from './ContextProvider'; diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index 4163590069..37b1810142 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -23,6 +23,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-react'; import { createEntity } from '../src/api/mock/mock-entity'; import CloudOffIcon from '@material-ui/icons/CloudOff'; import CloudIcon from '@material-ui/icons/Cloud'; +import { ContextProvider, Context } from './components/ContextProvider'; createDevApp() .registerPlugin(airbrakePlugin) @@ -48,16 +49,24 @@ createDevApp() }) .addPage({ element: ( - -
- -
- - - - - -
+ + +
+ +
+ + + {value => ( + + + + )} + + +
+
), title: 'Real API', path: '/airbrake-real-api', From f3238a1a6c69f04a778bd9aef445c5eedf022ef9 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 27 Jan 2022 11:07:21 +0000 Subject: [PATCH 14/34] Make the project slug into a project ID Signed-off-by: Karan Shah --- plugins/airbrake/dev/components/ApiBar/ApiBar.tsx | 4 +++- plugins/airbrake/dev/index.tsx | 6 ++---- plugins/airbrake/src/api/mock/mock-entity.ts | 15 +++++++++------ .../EntityAirbrakeWidget.test.tsx | 2 +- .../EntityAirbrakeWidget/EntityAirbrakeWidget.tsx | 4 ++-- plugins/airbrake/src/components/useProjectSlug.ts | 4 ++-- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx index bd8079420f..8437559baf 100644 --- a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx +++ b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx @@ -35,7 +35,9 @@ export const ApiBar = () => { value.setProjectId?.(parseInt(e.target.value, 10))} + onChange={e => + value.setProjectId?.(parseInt(e.target.value, 10) || undefined) + } />
- + @@ -57,9 +57,7 @@ createDevApp() {value => ( - + )} diff --git a/plugins/airbrake/src/api/mock/mock-entity.ts b/plugins/airbrake/src/api/mock/mock-entity.ts index b169938004..7891478f51 100644 --- a/plugins/airbrake/src/api/mock/mock-entity.ts +++ b/plugins/airbrake/src/api/mock/mock-entity.ts @@ -13,17 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AIRBRAKE_PROJECT_SLUG_ANNOTATION } from '../../components/useProjectSlug'; +import { AIRBRAKE_PROJECT_ID_ANNOTATION } from '../../components/useProjectSlug'; import { Entity } from '@backstage/catalog-model'; -export const createEntity = (name?: string) => - ({ +export const createEntity = (projectId?: number) => { + const projectIdString = projectId?.toString(); + + return { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', metadata: { annotations: { - [AIRBRAKE_PROJECT_SLUG_ANNOTATION]: name, + [AIRBRAKE_PROJECT_ID_ANNOTATION]: projectIdString, }, - name: name, + name: projectIdString, }, - } as Entity); + } as Entity; +}; diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 3c63f84f19..5fd1aa9bf5 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -23,7 +23,7 @@ import { createEntity } from '../../api/mock/mock-entity'; describe('EntityAirbrakeContent', () => { it('renders all errors sent from Airbrake', async () => { const table = await renderInTestApp( - , + , ); expect(exampleData.groups.length).toBeGreaterThan(0); for (const group of exampleData.groups) { diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx index 53f7d63f13..543ebbefc0 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx @@ -29,7 +29,7 @@ import { ErrorApi, errorApiRef, useApi } from '@backstage/core-plugin-api'; import { airbrakeApiRef } from '../../api'; import useAsync from 'react-use/lib/useAsync'; import { - AIRBRAKE_PROJECT_SLUG_ANNOTATION, + AIRBRAKE_PROJECT_ID_ANNOTATION, useProjectSlug, } from '../useProjectSlug'; @@ -64,7 +64,7 @@ export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => { {!loading && !projectId && ( )} diff --git a/plugins/airbrake/src/components/useProjectSlug.ts b/plugins/airbrake/src/components/useProjectSlug.ts index 4c6337b49e..07ba527c00 100644 --- a/plugins/airbrake/src/components/useProjectSlug.ts +++ b/plugins/airbrake/src/components/useProjectSlug.ts @@ -16,8 +16,8 @@ import { Entity } from '@backstage/catalog-model'; -export const AIRBRAKE_PROJECT_SLUG_ANNOTATION = 'airbrake.io/project-slug'; +export const AIRBRAKE_PROJECT_ID_ANNOTATION = 'airbrake.io/project-id'; export const useProjectSlug = (entity: Entity) => { - return entity?.metadata.annotations?.[AIRBRAKE_PROJECT_SLUG_ANNOTATION] ?? ''; + return entity?.metadata.annotations?.[AIRBRAKE_PROJECT_ID_ANNOTATION] ?? ''; }; From 6b5b9f2f1ba0c30a3344b687b9160e41fcb2c8aa Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 27 Jan 2022 14:21:27 +0000 Subject: [PATCH 15/34] Rename useProjectSlug.ts to useProjectId.ts Signed-off-by: Karan Shah --- plugins/airbrake/src/api/mock/mock-entity.ts | 2 +- .../EntityAirbrakeWidget/EntityAirbrakeWidget.tsx | 7 ++----- .../src/components/{useProjectSlug.ts => useProjectId.ts} | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) rename plugins/airbrake/src/components/{useProjectSlug.ts => useProjectId.ts} (93%) diff --git a/plugins/airbrake/src/api/mock/mock-entity.ts b/plugins/airbrake/src/api/mock/mock-entity.ts index 7891478f51..13534f2458 100644 --- a/plugins/airbrake/src/api/mock/mock-entity.ts +++ b/plugins/airbrake/src/api/mock/mock-entity.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AIRBRAKE_PROJECT_ID_ANNOTATION } from '../../components/useProjectSlug'; +import { AIRBRAKE_PROJECT_ID_ANNOTATION } from '../../components/useProjectId'; import { Entity } from '@backstage/catalog-model'; export const createEntity = (projectId?: number) => { diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx index 543ebbefc0..4564af6079 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx @@ -28,10 +28,7 @@ import { BackstageTheme } from '@backstage/theme'; import { ErrorApi, errorApiRef, useApi } from '@backstage/core-plugin-api'; import { airbrakeApiRef } from '../../api'; import useAsync from 'react-use/lib/useAsync'; -import { - AIRBRAKE_PROJECT_ID_ANNOTATION, - useProjectSlug, -} from '../useProjectSlug'; +import { AIRBRAKE_PROJECT_ID_ANNOTATION, useProjectId } from '../useProjectId'; const useStyles = makeStyles(() => ({ multilineText: { @@ -42,7 +39,7 @@ const useStyles = makeStyles(() => ({ export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => { const classes = useStyles(); - const projectId = useProjectSlug(entity); + const projectId = useProjectId(entity); const errorApi = useApi(errorApiRef); const airbrakeApi = useApi(airbrakeApiRef); diff --git a/plugins/airbrake/src/components/useProjectSlug.ts b/plugins/airbrake/src/components/useProjectId.ts similarity index 93% rename from plugins/airbrake/src/components/useProjectSlug.ts rename to plugins/airbrake/src/components/useProjectId.ts index 07ba527c00..3402778b7b 100644 --- a/plugins/airbrake/src/components/useProjectSlug.ts +++ b/plugins/airbrake/src/components/useProjectId.ts @@ -18,6 +18,6 @@ import { Entity } from '@backstage/catalog-model'; export const AIRBRAKE_PROJECT_ID_ANNOTATION = 'airbrake.io/project-id'; -export const useProjectSlug = (entity: Entity) => { +export const useProjectId = (entity: Entity) => { return entity?.metadata.annotations?.[AIRBRAKE_PROJECT_ID_ANNOTATION] ?? ''; }; From f99d31d38526990abce378b7a1e61d2b8dbb158e Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 27 Jan 2022 15:23:42 +0000 Subject: [PATCH 16/34] Improved how the API Bar looks Signed-off-by: Karan Shah --- .../airbrake/dev/components/ApiBar/ApiBar.tsx | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx index 8437559baf..294e4506d1 100644 --- a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx +++ b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx @@ -14,7 +14,12 @@ * limitations under the License. */ import React from 'react'; -import { makeStyles, TextField } from '@material-ui/core'; +import { + createTheme, + makeStyles, + MuiThemeProvider, + TextField, +} from '@material-ui/core'; import { Context } from '../ContextProvider'; const useStyles = makeStyles({ @@ -25,6 +30,31 @@ const useStyles = makeStyles({ }, }); +const textFieldTheme = createTheme({ + palette: { + type: 'dark', + primary: { + light: '#fff', + main: '#fff', + dark: '#fff', + contrastText: '#fff', + }, + secondary: { + light: '#fff', + main: '#fff', + dark: '#fff', + contrastText: '#fff', + }, + action: { + disabled: '#fff', + }, + text: { + primary: '#fff', + secondary: '#fff', + }, + }, +}); + export const ApiBar = () => { const classes = useStyles(); @@ -32,18 +62,22 @@ export const ApiBar = () => { {value => (
- - value.setProjectId?.(parseInt(e.target.value, 10) || undefined) - } - /> - value.setApiKey?.(e.target.value)} - /> + + + value.setProjectId?.(parseInt(e.target.value, 10) || undefined) + } + /> + value.setApiKey?.(e.target.value)} + /> +
)}
From 899b8d748fa414bbe1d0c5d1ea71cafda1a18431 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Mon, 31 Jan 2022 16:39:49 +0000 Subject: [PATCH 17/34] Connect up the production API Signed-off-by: Karan Shah --- .../airbrake/dev/components/ApiBar/ApiBar.tsx | 4 +-- .../ContextProvider/ContextProvider.tsx | 2 +- plugins/airbrake/dev/index.tsx | 34 ++++++++++++------- plugins/airbrake/src/api/airbrake-api.ts | 2 +- plugins/airbrake/src/api/production-api.ts | 13 +++---- .../EntityAirbrakeWidget.tsx | 4 +-- 6 files changed, 32 insertions(+), 27 deletions(-) diff --git a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx index 294e4506d1..eae00c2d32 100644 --- a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx +++ b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx @@ -66,7 +66,7 @@ export const ApiBar = () => { value.setProjectId?.(parseInt(e.target.value, 10) || undefined) } @@ -74,7 +74,7 @@ export const ApiBar = () => { value.setApiKey?.(e.target.value)} /> diff --git a/plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx b/plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx index bbf72f3b99..6d7fe0f7f2 100644 --- a/plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx +++ b/plugins/airbrake/dev/components/ContextProvider/ContextProvider.tsx @@ -23,7 +23,7 @@ import React, { interface ContextInterface { projectId?: number; setProjectId?: Dispatch>; - apiKey?: String; + apiKey?: string; setApiKey?: Dispatch>; } diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index c0a7c59fd9..7f32d30b4a 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -15,31 +15,33 @@ */ import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; +import { TestApiProvider } from '@backstage/test-utils'; import { airbrakePlugin, EntityAirbrakeContent } from '../src'; -import { airbrakeApiRef, MockAirbrakeApi } from '../src/api'; +import { + airbrakeApiRef, + MockAirbrakeApi, + ProductionAirbrakeApi, +} from '../src/api'; import { ApiBar } from './components/ApiBar'; import { Content, Header, Page } from '@backstage/core-components'; import { EntityProvider } from '@backstage/plugin-catalog-react'; import { createEntity } from '../src/api/mock/mock-entity'; import CloudOffIcon from '@material-ui/icons/CloudOff'; import CloudIcon from '@material-ui/icons/Cloud'; -import { ContextProvider, Context } from './components/ContextProvider'; +import { Context, ContextProvider } from './components/ContextProvider'; createDevApp() .registerPlugin(airbrakePlugin) - .registerApi({ - api: airbrakeApiRef, - deps: {}, - factory: () => new MockAirbrakeApi(), - }) .addPage({ element: (
- - - + + + + + ), @@ -57,9 +59,15 @@ createDevApp() {value => ( - - - + + + + + )} diff --git a/plugins/airbrake/src/api/airbrake-api.ts b/plugins/airbrake/src/api/airbrake-api.ts index 1a3cfa72e4..3e3fe123b9 100644 --- a/plugins/airbrake/src/api/airbrake-api.ts +++ b/plugins/airbrake/src/api/airbrake-api.ts @@ -22,5 +22,5 @@ export const airbrakeApiRef = createApiRef({ }); export interface AirbrakeApi { - fetchGroups(project: string): Promise; + fetchGroups(projectId: string): Promise; } diff --git a/plugins/airbrake/src/api/production-api.ts b/plugins/airbrake/src/api/production-api.ts index 37ac938752..cd8d2f95bf 100644 --- a/plugins/airbrake/src/api/production-api.ts +++ b/plugins/airbrake/src/api/production-api.ts @@ -16,20 +16,17 @@ import { Groups } from './airbrake-groups'; import { AirbrakeApi } from './airbrake-api'; -import { DiscoveryApi } from '@backstage/core-plugin-api'; export class ProductionAirbrakeApi implements AirbrakeApi { - constructor(private readonly discoveryApi: DiscoveryApi) {} + constructor(private readonly apiKey?: string) {} - async fetchGroups(project: string): Promise { - const apiUrl = `${await this.discoveryApi.getBaseUrl( - 'proxy', - )}/airbrake/api`; + async fetchGroups(projectId: string): Promise { + const apiUrl = `https://api.airbrake.io/api/v4/projects/${projectId}/groups?key=${this.apiKey}`; - const response = await fetch(`${apiUrl}/v4/projects/${project}/groups`); + const response = await fetch(apiUrl); if (response.status >= 400 && response.status < 600) { - throw new Error('Failed fetching Airbrake issues'); + throw new Error('Failed fetching Airbrake groups'); } return (await response.json()) as Groups; diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx index 4564af6079..b11116aeef 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.tsx @@ -45,7 +45,7 @@ export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => { const { loading, value, error } = useAsync( () => airbrakeApi.fetchGroups(projectId), - [projectId], + [airbrakeApi, projectId], ); useEffect(() => { @@ -69,7 +69,7 @@ export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => { )} From 3193d9d3bb1bdb1212726442bb0bdbf8de068684 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 1 Feb 2022 11:03:33 +0000 Subject: [PATCH 18/34] Add back catalog model and catalog plugin react Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index e037e38009..2ccb7f31b8 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -20,8 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-model": "^0.9.10", "@backstage/core-components": "^0.8.6", "@backstage/core-plugin-api": "^0.6.0", + "@backstage/plugin-catalog-react": "^0.6.12", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", From 6c3a7ad51fef61c550f7e47f4bb6a3b087ee6783 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 1 Feb 2022 15:24:25 +0000 Subject: [PATCH 19/34] Remove Discovery API Ref Signed-off-by: Karan Shah --- plugins/airbrake/src/plugin.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/plugins/airbrake/src/plugin.ts b/plugins/airbrake/src/plugin.ts index 5a58f5b4b3..f008b48ad4 100644 --- a/plugins/airbrake/src/plugin.ts +++ b/plugins/airbrake/src/plugin.ts @@ -13,11 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - createApiFactory, - createPlugin, - discoveryApiRef, -} from '@backstage/core-plugin-api'; +import { createApiFactory, createPlugin } from '@backstage/core-plugin-api'; import { rootRouteRef } from './routes'; import { airbrakeApiRef, ProductionAirbrakeApi } from './api'; @@ -27,10 +23,8 @@ export const airbrakePlugin = createPlugin({ apis: [ createApiFactory({ api: airbrakeApiRef, - deps: { - discoveryApi: discoveryApiRef, - }, - factory: ({ discoveryApi }) => new ProductionAirbrakeApi(discoveryApi), + deps: {}, + factory: () => new ProductionAirbrakeApi(), }), ], routes: { From 9e505d20a340879289384684ccbdf24b0edb7464 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 1 Feb 2022 15:56:46 +0000 Subject: [PATCH 20/34] Added a changeset Signed-off-by: Karan Shah --- .changeset/two-elephants-arrive.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/two-elephants-arrive.md diff --git a/.changeset/two-elephants-arrive.md b/.changeset/two-elephants-arrive.md new file mode 100644 index 0000000000..6a1e713602 --- /dev/null +++ b/.changeset/two-elephants-arrive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-airbrake': minor +--- + +API connectivity has added, but currently will only work by running it standalone on a browser with CORS disabled. From 499f8f547eb613efc24d221f83b4bba0229d1871 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 1 Feb 2022 17:18:22 +0000 Subject: [PATCH 21/34] Add some routes to the extensions test Signed-off-by: Karan Shah --- plugins/airbrake/src/extensions.test.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/airbrake/src/extensions.test.tsx b/plugins/airbrake/src/extensions.test.tsx index db8670ac50..5532621848 100644 --- a/plugins/airbrake/src/extensions.test.tsx +++ b/plugins/airbrake/src/extensions.test.tsx @@ -15,11 +15,23 @@ */ import React from 'react'; import { EntityAirbrakeContent } from './extensions'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { airbrakeApiRef, MockAirbrakeApi } from './api'; +import { rootRouteRef } from './routes'; describe('The Airbrake entity', () => { it('should render the content properly', async () => { - const rendered = await renderInTestApp(); + const rendered = await renderInTestApp( + + + , + { + mountedRoutes: { + '/': rootRouteRef, + }, + routeEntries: ['/'], + }, + ); expect(rendered.getByText('ChunkLoadError')).toBeInTheDocument(); }); }); From c0d61279af21ff72d5da138aed829a6916e3f743 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Fri, 4 Feb 2022 10:50:50 +0000 Subject: [PATCH 22/34] Fix extensions.test.tsx Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 2 +- plugins/airbrake/src/api/mock/mock-api.ts | 8 +++++++- plugins/airbrake/src/extensions.test.tsx | 18 +++++++++--------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index 7f32d30b4a..8fc43a73a1 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -37,7 +37,7 @@ createDevApp()
- + diff --git a/plugins/airbrake/src/api/mock/mock-api.ts b/plugins/airbrake/src/api/mock/mock-api.ts index df0293da7c..4acfa51778 100644 --- a/plugins/airbrake/src/api/mock/mock-api.ts +++ b/plugins/airbrake/src/api/mock/mock-api.ts @@ -19,9 +19,15 @@ import { AirbrakeApi } from '../airbrake-api'; import mockGroupsData from './airbrake-groups-api-mock.json'; export class MockAirbrakeApi implements AirbrakeApi { + waitTimeInMillis: number; + + constructor(waitTimeInMillis = 10) { + this.waitTimeInMillis = waitTimeInMillis; + } + fetchGroups(): Promise { return new Promise(resolve => { - setTimeout(() => resolve(mockGroupsData), 800); + setTimeout(() => resolve(mockGroupsData), this.waitTimeInMillis); }); } } diff --git a/plugins/airbrake/src/extensions.test.tsx b/plugins/airbrake/src/extensions.test.tsx index 5532621848..3b2d1927d9 100644 --- a/plugins/airbrake/src/extensions.test.tsx +++ b/plugins/airbrake/src/extensions.test.tsx @@ -15,23 +15,23 @@ */ import React from 'react'; import { EntityAirbrakeContent } from './extensions'; +import { Route } from 'react-router'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { airbrakeApiRef, MockAirbrakeApi } from './api'; -import { rootRouteRef } from './routes'; +import { createEntity } from './api/mock/mock-entity'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; describe('The Airbrake entity', () => { it('should render the content properly', async () => { const rendered = await renderInTestApp( - + + } /> + , - { - mountedRoutes: { - '/': rootRouteRef, - }, - routeEntries: ['/'], - }, ); - expect(rendered.getByText('ChunkLoadError')).toBeInTheDocument(); + await expect( + rendered.findByText('ChunkLoadError'), + ).resolves.toBeInTheDocument(); }); }); From f7ad6f0795c7f20c53856b9027aa72881cfe3796 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Fri, 4 Feb 2022 11:59:40 +0000 Subject: [PATCH 23/34] Fix EntityAirbrakeWidget.test.tsx Signed-off-by: Karan Shah --- .../EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 5fd1aa9bf5..7b3bd6595b 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -17,13 +17,16 @@ import React from 'react'; import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; import exampleData from '../../api/mock/airbrake-groups-api-mock.json'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { createEntity } from '../../api/mock/mock-entity'; +import { airbrakeApiRef, MockAirbrakeApi } from '../../api'; describe('EntityAirbrakeContent', () => { it('renders all errors sent from Airbrake', async () => { const table = await renderInTestApp( - , + + + , ); expect(exampleData.groups.length).toBeGreaterThan(0); for (const group of exampleData.groups) { From e8731ffbc6ddd19d61901ffe1a417bde6f8f7157 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Fri, 4 Feb 2022 14:06:18 +0000 Subject: [PATCH 24/34] Added a test for the annotation missing component Signed-off-by: Karan Shah --- .../EntityAirbrakeWidget.test.tsx | 12 ++++++++++++ plugins/airbrake/src/plugin.test.ts | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 7b3bd6595b..5baabe92f6 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -35,4 +35,16 @@ describe('EntityAirbrakeContent', () => { ).toBeInTheDocument(); } }); + + it('states that the annotation is missing if no annotation is provided', async () => { + const table = await renderInTestApp( + + + , + ); + expect(exampleData.groups.length).toBeGreaterThan(0); + await expect( + table.findByText('Missing Annotation'), + ).resolves.toBeInTheDocument(); + }); }); diff --git a/plugins/airbrake/src/plugin.test.ts b/plugins/airbrake/src/plugin.test.ts index c8e9c30e83..ad7865713f 100644 --- a/plugins/airbrake/src/plugin.test.ts +++ b/plugins/airbrake/src/plugin.test.ts @@ -15,9 +15,16 @@ */ import { airbrakePlugin } from './plugin'; +import { ProductionAirbrakeApi } from './api'; describe('catalog', () => { it('should export plugin', () => { expect(airbrakePlugin).toBeDefined(); }); + + it('should have at least one API, the production API', () => { + const apiFactories = Array.from(airbrakePlugin.getApis()); + expect(apiFactories.length).toBe(1); + expect(apiFactories[0].factory({})).toBeInstanceOf(ProductionAirbrakeApi); + }); }); From e76d563fe0b586b7912a650041c513d4fa1194b3 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Mon, 7 Feb 2022 15:42:26 +0000 Subject: [PATCH 25/34] Added production-api.test.ts Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 9 ++-- .../airbrake/src/api/production-api.test.ts | 50 +++++++++++++++++++ yarn.lock | 20 ++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 plugins/airbrake/src/api/production-api.test.ts diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 92e142c1a9..1c38212f36 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -28,14 +28,13 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", - "react-use": "^17.2.4", - "object-hash": "^2.2.0" + "object-hash": "^2.2.0", + "react-use": "^17.2.4" }, "peerDependencies": { "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@types/object-hash": "^2.2.1", "@backstage/app-defaults": "^0.1.6", "@backstage/cli": "^0.13.1", "@backstage/core-app-api": "^0.5.2", @@ -46,8 +45,10 @@ "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "msw": "^0.35.0", + "@types/object-hash": "^2.2.1", "cross-fetch": "^3.0.6", + "msw": "^0.35.0", + "nock": "^13.2.4", "react-router": "6.0.0-beta.0" }, "files": [ diff --git a/plugins/airbrake/src/api/production-api.test.ts b/plugins/airbrake/src/api/production-api.test.ts new file mode 100644 index 0000000000..89b7908f9a --- /dev/null +++ b/plugins/airbrake/src/api/production-api.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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 { ProductionAirbrakeApi } from './production-api'; +import nock from 'nock'; +import mockGroupsData from './mock/airbrake-groups-api-mock.json'; + +describe('The production Airbrake API', () => { + let productionApi: ProductionAirbrakeApi; + + beforeEach(() => { + productionApi = new ProductionAirbrakeApi('fakeApiKey'); + }); + + it('fetches groups using the provided project ID', async () => { + const scope = nock('https://api.airbrake.io') + .get('/api/v4/projects/123456/groups?key=fakeApiKey') + .reply(200, mockGroupsData); + expect(scope.isDone()).toBe(false); + + const groups = await productionApi.fetchGroups('123456'); + + expect(scope.isDone()).toBe(true); + expect(groups).toStrictEqual(mockGroupsData); + }); + + it('throws if fetching groups was unsuccessful', async () => { + const scope = nock('https://api.airbrake.io') + .get('/api/v4/projects/123456/groups?key=fakeApiKey') + .reply(500, mockGroupsData); + expect(scope.isDone()).toBe(false); + + await expect(productionApi.fetchGroups('123456')).rejects.toThrow(); + + expect(scope.isDone()).toBe(true); + }); +}); diff --git a/yarn.lock b/yarn.lock index 83e5796a6e..efe58ad8f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16155,6 +16155,11 @@ lodash.once@^4.0.0, lodash.once@^4.1.1: resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= +lodash.set@^4.3.2: + version "4.3.2" + resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -17658,6 +17663,16 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +nock@^13.2.4: + version "13.2.4" + resolved "https://registry.npmjs.org/nock/-/nock-13.2.4.tgz#43a309d93143ee5cdcca91358614e7bde56d20e1" + integrity sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash.set "^4.3.2" + propagate "^2.0.0" + node-abort-controller@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz#f91fa50b1dee3f909afabb7e261b1e1d6b0cb74e" @@ -19759,6 +19774,11 @@ prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, object-assign "^4.1.1" react-is "^16.13.1" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + properties-reader@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/properties-reader/-/properties-reader-2.2.0.tgz#41d837fe143d8d5f2386b6a869a1975c0b2c595c" From 82641a96d40fafadfa5ab61a8e11605756ffa653 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Mon, 7 Feb 2022 16:25:49 +0000 Subject: [PATCH 26/34] Fleshing out more tests with API failures Signed-off-by: Karan Shah --- .../airbrake/src/api/production-api.test.ts | 2 +- .../EntityAirbrakeWidget.test.tsx | 30 +++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/plugins/airbrake/src/api/production-api.test.ts b/plugins/airbrake/src/api/production-api.test.ts index 89b7908f9a..3753cab592 100644 --- a/plugins/airbrake/src/api/production-api.test.ts +++ b/plugins/airbrake/src/api/production-api.test.ts @@ -40,7 +40,7 @@ describe('The production Airbrake API', () => { it('throws if fetching groups was unsuccessful', async () => { const scope = nock('https://api.airbrake.io') .get('/api/v4/projects/123456/groups?key=fakeApiKey') - .reply(500, mockGroupsData); + .reply(500); expect(scope.isDone()).toBe(false); await expect(productionApi.fetchGroups('123456')).rejects.toThrow(); diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 5baabe92f6..5d3787b875 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -19,7 +19,12 @@ import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; import exampleData from '../../api/mock/airbrake-groups-api-mock.json'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { createEntity } from '../../api/mock/mock-entity'; -import { airbrakeApiRef, MockAirbrakeApi } from '../../api'; +import { + airbrakeApiRef, + MockAirbrakeApi, + ProductionAirbrakeApi, +} from '../../api'; +import nock from 'nock'; describe('EntityAirbrakeContent', () => { it('renders all errors sent from Airbrake', async () => { @@ -36,15 +41,34 @@ describe('EntityAirbrakeContent', () => { } }); - it('states that the annotation is missing if no annotation is provided', async () => { + it('states that the annotation is missing if no project ID annotation is provided', async () => { const table = await renderInTestApp( , ); - expect(exampleData.groups.length).toBeGreaterThan(0); await expect( table.findByText('Missing Annotation'), ).resolves.toBeInTheDocument(); }); + + it('states that an error occurred if API call fails', async () => { + const scope = nock('https://api.airbrake.io') + .get('/api/v4/projects/123/groups?key=fakeApiKey') + .reply(500); + expect(scope.isDone()).toBe(false); + + const table = await renderInTestApp( + + + , + ); + + await expect( + table.findByText('*there was an issue communicating with Airbrake*'), + ).resolves.toBeInTheDocument(); + expect(scope.isDone()).toBe(true); + }); }); From db4ed55d9d380ff9851cad327a799d86c059202b Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 8 Feb 2022 10:03:15 +0000 Subject: [PATCH 27/34] Error test now passes Signed-off-by: Karan Shah --- .../EntityAirbrakeWidget.test.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 5d3787b875..2415aa1bdf 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -17,7 +17,11 @@ import React from 'react'; import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; import exampleData from '../../api/mock/airbrake-groups-api-mock.json'; -import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { + MockErrorApi, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; import { createEntity } from '../../api/mock/mock-entity'; import { airbrakeApiRef, @@ -25,6 +29,7 @@ import { ProductionAirbrakeApi, } from '../../api'; import nock from 'nock'; +import { errorApiRef } from '@backstage/core-plugin-api'; describe('EntityAirbrakeContent', () => { it('renders all errors sent from Airbrake', async () => { @@ -57,18 +62,26 @@ describe('EntityAirbrakeContent', () => { .get('/api/v4/projects/123/groups?key=fakeApiKey') .reply(500); expect(scope.isDone()).toBe(false); + const mockErrorApi = new MockErrorApi({ collect: true }); const table = await renderInTestApp( , ); await expect( - table.findByText('*there was an issue communicating with Airbrake*'), + table.findByText(/.*there was an issue communicating with Airbrake.*/), ).resolves.toBeInTheDocument(); + expect(mockErrorApi.getErrors().length).toBe(1); + expect(mockErrorApi.getErrors()[0].error.message).toStrictEqual( + 'Failed fetching Airbrake groups', + ); expect(scope.isDone()).toBe(true); }); }); From 81e19c612ae1e067c573afedd0f67b47661ad60c Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Tue, 8 Feb 2022 10:04:39 +0000 Subject: [PATCH 28/34] Minor refactoring to better name variables Signed-off-by: Karan Shah --- .../EntityAirbrakeWidget.test.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 2415aa1bdf..5317d1469c 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -33,7 +33,7 @@ import { errorApiRef } from '@backstage/core-plugin-api'; describe('EntityAirbrakeContent', () => { it('renders all errors sent from Airbrake', async () => { - const table = await renderInTestApp( + const widget = await renderInTestApp( , @@ -41,30 +41,30 @@ describe('EntityAirbrakeContent', () => { expect(exampleData.groups.length).toBeGreaterThan(0); for (const group of exampleData.groups) { expect( - await table.getByText(group.errors[0].message), + await widget.getByText(group.errors[0].message), ).toBeInTheDocument(); } }); it('states that the annotation is missing if no project ID annotation is provided', async () => { - const table = await renderInTestApp( + const widget = await renderInTestApp( , ); await expect( - table.findByText('Missing Annotation'), + widget.findByText('Missing Annotation'), ).resolves.toBeInTheDocument(); }); - it('states that an error occurred if API call fails', async () => { + it('states that an error occurred if the API call fails', async () => { const scope = nock('https://api.airbrake.io') .get('/api/v4/projects/123/groups?key=fakeApiKey') .reply(500); expect(scope.isDone()).toBe(false); const mockErrorApi = new MockErrorApi({ collect: true }); - const table = await renderInTestApp( + const widget = await renderInTestApp( { ); await expect( - table.findByText(/.*there was an issue communicating with Airbrake.*/), + widget.findByText(/.*there was an issue communicating with Airbrake.*/), ).resolves.toBeInTheDocument(); expect(mockErrorApi.getErrors().length).toBe(1); expect(mockErrorApi.getErrors()[0].error.message).toStrictEqual( From 6cb193343af1a84bf2b0022ec12c04bbd0611ff7 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 9 Feb 2022 16:04:08 +0000 Subject: [PATCH 29/34] Update yarn.lock Signed-off-by: Karan Shah --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index d7521577c4..7461cc24f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1440,7 +1440,7 @@ "@material-ui/lab" "4.0.0-alpha.57" react-use "^17.2.4" -"@backstage/plugin-catalog-react@^0.6.13", "@backstage/plugin-catalog-react@^0.6.5": +"@backstage/plugin-catalog-react@^0.6.12", "@backstage/plugin-catalog-react@^0.6.13", "@backstage/plugin-catalog-react@^0.6.5": version "0.6.13" resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.6.13.tgz#b325eae501d3edeb8b7caef5d9615f2e632f5430" integrity sha512-XBwop7PwAZqfongx3KP6jAJar+MEscLSp8nLuHYX5XxA+suQNiBgi96uO3SEQmvtae+hvsRM7c0WHSxbYiXsDA== From 7a8ac1db0bc78ca3ef4d7e89ec483c4f7f816780 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 9 Feb 2022 16:07:15 +0000 Subject: [PATCH 30/34] Revert ADOPTERS.md Signed-off-by: Karan Shah --- ADOPTERS.md | 188 ++++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index d70ffe4cc0..beb161d9ee 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,94 +1,94 @@ -| Organization | Contact | Description of Use | -| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@dschwank](https://github.com/dschwank), [@iammnils](https://github.com/iammnils) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | -| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | -| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | -| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | -| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | -| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | -| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | -| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | -| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | -| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | -| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | -| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | -| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | -| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | -| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | -| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | -| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | -| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | -| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | -| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | -| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | -| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | -| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | -| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | -| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | -| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | -| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | -| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | -| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | -| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | -| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | -| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | -| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | -| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | -| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | -| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | -| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | -| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | -| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | -| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | -| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | -| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | -| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | -| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | -| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | -| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. | -| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. | -| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. | -| [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. | -| [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling | -| [Signal Iduna Group](https://www.signal-iduna.de/) | [Jonas Thomsen](https://github.com/JoThomsen) | Developer Portal, documentation, monitoring, service catalog for our insurance ecosystem | -| [Tradeshift](https://www.tradeshift.com/) | [Soren Mathiasen](https://github.com/sorenmat) | Developer Portal: documentation, monitoring, service templates, service catalog for our micro services | -| [Unity](https://unity.com) | [Ted Cordery](https://github.com/TeddyBallGame) | A centralized service catalog with documentation for our service engineers. | -| [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. | -| [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! | -| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. | -| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 | -| [GoTo](https://www.goto.com) | [Lorenzo Orsatti](https://github.com/lorsatti) | Improve onboarding experience of new developers. Discover faster and painlessly developer documentation, API definitions and team information. Provide useful dev metrics in a central place. Provide easy-to-use templates for new services. | -| [Telstra](https://www.telstra.com.au) | [@kiranpatel11](https://github.com/kiranpatel11), [JasonC](https://github.com/JasonC17) | Primary usage: software catalog and templates
Emerging usage : TechDocs, Explore Ecosystem, TechRadar, etc | -| [Mosaico](https://www.mosaico.com.br/) | [Wédney Yuri](https://github.com/wedneyyuri),[@tino.milton](https://github.com/miltonjacomini) | A centralized service catalog of our documentation for our service engineers. | -| [Mox Bank](https://www.mox.com/) | [Nick Laqua](https://github.com/nick-laqua-dragon), [Gauthier Roebroeck](https://github.com/gauthier-roebroeck-mox) | "Single pane of glass" developer portal for providing a best-in-class developer experience to our product teams and making Mox the best tech environment in Hongkong 🥰🚀 | -| [Keyloop](https://www.keyloop.com/) | [Andre Wanlin](https://github.com/awanlin) | Future-motive Developer Portal to help our teams create technology to make everything about buying and owning a car better. 🚗 | -| [Simply Business](https://sbtech.simplybusiness.co.uk/) | [@addersuk](https://github.com/addersuk), [@LightningStairs](https://github.com/LightningStairs), [@punitcse](https://github.com/punitcse), [@moltenice](https://github.com/moltenice) | Central developer portal to access everything a developer needs such as docs, internal service catalog, and the ability to quickly create a new service from a template. Internally developed Backstage plugins allow us to customise the experience to how we work. | -| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding | -| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. | -| [EF Education First](https://www.ef.com) | [Daan Boerlage](https://github.com/runebaas), [Rafał Nowosielski](https://github.com/rnowosielski) | Our developer portal - primarily used for cataloging and scaffolding with the ambition to expand with more feature adoptions over time | -| [Power Home Remodeling](https://www.techatpower.com) | [Ben Langfeld](https://github.com/benlangfeld) | Developer portal to our internal services, build on open-source software (including Kubernetes) in our own datacenters. Our Portal allows our team members to navigate inherant complexity and standardise. | -| [Livspace](https://www.livspace.com) | [Praveen Kumar](https://github.com/praveen-livspace) | Developer portal, service catalog, tech docs, API docs and plugins | -| [Just Eat Takeaway](https://www.justeattakeaway.com) | [Kim Wilson](https://github.com/kwilson541) | Our developer portal which centralises applications, reduces cognitive load and provides teams insights. | -| [Hopin](https://hopin.com) | [Vladimir Glafirov](https://github.com/vglafirov), [Chloe Lee](https://github.com/msfuko) | Developer portal to streamline the development practices. Integrated with service catalog, software templates, application monitoring, tech docs and plugins. | -| [HBO Max](https://hbomax.com) | [@mdb](https://github.com/mdb), [@nesta219](https://github.com/nesta219), [@nmische](https://github.com/nmische), [@hbomark](https://github.com/hbomark) | Developer portal hosting service catalog and API documentation, as well as cloud infrastructure details, operational visibility tools, and a custom plugin for browsing notable platform change events, such as deployments and configuration updates. | -| [RCHLO](https://www.riachuelo.com.br) & [MIDWAY](https://www.midway.com.br) | [@marcosborges](https://github.com/marcosborges), [@defaultbr](https://github.com/defaultbr) | Self-Service Platform | -| [HP Inc](https://www.hp.com) | [Damon Kaswell](https://github.com/dekoding) | DevEx engagement hub (dev portal: docs, standards, Q&A) and extensive assets catalog (APIs, services, code, data, etc.) for the pan-HP internal developer community. | -| [VMware](https://www.vmware.com) | [@mpriamo](https://github.com/mpriamo), [@krisapplegate](https://github.com/krisapplegate) | Part of [Tanzu Application Platform](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/index.html) offering; internal developer portal | -| [Ualá](https://www.uala.com.ar/) | [Santiago Bernal](https://github.com/sabernal) | Initial work being done to centralize documentation for all our microservices and APIs, as well as scaffolding new services and tracking code quality | -| [IKEA IT AB](https://www.ingka.com) | [@bjornramberg](https://github.com/bjornramberg), [@supriyachitale](https://github.com/supriyachitale) | Supporting engineers at scale with self serve access and connecting the dots of our engineering platform and services, enabling product teams to move faster and go further, and unleashing innovation, reuse and co-creation across the organisation. | -| [Invitae](https://www.invitae.com/en) | [@ryan-hanchett](https://github.com/ryan-hanchett), [@gmandler42](https://github.com/gmandler42) | Centralized Developer Experience portal, putting all of our tooling behind a single pane of glass and creating a living service catalog. | -| [Fortum](https://www.fortum.com/) | [@brunoamaroalmeida](https://github.com/brunoamaroalmeida), [@dhaval-vithalani](https://github.com/dhaval-vithalani), [@sunilkumarmohanty](https://github.com/sunilkumarmohanty) | A central portal containing information about our applications, services, processes and other software assets to be used by Fortum software engineering community. | +| Organization | Contact | Description of Use | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@dschwank](https://github.com/dschwank), [@iammnils](https://github.com/iammnils) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | +| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | +| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | +| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | +| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | +| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | +| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | +| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | +| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | +| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | +| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | +| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | +| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | +| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | +| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | +| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | +| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | +| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | +| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | +| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | +| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | +| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | +| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | +| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | +| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | +| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | +| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | +| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | +| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | +| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | +| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | +| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | +| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | +| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | +| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | +| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | +| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | +| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | +| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | +| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | +| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | +| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | +| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | +| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. | +| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. | +| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. | +| [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. | +| [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling | +| [Signal Iduna Group](https://www.signal-iduna.de/) | [Jonas Thomsen](https://github.com/JoThomsen) | Developer Portal, documentation, monitoring, service catalog for our insurance ecosystem | +| [Tradeshift](https://www.tradeshift.com/) | [Soren Mathiasen](https://github.com/sorenmat) | Developer Portal: documentation, monitoring, service templates, service catalog for our micro services | +| [Unity](https://unity.com) | [Ted Cordery](https://github.com/TeddyBallGame) | A centralized service catalog with documentation for our service engineers. | +| [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. | +| [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! | +| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. | +| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 | +| [GoTo](https://www.goto.com) | [Lorenzo Orsatti](https://github.com/lorsatti) | Improve onboarding experience of new developers. Discover faster and painlessly developer documentation, API definitions and team information. Provide useful dev metrics in a central place. Provide easy-to-use templates for new services. | +| [Telstra](https://www.telstra.com.au) | [@kiranpatel11](https://github.com/kiranpatel11), [JasonC](https://github.com/JasonC17) | Primary usage: software catalog and templates
Emerging usage : TechDocs, Explore Ecosystem, TechRadar, etc | +| [Mosaico](https://www.mosaico.com.br/) | [Wédney Yuri](https://github.com/wedneyyuri),[@tino.milton](https://github.com/miltonjacomini) | A centralized service catalog of our documentation for our service engineers. | +| [Mox Bank](https://www.mox.com/) | [Nick Laqua](https://github.com/nick-laqua-dragon), [Gauthier Roebroeck](https://github.com/gauthier-roebroeck-mox) | "Single pane of glass" developer portal for providing a best-in-class developer experience to our product teams and making Mox the best tech environment in Hongkong 🥰🚀 | +| [Keyloop](https://www.keyloop.com/) | [Andre Wanlin](https://github.com/awanlin) | Future-motive Developer Portal to help our teams create technology to make everything about buying and owning a car better. 🚗 | +| [Simply Business](https://sbtech.simplybusiness.co.uk/) | [@addersuk](https://github.com/addersuk), [@LightningStairs](https://github.com/LightningStairs), [@punitcse](https://github.com/punitcse), [@moltenice](https://github.com/moltenice) | Central developer portal to access everything a developer needs such as docs, internal service catalog, and the ability to quickly create a new service from a template. Internally developed Backstage plugins allow us to customise the experience to how we work. | +| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding | +| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. | +| [EF Education First](https://www.ef.com) | [Daan Boerlage](https://github.com/runebaas), [Rafał Nowosielski](https://github.com/rnowosielski) | Our developer portal - primarily used for cataloging and scaffolding with the ambition to expand with more feature adoptions over time | +| [Power Home Remodeling](https://www.techatpower.com) | [Ben Langfeld](https://github.com/benlangfeld) | Developer portal to our internal services, build on open-source software (including Kubernetes) in our own datacenters. Our Portal allows our team members to navigate inherant complexity and standardise. | +| [Livspace](https://www.livspace.com) | [Praveen Kumar](https://github.com/praveen-livspace) | Developer portal, service catalog, tech docs, API docs and plugins | +| [Just Eat Takeaway](https://www.justeattakeaway.com) | [Kim Wilson](https://github.com/kwilson541) | Our developer portal which centralises applications, reduces cognitive load and provides teams insights. | +| [Hopin](https://hopin.com) | [Vladimir Glafirov](https://github.com/vglafirov), [Chloe Lee](https://github.com/msfuko) | Developer portal to streamline the development practices. Integrated with service catalog, software templates, application monitoring, tech docs and plugins. | +| [HBO Max](https://hbomax.com) | [@mdb](https://github.com/mdb), [@nesta219](https://github.com/nesta219), [@nmische](https://github.com/nmische), [@hbomark](https://github.com/hbomark) | Developer portal hosting service catalog and API documentation, as well as cloud infrastructure details, operational visibility tools, and a custom plugin for browsing notable platform change events, such as deployments and configuration updates. | +| [RCHLO](https://www.riachuelo.com.br) & [MIDWAY](https://www.midway.com.br) | [@marcosborges](https://github.com/marcosborges), [@defaultbr](https://github.com/defaultbr) | Self-Service Platform | +| [HP Inc](https://www.hp.com) | [Damon Kaswell](https://github.com/dekoding) | DevEx engagement hub (dev portal: docs, standards, Q&A) and extensive assets catalog (APIs, services, code, data, etc.) for the pan-HP internal developer community. +| [VMware](https://www.vmware.com) | [@mpriamo](https://github.com/mpriamo), [@krisapplegate](https://github.com/krisapplegate) | Part of [Tanzu Application Platform](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/index.html) offering; internal developer portal | +| [Ualá](https://www.uala.com.ar/) | [Santiago Bernal](https://github.com/sabernal) | Initial work being done to centralize documentation for all our microservices and APIs, as well as scaffolding new services and tracking code quality | +| [IKEA IT AB](https://www.ingka.com) | [@bjornramberg](https://github.com/bjornramberg), [@supriyachitale](https://github.com/supriyachitale) | Supporting engineers at scale with self serve access and connecting the dots of our engineering platform and services, enabling product teams to move faster and go further, and unleashing innovation, reuse and co-creation across the organisation. | +| [Invitae](https://www.invitae.com/en) | [@ryan-hanchett](https://github.com/ryan-hanchett), [@gmandler42](https://github.com/gmandler42) | Centralized Developer Experience portal, putting all of our tooling behind a single pane of glass and creating a living service catalog. | +| [Fortum](https://www.fortum.com/) | [@brunoamaroalmeida](https://github.com/brunoamaroalmeida), [@dhaval-vithalani](https://github.com/dhaval-vithalani), [@sunilkumarmohanty](https://github.com/sunilkumarmohanty) | A central portal containing information about our applications, services, processes and other software assets to be used by Fortum software engineering community. | From b9e83843e276f3313c627737b7d4fcd2f47257ef Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Wed, 9 Feb 2022 16:19:58 +0000 Subject: [PATCH 31/34] Stop using snake case Signed-off-by: Karan Shah --- plugins/airbrake/dev/index.tsx | 2 +- plugins/airbrake/package.json | 5 ++--- .../airbrake/src/api/{airbrake-api.ts => AirbrakeApi.ts} | 2 +- .../api/{production-api.test.ts => ProductionApi.test.ts} | 4 ++-- .../src/api/{production-api.ts => ProductionApi.ts} | 4 ++-- .../src/api/{airbrake-groups.d.ts => airbrakeGroups.d.ts} | 0 plugins/airbrake/src/api/index.ts | 8 ++++---- plugins/airbrake/src/api/mock/{mock-api.ts => MockApi.ts} | 6 +++--- .../src/api/mock/{mock-entity.ts => MockEntity.ts} | 0 ...ke-groups-api-mock.json => airbrakeGroupsApiMock.json} | 0 plugins/airbrake/src/api/mock/index.ts | 2 +- .../EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx | 4 ++-- plugins/airbrake/src/extensions.test.tsx | 2 +- 13 files changed, 19 insertions(+), 20 deletions(-) rename plugins/airbrake/src/api/{airbrake-api.ts => AirbrakeApi.ts} (95%) rename plugins/airbrake/src/api/{production-api.test.ts => ProductionApi.test.ts} (92%) rename plugins/airbrake/src/api/{production-api.ts => ProductionApi.ts} (92%) rename plugins/airbrake/src/api/{airbrake-groups.d.ts => airbrakeGroups.d.ts} (100%) rename plugins/airbrake/src/api/mock/{mock-api.ts => MockApi.ts} (85%) rename plugins/airbrake/src/api/mock/{mock-entity.ts => MockEntity.ts} (100%) rename plugins/airbrake/src/api/mock/{airbrake-groups-api-mock.json => airbrakeGroupsApiMock.json} (100%) diff --git a/plugins/airbrake/dev/index.tsx b/plugins/airbrake/dev/index.tsx index 8fc43a73a1..cd0d29301f 100644 --- a/plugins/airbrake/dev/index.tsx +++ b/plugins/airbrake/dev/index.tsx @@ -25,7 +25,7 @@ import { import { ApiBar } from './components/ApiBar'; import { Content, Header, Page } from '@backstage/core-components'; import { EntityProvider } from '@backstage/plugin-catalog-react'; -import { createEntity } from '../src/api/mock/mock-entity'; +import { createEntity } from '../src/api/mock/MockEntity'; import CloudOffIcon from '@material-ui/icons/CloudOff'; import CloudIcon from '@material-ui/icons/Cloud'; import { Context, ContextProvider } from './components/ContextProvider'; diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 220873dbea..1ebd99434e 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -23,7 +23,9 @@ "@backstage/catalog-model": "^0.9.10", "@backstage/core-components": "^0.8.8-next.0", "@backstage/core-plugin-api": "^0.6.0", + "@backstage/dev-utils": "^0.2.21-next.0", "@backstage/plugin-catalog-react": "^0.6.12", + "@backstage/test-utils": "^0.2.4", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -35,12 +37,9 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@types/object-hash": "^2.2.1", "@backstage/app-defaults": "^0.1.7-next.0", "@backstage/cli": "^0.13.2-next.0", "@backstage/core-app-api": "^0.5.2", - "@backstage/dev-utils": "^0.2.21-next.0", - "@backstage/test-utils": "^0.2.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/airbrake/src/api/airbrake-api.ts b/plugins/airbrake/src/api/AirbrakeApi.ts similarity index 95% rename from plugins/airbrake/src/api/airbrake-api.ts rename to plugins/airbrake/src/api/AirbrakeApi.ts index 3e3fe123b9..a29aa3eca7 100644 --- a/plugins/airbrake/src/api/airbrake-api.ts +++ b/plugins/airbrake/src/api/AirbrakeApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Groups } from './airbrake-groups'; +import { Groups } from './airbrakeGroups'; import { createApiRef } from '@backstage/core-plugin-api'; export const airbrakeApiRef = createApiRef({ diff --git a/plugins/airbrake/src/api/production-api.test.ts b/plugins/airbrake/src/api/ProductionApi.test.ts similarity index 92% rename from plugins/airbrake/src/api/production-api.test.ts rename to plugins/airbrake/src/api/ProductionApi.test.ts index 3753cab592..d978cd82a0 100644 --- a/plugins/airbrake/src/api/production-api.test.ts +++ b/plugins/airbrake/src/api/ProductionApi.test.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { ProductionAirbrakeApi } from './production-api'; +import { ProductionAirbrakeApi } from './ProductionApi'; import nock from 'nock'; -import mockGroupsData from './mock/airbrake-groups-api-mock.json'; +import mockGroupsData from './mock/airbrakeGroupsApiMock.json'; describe('The production Airbrake API', () => { let productionApi: ProductionAirbrakeApi; diff --git a/plugins/airbrake/src/api/production-api.ts b/plugins/airbrake/src/api/ProductionApi.ts similarity index 92% rename from plugins/airbrake/src/api/production-api.ts rename to plugins/airbrake/src/api/ProductionApi.ts index cd8d2f95bf..76416bc173 100644 --- a/plugins/airbrake/src/api/production-api.ts +++ b/plugins/airbrake/src/api/ProductionApi.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { Groups } from './airbrake-groups'; -import { AirbrakeApi } from './airbrake-api'; +import { Groups } from './airbrakeGroups'; +import { AirbrakeApi } from './AirbrakeApi'; export class ProductionAirbrakeApi implements AirbrakeApi { constructor(private readonly apiKey?: string) {} diff --git a/plugins/airbrake/src/api/airbrake-groups.d.ts b/plugins/airbrake/src/api/airbrakeGroups.d.ts similarity index 100% rename from plugins/airbrake/src/api/airbrake-groups.d.ts rename to plugins/airbrake/src/api/airbrakeGroups.d.ts diff --git a/plugins/airbrake/src/api/index.ts b/plugins/airbrake/src/api/index.ts index 7bad94e298..2d92789653 100644 --- a/plugins/airbrake/src/api/index.ts +++ b/plugins/airbrake/src/api/index.ts @@ -15,7 +15,7 @@ */ export * from './mock'; -export type { AirbrakeApi } from './airbrake-api'; -export { airbrakeApiRef } from './airbrake-api'; -export type { Groups } from './airbrake-groups'; -export { ProductionAirbrakeApi } from './production-api'; +export type { AirbrakeApi } from './AirbrakeApi'; +export { airbrakeApiRef } from './AirbrakeApi'; +export type { Groups } from './airbrakeGroups'; +export { ProductionAirbrakeApi } from './ProductionApi'; diff --git a/plugins/airbrake/src/api/mock/mock-api.ts b/plugins/airbrake/src/api/mock/MockApi.ts similarity index 85% rename from plugins/airbrake/src/api/mock/mock-api.ts rename to plugins/airbrake/src/api/mock/MockApi.ts index 4acfa51778..dbb1d5c049 100644 --- a/plugins/airbrake/src/api/mock/mock-api.ts +++ b/plugins/airbrake/src/api/mock/MockApi.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { Groups } from '../airbrake-groups'; -import { AirbrakeApi } from '../airbrake-api'; -import mockGroupsData from './airbrake-groups-api-mock.json'; +import { Groups } from '../airbrakeGroups'; +import { AirbrakeApi } from '../AirbrakeApi'; +import mockGroupsData from './airbrakeGroupsApiMock.json'; export class MockAirbrakeApi implements AirbrakeApi { waitTimeInMillis: number; diff --git a/plugins/airbrake/src/api/mock/mock-entity.ts b/plugins/airbrake/src/api/mock/MockEntity.ts similarity index 100% rename from plugins/airbrake/src/api/mock/mock-entity.ts rename to plugins/airbrake/src/api/mock/MockEntity.ts diff --git a/plugins/airbrake/src/api/mock/airbrake-groups-api-mock.json b/plugins/airbrake/src/api/mock/airbrakeGroupsApiMock.json similarity index 100% rename from plugins/airbrake/src/api/mock/airbrake-groups-api-mock.json rename to plugins/airbrake/src/api/mock/airbrakeGroupsApiMock.json diff --git a/plugins/airbrake/src/api/mock/index.ts b/plugins/airbrake/src/api/mock/index.ts index f084268951..0588d45fa6 100644 --- a/plugins/airbrake/src/api/mock/index.ts +++ b/plugins/airbrake/src/api/mock/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { MockAirbrakeApi } from './mock-api'; +export { MockAirbrakeApi } from './MockApi'; diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index 5317d1469c..c242ff77f2 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -16,13 +16,13 @@ import React from 'react'; import { EntityAirbrakeWidget } from './EntityAirbrakeWidget'; -import exampleData from '../../api/mock/airbrake-groups-api-mock.json'; +import exampleData from '../../api/mock/airbrakeGroupsApiMock.json'; import { MockErrorApi, renderInTestApp, TestApiProvider, } from '@backstage/test-utils'; -import { createEntity } from '../../api/mock/mock-entity'; +import { createEntity } from '../../api/mock/MockEntity'; import { airbrakeApiRef, MockAirbrakeApi, diff --git a/plugins/airbrake/src/extensions.test.tsx b/plugins/airbrake/src/extensions.test.tsx index 3b2d1927d9..d677d939a4 100644 --- a/plugins/airbrake/src/extensions.test.tsx +++ b/plugins/airbrake/src/extensions.test.tsx @@ -18,7 +18,7 @@ import { EntityAirbrakeContent } from './extensions'; import { Route } from 'react-router'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { airbrakeApiRef, MockAirbrakeApi } from './api'; -import { createEntity } from './api/mock/mock-entity'; +import { createEntity } from './api/mock/MockEntity'; import { EntityProvider } from '@backstage/plugin-catalog-react'; describe('The Airbrake entity', () => { From 71a6648e74adfb0886ee80e733e9ad3e840c3ca4 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 10 Feb 2022 09:39:11 +0000 Subject: [PATCH 32/34] Move tests to MSW from Nock Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 1 - .../airbrake/src/api/ProductionApi.test.ts | 42 +++++++++++-------- .../EntityAirbrakeWidget.test.tsx | 20 ++++++--- yarn.lock | 20 --------- 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 1ebd99434e..36eb40f60b 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -48,7 +48,6 @@ "@types/object-hash": "^2.2.1", "cross-fetch": "^3.0.6", "msw": "^0.35.0", - "nock": "^13.2.4", "react-router": "6.0.0-beta.0" }, "files": [ diff --git a/plugins/airbrake/src/api/ProductionApi.test.ts b/plugins/airbrake/src/api/ProductionApi.test.ts index d978cd82a0..04bb73caf7 100644 --- a/plugins/airbrake/src/api/ProductionApi.test.ts +++ b/plugins/airbrake/src/api/ProductionApi.test.ts @@ -15,36 +15,44 @@ */ import { ProductionAirbrakeApi } from './ProductionApi'; -import nock from 'nock'; +import { rest } from 'msw'; import mockGroupsData from './mock/airbrakeGroupsApiMock.json'; +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; describe('The production Airbrake API', () => { - let productionApi: ProductionAirbrakeApi; - - beforeEach(() => { - productionApi = new ProductionAirbrakeApi('fakeApiKey'); - }); + const productionApi = new ProductionAirbrakeApi('fakeApiKey'); + const worker = setupServer(); + setupRequestMockHandlers(worker); it('fetches groups using the provided project ID', async () => { - const scope = nock('https://api.airbrake.io') - .get('/api/v4/projects/123456/groups?key=fakeApiKey') - .reply(200, mockGroupsData); - expect(scope.isDone()).toBe(false); + worker.use( + rest.get( + 'https://api.airbrake.io/api/v4/projects/123456/groups', + (req, res, ctx) => { + if (req.url.searchParams.get('key') === 'fakeApiKey') { + return res(ctx.status(200), ctx.json(mockGroupsData)); + } + return res(ctx.status(401)); + }, + ), + ); const groups = await productionApi.fetchGroups('123456'); - expect(scope.isDone()).toBe(true); expect(groups).toStrictEqual(mockGroupsData); }); it('throws if fetching groups was unsuccessful', async () => { - const scope = nock('https://api.airbrake.io') - .get('/api/v4/projects/123456/groups?key=fakeApiKey') - .reply(500); - expect(scope.isDone()).toBe(false); + worker.use( + rest.get( + 'https://api.airbrake.io/api/v4/projects/123456/groups', + (_, res, ctx) => { + return res(ctx.status(500)); + }, + ), + ); await expect(productionApi.fetchGroups('123456')).rejects.toThrow(); - - expect(scope.isDone()).toBe(true); }); }); diff --git a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx index c242ff77f2..ef1ac229c9 100644 --- a/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx +++ b/plugins/airbrake/src/components/EntityAirbrakeWidget/EntityAirbrakeWidget.test.tsx @@ -20,6 +20,7 @@ import exampleData from '../../api/mock/airbrakeGroupsApiMock.json'; import { MockErrorApi, renderInTestApp, + setupRequestMockHandlers, TestApiProvider, } from '@backstage/test-utils'; import { createEntity } from '../../api/mock/MockEntity'; @@ -28,10 +29,14 @@ import { MockAirbrakeApi, ProductionAirbrakeApi, } from '../../api'; -import nock from 'nock'; import { errorApiRef } from '@backstage/core-plugin-api'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; describe('EntityAirbrakeContent', () => { + const worker = setupServer(); + setupRequestMockHandlers(worker); + it('renders all errors sent from Airbrake', async () => { const widget = await renderInTestApp( @@ -58,10 +63,14 @@ describe('EntityAirbrakeContent', () => { }); it('states that an error occurred if the API call fails', async () => { - const scope = nock('https://api.airbrake.io') - .get('/api/v4/projects/123/groups?key=fakeApiKey') - .reply(500); - expect(scope.isDone()).toBe(false); + worker.use( + rest.get( + 'https://api.airbrake.io/api/v4/projects/123/groups', + (_, res, ctx) => { + return res(ctx.status(500)); + }, + ), + ); const mockErrorApi = new MockErrorApi({ collect: true }); const widget = await renderInTestApp( @@ -82,6 +91,5 @@ describe('EntityAirbrakeContent', () => { expect(mockErrorApi.getErrors()[0].error.message).toStrictEqual( 'Failed fetching Airbrake groups', ); - expect(scope.isDone()).toBe(true); }); }); diff --git a/yarn.lock b/yarn.lock index 7461cc24f2..cbe90e7e83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16250,11 +16250,6 @@ lodash.once@^4.0.0, lodash.once@^4.1.1: resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash.set@^4.3.2: - version "4.3.2" - resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -17738,16 +17733,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -nock@^13.2.4: - version "13.2.4" - resolved "https://registry.npmjs.org/nock/-/nock-13.2.4.tgz#43a309d93143ee5cdcca91358614e7bde56d20e1" - integrity sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash.set "^4.3.2" - propagate "^2.0.0" - node-abort-controller@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz#f91fa50b1dee3f909afabb7e261b1e1d6b0cb74e" @@ -19804,11 +19789,6 @@ prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, object-assign "^4.1.1" react-is "^16.13.1" -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - properties-reader@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/properties-reader/-/properties-reader-2.2.0.tgz#41d837fe143d8d5f2386b6a869a1975c0b2c595c" From c13bb6fc29ca8c332af98543600a30691ab6a63e Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 10 Feb 2022 09:57:55 +0000 Subject: [PATCH 33/34] Updated yarn.lock after fixing conflict with master Signed-off-by: Karan Shah --- yarn.lock | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6988edda3f..b0eabdb265 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1440,7 +1440,7 @@ "@material-ui/lab" "4.0.0-alpha.57" react-use "^17.2.4" -"@backstage/plugin-catalog-react@^0.6.13", "@backstage/plugin-catalog-react@^0.6.5", "@backstage/plugin-catalog-react@^0.6.9": +"@backstage/plugin-catalog-react@^0.6.12", "@backstage/plugin-catalog-react@^0.6.13", "@backstage/plugin-catalog-react@^0.6.5", "@backstage/plugin-catalog-react@^0.6.9": version "0.6.13" resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.6.13.tgz#b325eae501d3edeb8b7caef5d9615f2e632f5430" integrity sha512-XBwop7PwAZqfongx3KP6jAJar+MEscLSp8nLuHYX5XxA+suQNiBgi96uO3SEQmvtae+hvsRM7c0WHSxbYiXsDA== @@ -1835,14 +1835,7 @@ dependencies: "@date-io/core" "^1.3.13" -"@date-io/luxon@1.x": - version "1.3.13" - resolved "https://registry.npmjs.org/@date-io/luxon/-/luxon-1.3.13.tgz#68f0134bb38ef486b2ed6df01981f814c633e28a" - integrity sha512-9wUrJCNSMZJeYAiH+dbb45oGpnHeFP7TOH/Lt26If47gjFCkjvyINzWx+K5AGsnlP0Qosxc7hkF1yLi6ecutxw== - dependencies: - "@date-io/core" "^1.3.13" - -"@date-io/luxon@^1.3.13": +"@date-io/luxon@1.x", "@date-io/luxon@^1.3.13": version "1.3.13" resolved "https://registry.npmjs.org/@date-io/luxon/-/luxon-1.3.13.tgz#68f0134bb38ef486b2ed6df01981f814c633e28a" integrity sha512-9wUrJCNSMZJeYAiH+dbb45oGpnHeFP7TOH/Lt26If47gjFCkjvyINzWx+K5AGsnlP0Qosxc7hkF1yLi6ecutxw== From aab863538baf61520669b48dc31e7d1634826fe1 Mon Sep 17 00:00:00 2001 From: Karan Shah Date: Thu, 10 Feb 2022 15:38:40 +0000 Subject: [PATCH 34/34] Update package.json dependencies to the latest versions of Backstage dependencies Signed-off-by: Karan Shah --- plugins/airbrake/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 4c6ab0c21e..915762896a 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -23,8 +23,8 @@ "@backstage/catalog-model": "^0.9.10", "@backstage/core-components": "^0.8.8", "@backstage/core-plugin-api": "^0.6.0", - "@backstage/dev-utils": "^0.2.21-next.0", - "@backstage/plugin-catalog-react": "^0.6.12", + "@backstage/dev-utils": "^0.2.21", + "@backstage/plugin-catalog-react": "^0.6.14", "@backstage/test-utils": "^0.2.4", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2",