diff --git a/grafana-plugin/src/models/alertgroup/alertgroup.ts b/grafana-plugin/src/models/alertgroup/alertgroup.ts index 36299caf..7a73a4df 100644 --- a/grafana-plugin/src/models/alertgroup/alertgroup.ts +++ b/grafana-plugin/src/models/alertgroup/alertgroup.ts @@ -8,11 +8,17 @@ import { onCallApi } from 'network/oncall-api/http-client'; import { RootStore } from 'state/rootStore'; import { SelectOption } from 'state/types'; import { LocationHelper } from 'utils/LocationHelper'; +import { GENERIC_ERROR } from 'utils/consts'; import { AutoLoadingState, WithGlobalNotification } from 'utils/decorators'; import { AlertGroupHelper } from './alertgroup.helpers'; import { AlertGroupColumn, AlertAction, IncidentStatus } from './alertgroup.types'; +const composeFailureMessageFn = async (error: Response) => { + let errorData = await error.json(); + return errorData?.detail || GENERIC_ERROR; +}; + export class AlertGroupStore { rootStore: RootStore; alerts = new Map(); @@ -204,9 +210,13 @@ export class AlertGroupStore { }); } + @WithGlobalNotification({ + failure: 'Failure', + composeFailureMessageFn, + }) async resolve(id: ApiSchemas['AlertGroup']['pk']) { this.setLiveUpdatesPaused(true); - const { data } = await onCallApi().POST('/alertgroups/{id}/resolve/', { + const { data } = await onCallApi({ skipErrorHandling: true }).POST('/alertgroups/{id}/resolve/', { params: { path: { id } }, }); this.updateAlert(id, { @@ -215,6 +225,10 @@ export class AlertGroupStore { }); } + @WithGlobalNotification({ + failure: 'Failure', + composeFailureMessageFn, + }) async unresolve(id: ApiSchemas['AlertGroup']['pk']) { this.setLiveUpdatesPaused(true); const { data } = await onCallApi().POST('/alertgroups/{id}/unresolve/', { params: { path: { id } } }); @@ -224,6 +238,10 @@ export class AlertGroupStore { }); } + @WithGlobalNotification({ + failure: 'Failure', + composeFailureMessageFn, + }) async acknowledge(id: ApiSchemas['AlertGroup']['pk']) { this.setLiveUpdatesPaused(true); const { data } = await onCallApi().POST('/alertgroups/{id}/acknowledge/', { params: { path: { id } } }); @@ -233,6 +251,10 @@ export class AlertGroupStore { }); } + @WithGlobalNotification({ + failure: 'Failure', + composeFailureMessageFn, + }) async unacknowledge(id: ApiSchemas['AlertGroup']['pk']) { this.setLiveUpdatesPaused(true); const { data } = await onCallApi().POST('/alertgroups/{id}/unacknowledge/', { params: { path: { id } } }); @@ -242,6 +264,10 @@ export class AlertGroupStore { }); } + @WithGlobalNotification({ + failure: 'Failure', + composeFailureMessageFn, + }) async silence(id: ApiSchemas['AlertGroup']['pk'], delay: number) { this.setLiveUpdatesPaused(true); const { data } = await onCallApi().POST('/alertgroups/{id}/silence/', { @@ -254,6 +280,10 @@ export class AlertGroupStore { }); } + @WithGlobalNotification({ + failure: 'Failure', + composeFailureMessageFn, + }) async unsilence(id: ApiSchemas['AlertGroup']['pk']) { this.setLiveUpdatesPaused(true); const { data } = await onCallApi().POST('/alertgroups/{id}/unsilence/', { params: { path: { id } } }); diff --git a/grafana-plugin/src/network/oncall-api/http-client.test.ts b/grafana-plugin/src/network/oncall-api/http-client.test.ts index 1d1fd31a..82fcd0f3 100644 --- a/grafana-plugin/src/network/oncall-api/http-client.test.ts +++ b/grafana-plugin/src/network/oncall-api/http-client.test.ts @@ -28,7 +28,16 @@ const REQUEST_CONFIG = { }; const URL = 'https://someurl.com'; const SUCCESSFUL_RESPONSE_MOCK = { ok: true }; -const FAILING_RESPONSE_MOCK = { ok: false, json: () => 'ERROR' }; +const FAILING_RESPONSE_MOCK = { + ok: false, + json: () => 'ERROR', + + // we need to have clone available to actually clone the response + clone: () => ({ + ok: false, + json: () => 'ERROR', + }), +}; const customFetch = getCustomFetchFn({ withGlobalErrorHandler: true }); describe('customFetch', () => { diff --git a/grafana-plugin/src/network/oncall-api/http-client.ts b/grafana-plugin/src/network/oncall-api/http-client.ts index 15a983a9..bc31667e 100644 --- a/grafana-plugin/src/network/oncall-api/http-client.ts +++ b/grafana-plugin/src/network/oncall-api/http-client.ts @@ -72,12 +72,13 @@ export const getCustomFetchFn = faro?.api.pushEvent('Request completed', { url }); return res; } else { - const errorData = await res.json(); + const errorData = await res.clone().json(); faro?.api.pushEvent('Request failed', { url }); faro?.api.pushError(errorData); if (withGlobalErrorHandler) { showApiError(res); } + throw res; } } diff --git a/grafana-plugin/src/utils/consts.ts b/grafana-plugin/src/utils/consts.ts index 2f607f35..6d4fae31 100644 --- a/grafana-plugin/src/utils/consts.ts +++ b/grafana-plugin/src/utils/consts.ts @@ -83,3 +83,5 @@ export enum OnCallAGStatus { Silenced = 'silenced', Acknowledged = 'acknowledged', } + +export const GENERIC_ERROR = 'An error has occurred. Please try again'; diff --git a/grafana-plugin/src/utils/decorators.ts b/grafana-plugin/src/utils/decorators.ts index 933d7b64..c1d10a75 100644 --- a/grafana-plugin/src/utils/decorators.ts +++ b/grafana-plugin/src/utils/decorators.ts @@ -36,7 +36,7 @@ export function WrapAutoLoadingState(callback: Function, actionKey: string): (.. type GlobalNotificationConfig = { success?: string; failure?: string; - composeFailureMessageFn?: (error: unknown) => string; + composeFailureMessageFn?: (error: Response) => Promise; failureType?: 'error' | 'warning'; }; @@ -50,7 +50,7 @@ export function WrapWithGlobalNotification( success && openNotification(success); } catch (err) { const open = failureType === 'error' ? openErrorNotification : openWarningNotification; - const message = composeFailureMessageFn ? composeFailureMessageFn(err) : failure; + const message = composeFailureMessageFn ? await composeFailureMessageFn(err) : failure; open(message); throw err; } @@ -73,7 +73,7 @@ export function WithGlobalNotification({ return response; } catch (err) { const open = failureType === 'error' ? openErrorNotification : openWarningNotification; - const message = composeFailureMessageFn ? composeFailureMessageFn(err) : failure; + const message = composeFailureMessageFn ? await composeFailureMessageFn(err) : failure; open(message); throw err; }