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; + } +}