From 39f07e7477932bcc1086f1303225e743ca186340 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Fri, 9 May 2025 13:26:47 +0530 Subject: [PATCH] chore(error): update the channels module to use the new api errors (#7856) * chore(error): integrate new errors for channels create and test * chore(error): update all the channel APIs * chore(error): update the edit org http issue * chore(error): fix create channel test * chore(error): fix create channel test * chore(error): fix create channel test * chore(error): fix create channel test * chore(error): remove console logs --- frontend/src/api/ErrorResponseHandlerV2.ts | 12 +- frontend/src/api/channels/createEmail.ts | 17 +- frontend/src/api/channels/createMsTeams.ts | 17 +- frontend/src/api/channels/createOpsgenie.ts | 17 +- frontend/src/api/channels/createPager.ts | 17 +- frontend/src/api/channels/createSlack.ts | 17 +- frontend/src/api/channels/createWebhook.ts | 17 +- frontend/src/api/channels/delete.ts | 17 +- frontend/src/api/channels/editEmail.ts | 17 +- frontend/src/api/channels/editMsTeams.ts | 17 +- frontend/src/api/channels/editOpsgenie.ts | 17 +- frontend/src/api/channels/editPager.ts | 17 +- frontend/src/api/channels/editSlack.ts | 17 +- frontend/src/api/channels/editWebhook.ts | 17 +- frontend/src/api/channels/get.ts | 20 +- frontend/src/api/channels/getAll.ts | 21 +- frontend/src/api/channels/testEmail.ts | 17 +- frontend/src/api/channels/testMsTeams.ts | 17 +- frontend/src/api/channels/testOpsgenie.ts | 17 +- frontend/src/api/channels/testPager.ts | 17 +- frontend/src/api/channels/testSlack.ts | 17 +- frontend/src/api/channels/testWebhook.ts | 17 +- frontend/src/api/login/login.ts | 4 +- .../AllAlertChannels/AlertChannels.tsx | 4 +- .../src/container/AllAlertChannels/Delete.tsx | 27 +-- .../__tests__/AlertChannels.test.tsx | 13 +- .../AlertChannelsNormalUser.test.tsx | 29 ++- .../__tests__/CreateAlertChannel.test.tsx | 14 +- .../src/container/AllAlertChannels/index.tsx | 24 +- .../container/CreateAlertChannels/index.tsx | 209 ++++++------------ .../src/container/EditAlertChannels/index.tsx | 204 +++++++++-------- .../container/FormAlertRules/BasicInfo.tsx | 32 ++- .../FormAlertRules/ChannelSelect/index.tsx | 32 +-- .../DisplayName/index.tsx | 2 +- frontend/src/mocks-server/handlers.ts | 4 +- frontend/src/pages/ChannelsEdit/index.tsx | 18 +- frontend/src/types/api/channels/get.ts | 5 +- frontend/src/types/api/channels/getAll.ts | 5 +- frontend/src/types/api/error.ts | 8 + frontend/src/types/api/index.ts | 5 + 40 files changed, 476 insertions(+), 539 deletions(-) diff --git a/frontend/src/api/ErrorResponseHandlerV2.ts b/frontend/src/api/ErrorResponseHandlerV2.ts index cb1f2c7015..2ef6a5d44b 100644 --- a/frontend/src/api/ErrorResponseHandlerV2.ts +++ b/frontend/src/api/ErrorResponseHandlerV2.ts @@ -1,9 +1,9 @@ import { AxiosError } from 'axios'; -import { ErrorV2 } from 'types/api'; +import { ErrorV2Resp } from 'types/api'; import APIError from 'types/api/error'; // reference - https://axios-http.com/docs/handling_errors -export function ErrorResponseHandlerV2(error: AxiosError): never { +export function ErrorResponseHandlerV2(error: AxiosError): never { const { response, request } = error; // The request was made and the server responded with a status code // that falls out of the range of 2xx @@ -11,10 +11,10 @@ export function ErrorResponseHandlerV2(error: AxiosError): never { throw new APIError({ httpStatusCode: response.status || 500, error: { - code: response.data.code, - message: response.data.message, - url: response.data.url, - errors: response.data.errors, + code: response.data.error.code, + message: response.data.error.message, + url: response.data.error.url, + errors: response.data.error.errors, }, }); } diff --git a/frontend/src/api/channels/createEmail.ts b/frontend/src/api/channels/createEmail.ts index 7d0910d40f..7729eb12ca 100644 --- a/frontend/src/api/channels/createEmail.ts +++ b/frontend/src/api/channels/createEmail.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createEmail'; const create = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/channels', { + const response = await axios.post('/channels', { name: props.name, email_configs: [ { @@ -21,13 +21,12 @@ const create = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/createMsTeams.ts b/frontend/src/api/channels/createMsTeams.ts index 269d7c2103..1a01a9c788 100644 --- a/frontend/src/api/channels/createMsTeams.ts +++ b/frontend/src/api/channels/createMsTeams.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createMsTeams'; const create = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/channels', { + const response = await axios.post('/channels', { name: props.name, msteamsv2_configs: [ { @@ -21,13 +21,12 @@ const create = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/createOpsgenie.ts b/frontend/src/api/channels/createOpsgenie.ts index 4cf60f9e94..edc9ba3024 100644 --- a/frontend/src/api/channels/createOpsgenie.ts +++ b/frontend/src/api/channels/createOpsgenie.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createOpsgenie'; const create = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/channels', { + const response = await axios.post('/channels', { name: props.name, opsgenie_configs: [ { @@ -24,13 +24,12 @@ const create = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/createPager.ts b/frontend/src/api/channels/createPager.ts index 682874f7b4..46bad5daf3 100644 --- a/frontend/src/api/channels/createPager.ts +++ b/frontend/src/api/channels/createPager.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createPager'; const create = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/channels', { + const response = await axios.post('/channels', { name: props.name, pagerduty_configs: [ { @@ -29,13 +29,12 @@ const create = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/createSlack.ts b/frontend/src/api/channels/createSlack.ts index d68beddc9b..4cd78d6396 100644 --- a/frontend/src/api/channels/createSlack.ts +++ b/frontend/src/api/channels/createSlack.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createSlack'; const create = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/channels', { + const response = await axios.post('/channels', { name: props.name, slack_configs: [ { @@ -22,13 +22,12 @@ const create = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/createWebhook.ts b/frontend/src/api/channels/createWebhook.ts index f63cb2e32b..2a5f7fa7b2 100644 --- a/frontend/src/api/channels/createWebhook.ts +++ b/frontend/src/api/channels/createWebhook.ts @@ -1,12 +1,12 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createWebhook'; const create = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { let httpConfig = {}; const username = props.username ? props.username.trim() : ''; @@ -28,7 +28,7 @@ const create = async ( }; } - const response = await axios.post('/channels', { + const response = await axios.post('/channels', { name: props.name, webhook_configs: [ { @@ -40,13 +40,12 @@ const create = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/delete.ts b/frontend/src/api/channels/delete.ts index a5366af6cc..387817ef20 100644 --- a/frontend/src/api/channels/delete.ts +++ b/frontend/src/api/channels/delete.ts @@ -1,23 +1,22 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/delete'; const deleteChannel = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.delete(`/channels/${props.id}`); + const response = await axios.delete(`/channels/${props.id}`); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/editEmail.ts b/frontend/src/api/channels/editEmail.ts index b80fe687a9..2a43a53d32 100644 --- a/frontend/src/api/channels/editEmail.ts +++ b/frontend/src/api/channels/editEmail.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/editEmail'; const editEmail = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.put(`/channels/${props.id}`, { + const response = await axios.put(`/channels/${props.id}`, { name: props.name, email_configs: [ { @@ -21,13 +21,12 @@ const editEmail = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/editMsTeams.ts b/frontend/src/api/channels/editMsTeams.ts index fddc9485b7..67b25fcf3a 100644 --- a/frontend/src/api/channels/editMsTeams.ts +++ b/frontend/src/api/channels/editMsTeams.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/editMsTeams'; const editMsTeams = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.put(`/channels/${props.id}`, { + const response = await axios.put(`/channels/${props.id}`, { name: props.name, msteamsv2_configs: [ { @@ -21,13 +21,12 @@ const editMsTeams = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/editOpsgenie.ts b/frontend/src/api/channels/editOpsgenie.ts index 1eb65c7add..e77e24ef26 100644 --- a/frontend/src/api/channels/editOpsgenie.ts +++ b/frontend/src/api/channels/editOpsgenie.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorResponse, ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/editOpsgenie'; const editOpsgenie = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise | ErrorResponse> => { try { - const response = await axios.put(`/channels/${props.id}`, { + const response = await axios.put(`/channels/${props.id}`, { name: props.name, opsgenie_configs: [ { @@ -25,13 +25,12 @@ const editOpsgenie = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + return ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/editPager.ts b/frontend/src/api/channels/editPager.ts index 091d42b640..dc1f1c3709 100644 --- a/frontend/src/api/channels/editPager.ts +++ b/frontend/src/api/channels/editPager.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/editPager'; const editPager = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.put(`/channels/${props.id}`, { + const response = await axios.put(`/channels/${props.id}`, { name: props.name, pagerduty_configs: [ { @@ -29,13 +29,12 @@ const editPager = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/editSlack.ts b/frontend/src/api/channels/editSlack.ts index 639646452c..000444d33d 100644 --- a/frontend/src/api/channels/editSlack.ts +++ b/frontend/src/api/channels/editSlack.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/editSlack'; const editSlack = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.put(`/channels/${props.id}`, { + const response = await axios.put(`/channels/${props.id}`, { name: props.name, slack_configs: [ { @@ -22,13 +22,12 @@ const editSlack = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/editWebhook.ts b/frontend/src/api/channels/editWebhook.ts index f6a661644f..2c818738af 100644 --- a/frontend/src/api/channels/editWebhook.ts +++ b/frontend/src/api/channels/editWebhook.ts @@ -1,12 +1,12 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/editWebhook'; const editWebhook = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { let httpConfig = {}; const username = props.username ? props.username.trim() : ''; @@ -28,7 +28,7 @@ const editWebhook = async ( }; } - const response = await axios.put(`/channels/${props.id}`, { + const response = await axios.put(`/channels/${props.id}`, { name: props.name, webhook_configs: [ { @@ -40,13 +40,12 @@ const editWebhook = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/get.ts b/frontend/src/api/channels/get.ts index 39c40ec935..18080cfacc 100644 --- a/frontend/src/api/channels/get.ts +++ b/frontend/src/api/channels/get.ts @@ -1,23 +1,21 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/get'; +import { Channels } from 'types/api/channels/getAll'; -const get = async ( - props: Props, -): Promise | ErrorResponse> => { +const get = async (props: Props): Promise> => { try { - const response = await axios.get(`/channels/${props.id}`); + const response = await axios.get(`/channels/${props.id}`); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/getAll.ts b/frontend/src/api/channels/getAll.ts index 11b530a84c..5b98bb83b9 100644 --- a/frontend/src/api/channels/getAll.ts +++ b/frontend/src/api/channels/getAll.ts @@ -1,23 +1,20 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; -import { PayloadProps } from 'types/api/channels/getAll'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; +import { Channels, PayloadProps } from 'types/api/channels/getAll'; -const getAll = async (): Promise< - SuccessResponse | ErrorResponse -> => { +const getAll = async (): Promise> => { try { - const response = await axios.get('/channels'); + const response = await axios.get('/channels'); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/testEmail.ts b/frontend/src/api/channels/testEmail.ts index 825836abea..66a934ca9b 100644 --- a/frontend/src/api/channels/testEmail.ts +++ b/frontend/src/api/channels/testEmail.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createEmail'; const testEmail = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/testChannel', { + const response = await axios.post('/testChannel', { name: props.name, email_configs: [ { @@ -21,13 +21,12 @@ const testEmail = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/testMsTeams.ts b/frontend/src/api/channels/testMsTeams.ts index 60474e3438..5399d7db4a 100644 --- a/frontend/src/api/channels/testMsTeams.ts +++ b/frontend/src/api/channels/testMsTeams.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createMsTeams'; const testMsTeams = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/testChannel', { + const response = await axios.post('/testChannel', { name: props.name, msteamsv2_configs: [ { @@ -21,13 +21,12 @@ const testMsTeams = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/testOpsgenie.ts b/frontend/src/api/channels/testOpsgenie.ts index 780a4432ae..1cdbfbe36d 100644 --- a/frontend/src/api/channels/testOpsgenie.ts +++ b/frontend/src/api/channels/testOpsgenie.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createOpsgenie'; const testOpsgenie = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/testChannel', { + const response = await axios.post('/testChannel', { name: props.name, opsgenie_configs: [ { @@ -24,13 +24,12 @@ const testOpsgenie = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/testPager.ts b/frontend/src/api/channels/testPager.ts index 717404649a..b247229c79 100644 --- a/frontend/src/api/channels/testPager.ts +++ b/frontend/src/api/channels/testPager.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createPager'; const testPager = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/testChannel', { + const response = await axios.post('/testChannel', { name: props.name, pagerduty_configs: [ { @@ -29,13 +29,12 @@ const testPager = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/testSlack.ts b/frontend/src/api/channels/testSlack.ts index a2b4b1f40a..39f04637af 100644 --- a/frontend/src/api/channels/testSlack.ts +++ b/frontend/src/api/channels/testSlack.ts @@ -1,14 +1,14 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createSlack'; const testSlack = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { - const response = await axios.post('/testChannel', { + const response = await axios.post('/testChannel', { name: props.name, slack_configs: [ { @@ -22,13 +22,12 @@ const testSlack = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/channels/testWebhook.ts b/frontend/src/api/channels/testWebhook.ts index 26c93f3d4a..cce92ff91f 100644 --- a/frontend/src/api/channels/testWebhook.ts +++ b/frontend/src/api/channels/testWebhook.ts @@ -1,12 +1,12 @@ import axios from 'api'; -import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/channels/createWebhook'; const testWebhook = async ( props: Props, -): Promise | ErrorResponse> => { +): Promise> => { try { let httpConfig = {}; const username = props.username ? props.username.trim() : ''; @@ -28,7 +28,7 @@ const testWebhook = async ( }; } - const response = await axios.post('/testChannel', { + const response = await axios.post('/testChannel', { name: props.name, webhook_configs: [ { @@ -40,13 +40,12 @@ const testWebhook = async ( }); return { - statusCode: 200, - error: null, - message: 'Success', - payload: response.data.data, + httpStatusCode: response.status, + data: response.data, }; } catch (error) { - return ErrorResponseHandler(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); + throw error; } }; diff --git a/frontend/src/api/login/login.ts b/frontend/src/api/login/login.ts index 6ca54bd618..a8a6af4d72 100644 --- a/frontend/src/api/login/login.ts +++ b/frontend/src/api/login/login.ts @@ -1,7 +1,7 @@ import axios from 'api'; import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2'; import { AxiosError } from 'axios'; -import { ErrorV2, SuccessResponseV2 } from 'types/api'; +import { ErrorV2Resp, SuccessResponseV2 } from 'types/api'; import { PayloadProps, Props } from 'types/api/user/login'; const login = async ( @@ -17,7 +17,7 @@ const login = async ( data: response.data, }; } catch (error) { - ErrorResponseHandlerV2(error as AxiosError); + ErrorResponseHandlerV2(error as AxiosError); // this line is never reached but ts isn't detecting the never type properly for the ErrorResponseHandlerV2 throw error; } diff --git a/frontend/src/container/AllAlertChannels/AlertChannels.tsx b/frontend/src/container/AllAlertChannels/AlertChannels.tsx index 9f1639d203..00335f1367 100644 --- a/frontend/src/container/AllAlertChannels/AlertChannels.tsx +++ b/frontend/src/container/AllAlertChannels/AlertChannels.tsx @@ -10,7 +10,7 @@ import { useAppContext } from 'providers/App/App'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { generatePath } from 'react-router-dom'; -import { Channels, PayloadProps } from 'types/api/channels/getAll'; +import { Channels } from 'types/api/channels/getAll'; import Delete from './Delete'; @@ -68,7 +68,7 @@ function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element { } interface AlertChannelsProps { - allChannels: PayloadProps; + allChannels: Channels[]; } export default AlertChannels; diff --git a/frontend/src/container/AllAlertChannels/Delete.tsx b/frontend/src/container/AllAlertChannels/Delete.tsx index c5edbf1e6a..2c617af109 100644 --- a/frontend/src/container/AllAlertChannels/Delete.tsx +++ b/frontend/src/container/AllAlertChannels/Delete.tsx @@ -4,6 +4,7 @@ import deleteChannel from 'api/channels/delete'; import { Dispatch, SetStateAction, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Channels } from 'types/api/channels/getAll'; +import APIError from 'types/api/error'; function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element { const { t } = useTranslation(['channels']); @@ -12,30 +13,20 @@ function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element { const onClickHandler = async (): Promise => { try { setLoading(true); - const response = await deleteChannel({ + await deleteChannel({ id, }); - if (response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_delete_success'), - }); - setChannels((preChannels) => preChannels.filter((e) => e.id !== id)); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_delete_unexp_error'), - }); - } + notifications.success({ + message: 'Success', + description: t('channel_delete_success'), + }); + setChannels((preChannels) => preChannels.filter((e) => e.id !== id)); setLoading(false); } catch (error) { notifications.error({ - message: 'Error', - description: - error instanceof Error - ? error.toString() - : t('channel_delete_unexp_error'), + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), }); setLoading(false); } diff --git a/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx index 6b99243cf0..32f597a82a 100644 --- a/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx +++ b/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx @@ -1,15 +1,7 @@ import ROUTES from 'constants/routes'; import AlertChannels from 'container/AllAlertChannels'; -import { allAlertChannels } from 'mocks-server/__mockdata__/alerts'; import { act, fireEvent, render, screen, waitFor } from 'tests/test-utils'; -jest.mock('hooks/useFetch', () => ({ - __esModule: true, - default: jest.fn().mockImplementation(() => ({ - payload: allAlertChannels, - })), -})); - const successNotification = jest.fn(); jest.mock('hooks/useNotifications', () => ({ __esModule: true, @@ -29,8 +21,11 @@ jest.mock('react-router-dom', () => ({ })); describe('Alert Channels Settings List page', () => { - beforeEach(() => { + beforeEach(async () => { render(); + await waitFor(() => + expect(screen.getByText('sending_channels_note')).toBeInTheDocument(), + ); }); afterEach(() => { jest.restoreAllMocks(); diff --git a/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx index 542e70c0bc..162f0fb8fb 100644 --- a/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx +++ b/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx @@ -1,15 +1,8 @@ +/* eslint-disable sonarjs/no-identical-functions */ import ROUTES from 'constants/routes'; import AlertChannels from 'container/AllAlertChannels'; -import { allAlertChannels } from 'mocks-server/__mockdata__/alerts'; import { fireEvent, render, screen, waitFor } from 'tests/test-utils'; -jest.mock('hooks/useFetch', () => ({ - __esModule: true, - default: jest.fn().mockImplementation(() => ({ - payload: allAlertChannels, - })), -})); - const successNotification = jest.fn(); jest.mock('hooks/useNotifications', () => ({ __esModule: true, @@ -34,27 +27,31 @@ jest.mock('react-router-dom', () => ({ })); describe('Alert Channels Settings List page (Normal User)', () => { - beforeEach(() => { + beforeEach(async () => { render(); + await waitFor(() => + expect(screen.getByText('sending_channels_note')).toBeInTheDocument(), + ); }); afterEach(() => { jest.restoreAllMocks(); }); describe('Should display the Alert Channels page properly', () => { - it('Should check if "The alerts will be sent to all the configured channels." is visible ', () => { - expect(screen.getByText('sending_channels_note')).toBeInTheDocument(); + it('Should check if "The alerts will be sent to all the configured channels." is visible ', async () => { + await waitFor(() => + expect(screen.getByText('sending_channels_note')).toBeInTheDocument(), + ); }); - it('Should check if "New Alert Channel" Button is visble and disabled', () => { + it('Should check if "New Alert Channel" Button is visble and disabled', async () => { const newAlertButton = screen.getByRole('button', { name: 'plus button_new_channel', }); - expect(newAlertButton).toBeInTheDocument(); + await waitFor(() => expect(newAlertButton).toBeInTheDocument()); expect(newAlertButton).toBeDisabled(); }); it('Should check if the help icon is visible and displays "tooltip_notification_channels ', async () => { const helpIcon = screen.getByLabelText('question-circle'); - fireEvent.mouseOver(helpIcon); await waitFor(() => { @@ -64,13 +61,13 @@ describe('Alert Channels Settings List page (Normal User)', () => { }); }); describe('Should check if the channels table is properly displayed', () => { - it('Should check if the table columns are properly displayed', () => { + it('Should check if the table columns are properly displayed', async () => { expect(screen.getByText('column_channel_name')).toBeInTheDocument(); expect(screen.getByText('column_channel_type')).toBeInTheDocument(); expect(screen.queryByText('column_channel_action')).not.toBeInTheDocument(); }); - it('Should check if the data in the table is displayed properly', () => { + it('Should check if the data in the table is displayed properly', async () => { expect(screen.getByText('Dummy-Channel')).toBeInTheDocument(); expect(screen.getAllByText('slack')[0]).toBeInTheDocument(); expect(screen.queryByText('column_channel_edit')).not.toBeInTheDocument(); diff --git a/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx index b851acf878..3735ed1ff6 100644 --- a/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx +++ b/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx @@ -119,12 +119,7 @@ describe('Create Alert Channel', () => { fireEvent.click(saveButton); - await waitFor(() => - expect(errorNotification).toHaveBeenCalledWith({ - description: 'Something went wrong', - message: 'Error', - }), - ); + await waitFor(() => expect(errorNotification).toHaveBeenCalled()); }); it('Should check if clicking on Test button shows "An alert has been sent to this channel" success message if testing passes', async () => { server.use( @@ -158,12 +153,7 @@ describe('Create Alert Channel', () => { fireEvent.click(testButton); - await waitFor(() => - expect(errorNotification).toHaveBeenCalledWith({ - message: 'Error', - description: 'channel_test_failed', - }), - ); + await waitFor(() => expect(errorNotification).toHaveBeenCalled()); }); }); describe('New Alert Channel Cascading Fields Based on Channel Type', () => { diff --git a/frontend/src/container/AllAlertChannels/index.tsx b/frontend/src/container/AllAlertChannels/index.tsx index a23e5a423f..9b8c44fed6 100644 --- a/frontend/src/container/AllAlertChannels/index.tsx +++ b/frontend/src/container/AllAlertChannels/index.tsx @@ -6,12 +6,15 @@ import Spinner from 'components/Spinner'; import TextToolTip from 'components/TextToolTip'; import ROUTES from 'constants/routes'; import useComponentPermission from 'hooks/useComponentPermission'; -import useFetch from 'hooks/useFetch'; import history from 'lib/history'; import { isUndefined } from 'lodash-es'; import { useAppContext } from 'providers/App/App'; import { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; +import { SuccessResponseV2 } from 'types/api'; +import { Channels } from 'types/api/channels/getAll'; +import APIError from 'types/api/error'; import AlertChannelsComponent from './AlertChannels'; import { Button, ButtonContainer, RightActionContainer } from './styles'; @@ -29,21 +32,26 @@ function AlertChannels(): JSX.Element { history.push(ROUTES.CHANNELS_NEW); }, []); - const { loading, payload, error, errorMessage } = useFetch(getAll); + const { isLoading, data, error } = useQuery< + SuccessResponseV2, + APIError + >(['getChannels'], { + queryFn: () => getAll(), + }); useEffect(() => { - if (!isUndefined(payload)) { + if (!isUndefined(data?.data)) { logEvent('Alert Channel: Channel list page visited', { - number: payload?.length, + number: data?.data?.length, }); } - }, [payload]); + }, [data?.data]); if (error) { - return {errorMessage}; + return {error.getErrorMessage()}; } - if (loading || payload === undefined) { + if (isLoading || isUndefined(data?.data)) { return ; } @@ -78,7 +86,7 @@ function AlertChannels(): JSX.Element { - + ); } diff --git a/frontend/src/container/CreateAlertChannels/index.tsx b/frontend/src/container/CreateAlertChannels/index.tsx index 7345fa4ef9..8651474347 100644 --- a/frontend/src/container/CreateAlertChannels/index.tsx +++ b/frontend/src/container/CreateAlertChannels/index.tsx @@ -18,6 +18,7 @@ import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import APIError from 'types/api/error'; import { ChannelType, @@ -136,28 +137,17 @@ function CreateAlertChannels({ setSavingState(true); try { - const response = await createSlackApi(prepareSlackRequest()); - - if (response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_creation_done'), - }); - history.replace(ROUTES.ALL_CHANNELS); - return { status: 'success', statusMessage: t('channel_creation_done') }; - } - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), + await createSlackApi(prepareSlackRequest()); + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), }); - return { - status: 'failed', - statusMessage: response.error || t('channel_creation_failed'), - }; + history.replace(ROUTES.ALL_CHANNELS); + return { status: 'success', statusMessage: t('channel_creation_done') }; } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_creation_failed'), + message: (error as APIError).error.error.code, + description: (error as APIError).error.error.message, }); return { status: 'failed', statusMessage: t('channel_creation_failed') }; } finally { @@ -204,27 +194,17 @@ function CreateAlertChannels({ setSavingState(true); try { const request = prepareWebhookRequest(); - const response = await createWebhookApi(request); - if (response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_creation_done'), - }); - history.replace(ROUTES.ALL_CHANNELS); - return { status: 'success', statusMessage: t('channel_creation_done') }; - } - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), + await createWebhookApi(request); + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), }); - return { - status: 'failed', - statusMessage: response.error || t('channel_creation_failed'), - }; + history.replace(ROUTES.ALL_CHANNELS); + return { status: 'success', statusMessage: t('channel_creation_done') }; } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_creation_failed'), + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), }); return { status: 'failed', statusMessage: t('channel_creation_failed') }; } finally { @@ -264,34 +244,19 @@ function CreateAlertChannels({ try { if (request) { - const response = await createPagerApi(request); - - if (response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_creation_done'), - }); - history.replace(ROUTES.ALL_CHANNELS); - return { status: 'success', statusMessage: t('channel_creation_done') }; - } - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), + await createPagerApi(request); + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), }); - return { - status: 'failed', - statusMessage: response.error || t('channel_creation_failed'), - }; + history.replace(ROUTES.ALL_CHANNELS); + return { status: 'success', statusMessage: t('channel_creation_done') }; } - notifications.error({ - message: 'Error', - description: t('channel_creation_failed'), - }); return { status: 'failed', statusMessage: t('channel_creation_failed') }; } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_creation_failed'), + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), }); return { status: 'failed', statusMessage: t('channel_creation_failed') }; } finally { @@ -313,30 +278,18 @@ function CreateAlertChannels({ const onOpsgenieHandler = useCallback(async () => { setSavingState(true); - try { - const response = await createOpsgenie(prepareOpsgenieRequest()); - - if (response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_creation_done'), - }); - history.replace(ROUTES.ALL_CHANNELS); - return { status: 'success', statusMessage: t('channel_creation_done') }; - } - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), + await createOpsgenie(prepareOpsgenieRequest()); + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), }); - return { - status: 'failed', - statusMessage: response.error || t('channel_creation_failed'), - }; + history.replace(ROUTES.ALL_CHANNELS); + return { status: 'success', statusMessage: t('channel_creation_done') }; } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_creation_failed'), + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), }); return { status: 'failed', statusMessage: t('channel_creation_failed') }; } finally { @@ -359,27 +312,17 @@ function CreateAlertChannels({ setSavingState(true); try { const request = prepareEmailRequest(); - const response = await createEmail(request); - if (response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_creation_done'), - }); - history.replace(ROUTES.ALL_CHANNELS); - return { status: 'success', statusMessage: t('channel_creation_done') }; - } - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), + await createEmail(request); + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), }); - return { - status: 'failed', - statusMessage: response.error || t('channel_creation_failed'), - }; + history.replace(ROUTES.ALL_CHANNELS); + return { status: 'success', statusMessage: t('channel_creation_done') }; } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_creation_failed'), + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), }); return { status: 'failed', statusMessage: t('channel_creation_failed') }; } finally { @@ -402,28 +345,17 @@ function CreateAlertChannels({ setSavingState(true); try { - const response = await createMsTeamsApi(prepareMsTeamsRequest()); - - if (response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_creation_done'), - }); - history.replace(ROUTES.ALL_CHANNELS); - return { status: 'success', statusMessage: t('channel_creation_done') }; - } - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), + await createMsTeamsApi(prepareMsTeamsRequest()); + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), }); - return { - status: 'failed', - statusMessage: response.error || t('channel_creation_failed'), - }; + history.replace(ROUTES.ALL_CHANNELS); + return { status: 'success', statusMessage: t('channel_creation_done') }; } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_creation_failed'), + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), }); return { status: 'failed', statusMessage: t('channel_creation_failed') }; } finally { @@ -481,31 +413,30 @@ function CreateAlertChannels({ setTestingState(true); try { let request; - let response; switch (channelType) { case ChannelType.Webhook: request = prepareWebhookRequest(); - response = await testWebhookApi(request); + await testWebhookApi(request); break; case ChannelType.Slack: request = prepareSlackRequest(); - response = await testSlackApi(request); + await testSlackApi(request); break; case ChannelType.Pagerduty: request = preparePagerRequest(); - if (request) response = await testPagerApi(request); + if (request) await testPagerApi(request); break; case ChannelType.MsTeams: request = prepareMsTeamsRequest(); - response = await testMsTeamsApi(request); + await testMsTeamsApi(request); break; case ChannelType.Opsgenie: request = prepareOpsgenieRequest(); - response = await testOpsGenie(request); + await testOpsGenie(request); break; case ChannelType.Email: request = prepareEmailRequest(); - response = await testEmail(request); + await testEmail(request); break; default: notifications.error({ @@ -516,30 +447,28 @@ function CreateAlertChannels({ return; } - if (response && response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_test_done'), - }); - } else { - notifications.error({ - message: 'Error', - description: t('channel_test_failed'), - }); - } - + notifications.success({ + message: 'Success', + description: t('channel_test_done'), + }); logEvent('Alert Channel: Test notification', { type: channelType, sendResolvedAlert: selectedConfig?.send_resolved, name: selectedConfig?.name, new: 'true', - status: - response && response.statusCode === 200 ? 'Test success' : 'Test failed', + status: 'Test success', }); } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_test_unexpected'), + message: (error as APIError).error.error.code, + description: (error as APIError).error.error.message, + }); + logEvent('Alert Channel: Test notification', { + type: channelType, + sendResolvedAlert: selectedConfig?.send_resolved, + name: selectedConfig?.name, + new: 'true', + status: 'Test failed', }); } diff --git a/frontend/src/container/EditAlertChannels/index.tsx b/frontend/src/container/EditAlertChannels/index.tsx index 264d7de821..d081f82c11 100644 --- a/frontend/src/container/EditAlertChannels/index.tsx +++ b/frontend/src/container/EditAlertChannels/index.tsx @@ -29,6 +29,7 @@ import history from 'lib/history'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; +import APIError from 'types/api/error'; function EditAlertChannels({ initialValue, @@ -93,9 +94,8 @@ function EditAlertChannels({ return { status: 'failed', statusMessage: t('webhook_url_required') }; } - const response = await editSlackApi(prepareSlackRequest()); - - if (response.statusCode === 200) { + try { + await editSlackApi(prepareSlackRequest()); notifications.success({ message: 'Success', description: t('channel_edit_done'), @@ -103,16 +103,19 @@ function EditAlertChannels({ history.replace(ROUTES.ALL_CHANNELS); return { status: 'success', statusMessage: t('channel_edit_done') }; + } catch (error) { + notifications.error({ + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), + }); + return { + status: 'failed', + statusMessage: + (error as APIError).getErrorMessage() || t('channel_edit_failed'), + }; + } finally { + setSavingState(false); } - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); - setSavingState(false); - return { - status: 'failed', - statusMessage: response.error || t('channel_edit_failed'), - }; }, [prepareSlackRequest, t, notifications, selectedConfig]); const prepareWebhookRequest = useCallback(() => { @@ -150,9 +153,8 @@ function EditAlertChannels({ return { status: 'failed', statusMessage: t('username_no_password') }; } - const response = await editWebhookApi(prepareWebhookRequest()); - - if (response.statusCode === 200) { + try { + await editWebhookApi(prepareWebhookRequest()); notifications.success({ message: 'Success', description: t('channel_edit_done'), @@ -160,14 +162,19 @@ function EditAlertChannels({ history.replace(ROUTES.ALL_CHANNELS); return { status: 'success', statusMessage: t('channel_edit_done') }; + } catch (error) { + notifications.error({ + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), + }); + return { + status: 'failed', + statusMessage: + (error as APIError).getErrorMessage() || t('channel_edit_failed'), + }; + } finally { + setSavingState(false); } - showError(response.error || t('channel_edit_failed')); - - setSavingState(false); - return { - status: 'failed', - statusMessage: response.error || t('channel_edit_failed'), - }; }, [prepareWebhookRequest, t, notifications, selectedConfig]); const prepareEmailRequest = useCallback( @@ -185,25 +192,28 @@ function EditAlertChannels({ const onEmailEditHandler = useCallback(async () => { setSavingState(true); const request = prepareEmailRequest(); - const response = await editEmail(request); - if (response.statusCode === 200) { + + try { + await editEmail(request); notifications.success({ message: 'Success', description: t('channel_edit_done'), }); history.replace(ROUTES.ALL_CHANNELS); return { status: 'success', statusMessage: t('channel_edit_done') }; + } catch (error) { + notifications.error({ + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), + }); + return { + status: 'failed', + statusMessage: + (error as APIError).getErrorMessage() || t('channel_edit_failed'), + }; + } finally { + setSavingState(false); } - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); - - setSavingState(false); - return { - status: 'failed', - statusMessage: response.error || t('channel_edit_failed'), - }; }, [prepareEmailRequest, t, notifications]); const preparePagerRequest = useCallback( @@ -237,27 +247,28 @@ function EditAlertChannels({ setSavingState(false); return { status: 'failed', statusMessage: validationError }; } - const response = await editPagerApi(preparePagerRequest()); - if (response.statusCode === 200) { + try { + await editPagerApi(preparePagerRequest()); notifications.success({ message: 'Success', description: t('channel_edit_done'), }); - history.replace(ROUTES.ALL_CHANNELS); return { status: 'success', statusMessage: t('channel_edit_done') }; + } catch (error) { + notifications.error({ + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), + }); + return { + status: 'failed', + statusMessage: + (error as APIError).getErrorMessage() || t('channel_edit_failed'), + }; + } finally { + setSavingState(false); } - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); - - setSavingState(false); - return { - status: 'failed', - statusMessage: response.error || t('channel_edit_failed'), - }; }, [preparePagerRequest, notifications, selectedConfig, t]); const prepareOpsgenieRequest = useCallback( @@ -284,28 +295,27 @@ function EditAlertChannels({ setSavingState(false); return { status: 'failed', statusMessage: t('api_key_required') }; } - - const response = await editOpsgenie(prepareOpsgenieRequest()); - - if (response.statusCode === 200) { + try { + await editOpsgenie(prepareOpsgenieRequest()); notifications.success({ message: 'Success', description: t('channel_edit_done'), }); - history.replace(ROUTES.ALL_CHANNELS); return { status: 'success', statusMessage: t('channel_edit_done') }; + } catch (error) { + notifications.error({ + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), + }); + return { + status: 'failed', + statusMessage: + (error as APIError).getErrorMessage() || t('channel_edit_failed'), + }; + } finally { + setSavingState(false); } - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); - - setSavingState(false); - return { - status: 'failed', - statusMessage: response.error || t('channel_edit_failed'), - }; }, [prepareOpsgenieRequest, t, notifications, selectedConfig]); const prepareMsTeamsRequest = useCallback( @@ -332,27 +342,27 @@ function EditAlertChannels({ return { status: 'failed', statusMessage: t('webhook_url_required') }; } - const response = await editMsTeamsApi(prepareMsTeamsRequest()); - - if (response.statusCode === 200) { + try { + await editMsTeamsApi(prepareMsTeamsRequest()); notifications.success({ message: 'Success', description: t('channel_edit_done'), }); - history.replace(ROUTES.ALL_CHANNELS); return { status: 'success', statusMessage: t('channel_edit_done') }; + } catch (error) { + notifications.error({ + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), + }); + return { + status: 'failed', + statusMessage: + (error as APIError).getErrorMessage() || t('channel_edit_failed'), + }; + } finally { + setSavingState(false); } - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); - - setSavingState(false); - return { - status: 'failed', - statusMessage: response.error || t('channel_edit_failed'), - }; }, [prepareMsTeamsRequest, t, notifications, selectedConfig]); const onSaveHandler = useCallback( @@ -396,31 +406,30 @@ function EditAlertChannels({ setTestingState(true); try { let request; - let response; switch (channelType) { case ChannelType.Webhook: request = prepareWebhookRequest(); - response = await testWebhookApi(request); + await testWebhookApi(request); break; case ChannelType.Slack: request = prepareSlackRequest(); - response = await testSlackApi(request); + await testSlackApi(request); break; case ChannelType.Pagerduty: request = preparePagerRequest(); - if (request) response = await testPagerApi(request); + if (request) await testPagerApi(request); break; case ChannelType.MsTeams: request = prepareMsTeamsRequest(); - if (request) response = await testMsTeamsApi(request); + if (request) await testMsTeamsApi(request); break; case ChannelType.Opsgenie: request = prepareOpsgenieRequest(); - if (request) response = await testOpsgenie(request); + if (request) await testOpsgenie(request); break; case ChannelType.Email: request = prepareEmailRequest(); - if (request) response = await testEmail(request); + if (request) await testEmail(request); break; default: notifications.error({ @@ -431,29 +440,28 @@ function EditAlertChannels({ return; } - if (response && response.statusCode === 200) { - notifications.success({ - message: 'Success', - description: t('channel_test_done'), - }); - } else { - notifications.error({ - message: 'Error', - description: t('channel_test_failed'), - }); - } + notifications.success({ + message: 'Success', + description: t('channel_test_done'), + }); logEvent('Alert Channel: Test notification', { type: channelType, sendResolvedAlert: selectedConfig?.send_resolved, name: selectedConfig?.name, new: 'false', - status: - response && response.statusCode === 200 ? 'Test success' : 'Test failed', + status: 'Test success', }); } catch (error) { notifications.error({ - message: 'Error', - description: t('channel_test_failed'), + message: (error as APIError).getErrorCode(), + description: (error as APIError).getErrorMessage(), + }); + logEvent('Alert Channel: Test notification', { + type: channelType, + sendResolvedAlert: selectedConfig?.send_resolved, + name: selectedConfig?.name, + new: 'false', + status: 'Test failed', }); } setTestingState(false); diff --git a/frontend/src/container/FormAlertRules/BasicInfo.tsx b/frontend/src/container/FormAlertRules/BasicInfo.tsx index 55fb40d1a0..0e2bbc862e 100644 --- a/frontend/src/container/FormAlertRules/BasicInfo.tsx +++ b/frontend/src/container/FormAlertRules/BasicInfo.tsx @@ -2,17 +2,20 @@ import './FormAlertRules.styles.scss'; import { PlusOutlined } from '@ant-design/icons'; import { Button, Form, Select, Switch, Tooltip } from 'antd'; -import getChannels from 'api/channels/getAll'; +import getAll from 'api/channels/getAll'; import logEvent from 'api/common/logEvent'; import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts'; import ROUTES from 'constants/routes'; import useComponentPermission from 'hooks/useComponentPermission'; -import useFetch from 'hooks/useFetch'; import { useAppContext } from 'providers/App/App'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; +import { SuccessResponseV2 } from 'types/api'; import { AlertTypes } from 'types/api/alerts/alertTypes'; import { AlertDef, Labels } from 'types/api/alerts/def'; +import { Channels } from 'types/api/channels/getAll'; +import APIError from 'types/api/error'; import { requireErrorMessage } from 'utils/form/requireErrorMessage'; import { popupContainer } from 'utils/selectPopupContainer'; @@ -42,7 +45,13 @@ function BasicInfo({ }: BasicInfoProps): JSX.Element { const { t } = useTranslation('alerts'); - const channels = useFetch(getChannels); + const { isLoading, data, error, isError, refetch } = useQuery< + SuccessResponseV2, + APIError + >(['getChannels'], { + queryFn: () => getAll(), + }); + const { user } = useAppContext(); const [addNewChannelPermission] = useComponentPermission( ['add_new_channel'], @@ -72,7 +81,7 @@ function BasicInfo({ }); }; - const noChannels = channels.payload?.length === 0; + const noChannels = data?.data?.length === 0; const handleCreateNewChannels = useCallback(() => { logEvent('Alert: Create notification channel button clicked', { dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes], @@ -84,18 +93,18 @@ function BasicInfo({ const hasLoggedEvent = useRef(false); useEffect(() => { - if (!channels.loading && isNewRule && !hasLoggedEvent.current) { + if (!isLoading && isNewRule && !hasLoggedEvent.current) { logEvent('Alert: New alert creation page visited', { dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes], - numberOfChannels: channels?.payload?.length, + numberOfChannels: data?.data?.length, }); hasLoggedEvent.current = true; } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [channels.loading]); + }, [isLoading]); const refetchChannels = async (): Promise => { - await channels.refetch(); + await refetch(); }; return ( @@ -192,7 +201,7 @@ function BasicInfo({ @@ -220,7 +229,10 @@ function BasicInfo({ disabled={shouldBroadCastToAllChannels} currentValue={alertDef.preferredChannels} handleCreateNewChannels={handleCreateNewChannels} - channels={channels} + channels={data?.data || []} + isLoading={isLoading} + hasError={isError} + error={error as APIError} onSelectChannels={(preferredChannels): void => { setAlertDef({ ...alertDef, diff --git a/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx b/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx index d9237dcca9..1dd152a87a 100644 --- a/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx +++ b/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx @@ -1,12 +1,12 @@ import { PlusOutlined } from '@ant-design/icons'; import { Select, Spin } from 'antd'; import useComponentPermission from 'hooks/useComponentPermission'; -import { State } from 'hooks/useFetch'; import { useNotifications } from 'hooks/useNotifications'; import { useAppContext } from 'providers/App/App'; import { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; -import { PayloadProps } from 'types/api/channels/getAll'; +import { Channels } from 'types/api/channels/getAll'; +import APIError from 'types/api/error'; import { StyledCreateChannelOption, StyledSelect } from './styles'; @@ -15,7 +15,10 @@ export interface ChannelSelectProps { currentValue?: string[]; onSelectChannels: (s: string[]) => void; onDropdownOpen: () => void; - channels: State; + isLoading: boolean; + channels: Channels[]; + hasError: boolean; + error: APIError; handleCreateNewChannels: () => void; } @@ -25,6 +28,9 @@ function ChannelSelect({ onSelectChannels, onDropdownOpen, channels, + isLoading, + hasError, + error, handleCreateNewChannels, }: ChannelSelectProps): JSX.Element | null { // init namespace for translations @@ -40,10 +46,10 @@ function ChannelSelect({ onSelectChannels(value); }; - if (channels.error && channels.errorMessage !== '') { + if (hasError) { notifications.error({ - message: 'Error', - description: channels.errorMessage, + message: error.getErrorCode(), + description: error.getErrorMessage(), }); } @@ -56,7 +62,7 @@ function ChannelSelect({ const renderOptions = (): ReactNode[] => { const children: ReactNode[] = []; - if (!channels.loading && addNewChannelPermission) { + if (!isLoading && addNewChannelPermission) { children.push( @@ -67,15 +73,11 @@ function ChannelSelect({ ); } - if ( - channels.loading || - channels.payload === undefined || - channels.payload.length === 0 - ) { + if (isLoading || channels.length === 0) { return children; } - channels.payload.forEach((o) => { + channels.forEach((o) => { children.push( {o.name} @@ -89,13 +91,13 @@ function ChannelSelect({ return ( } + notFoundContent={isLoading && } onDropdownVisibleChange={(open): void => { if (open) { onDropdownOpen(); diff --git a/frontend/src/container/OrganizationSettings/DisplayName/index.tsx b/frontend/src/container/OrganizationSettings/DisplayName/index.tsx index 8ddcb91eeb..c4726eacd1 100644 --- a/frontend/src/container/OrganizationSettings/DisplayName/index.tsx +++ b/frontend/src/container/OrganizationSettings/DisplayName/index.tsx @@ -25,7 +25,7 @@ function DisplayName({ index, id: orgId }: DisplayNameProps): JSX.Element { displayName, orgId, }); - if (statusCode === 200) { + if (statusCode === 204) { notifications.success({ message: t('success', { ns: 'common', diff --git a/frontend/src/mocks-server/handlers.ts b/frontend/src/mocks-server/handlers.ts index 55b5842612..cde413b2c8 100644 --- a/frontend/src/mocks-server/handlers.ts +++ b/frontend/src/mocks-server/handlers.ts @@ -237,8 +237,8 @@ export const handlers = [ (req, res, ctx) => res(ctx.status(200), ctx.json(traceDetailResponse)), ), - rest.post('http://localhost/api/v1//channels', (_, res, ctx) => - res(ctx.status(200), ctx.json(allAlertChannels)), + rest.get('http://localhost/api/v1/channels', (_, res, ctx) => + res(ctx.status(200), ctx.json({ data: allAlertChannels, status: 'success' })), ), rest.delete('http://localhost/api/v1/channels/:id', (_, res, ctx) => res( diff --git a/frontend/src/pages/ChannelsEdit/index.tsx b/frontend/src/pages/ChannelsEdit/index.tsx index ba421261c8..836a6ca94b 100644 --- a/frontend/src/pages/ChannelsEdit/index.tsx +++ b/frontend/src/pages/ChannelsEdit/index.tsx @@ -13,12 +13,18 @@ import EditAlertChannels from 'container/EditAlertChannels'; import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { useParams } from 'react-router-dom'; +import { SuccessResponseV2 } from 'types/api'; +import { Channels } from 'types/api/channels/getAll'; +import APIError from 'types/api/error'; function ChannelsEdit(): JSX.Element { const { id } = useParams(); const { t } = useTranslation(); - const { isFetching, isError, data } = useQuery(['getChannel', id], { + const { isFetching, isError, data, error } = useQuery< + SuccessResponseV2, + APIError + >(['getChannel', id], { queryFn: () => get({ id, @@ -26,14 +32,18 @@ function ChannelsEdit(): JSX.Element { }); if (isError) { - return {data?.error || t('something_went_wrong')}; + return ( + + {error?.getErrorMessage() || t('something_went_wrong')} + + ); } - if (isFetching || !data?.payload) { + if (isFetching || !data?.data) { return ; } - const { data: ChannelData } = data.payload; + const { data: ChannelData } = data.data; const value = JSON.parse(ChannelData); diff --git a/frontend/src/types/api/channels/get.ts b/frontend/src/types/api/channels/get.ts index d24123072e..5e370fada7 100644 --- a/frontend/src/types/api/channels/get.ts +++ b/frontend/src/types/api/channels/get.ts @@ -4,4 +4,7 @@ export interface Props { id: Channels['id']; } -export type PayloadProps = Channels; +export type PayloadProps = { + data: Channels; + status: string; +}; diff --git a/frontend/src/types/api/channels/getAll.ts b/frontend/src/types/api/channels/getAll.ts index 950dea3f3e..38546c54b7 100644 --- a/frontend/src/types/api/channels/getAll.ts +++ b/frontend/src/types/api/channels/getAll.ts @@ -1,4 +1,7 @@ -export type PayloadProps = Channels[]; +export type PayloadProps = { + data: Channels[]; + status: string; +}; export interface Channels { created_at: string; diff --git a/frontend/src/types/api/error.ts b/frontend/src/types/api/error.ts index 8ad328817e..1e5fa8aa94 100644 --- a/frontend/src/types/api/error.ts +++ b/frontend/src/types/api/error.ts @@ -13,6 +13,14 @@ class APIError extends Error { getHttpStatusCode(): StatusCodes { return this.error.httpStatusCode; } + + getErrorMessage(): string { + return this.error.error.message; + } + + getErrorCode(): string { + return this.error.error.code; + } } export default APIError; diff --git a/frontend/src/types/api/index.ts b/frontend/src/types/api/index.ts index 28512bb8d7..609fea3eaf 100644 --- a/frontend/src/types/api/index.ts +++ b/frontend/src/types/api/index.ts @@ -30,6 +30,11 @@ export interface ErrorV2 { url: string; errors: AdditionalErrors[]; } + +export interface ErrorV2Resp { + error: ErrorV2; +} + export interface ErrorResponseV2 { httpStatusCode: StatusCodes; error: ErrorV2;