diff --git a/src/frontend/apps/desk/src/api/__tests__/parseAPIError.test.tsx b/src/frontend/apps/desk/src/api/__tests__/parseAPIError.test.tsx new file mode 100644 index 0000000..83bd991 --- /dev/null +++ b/src/frontend/apps/desk/src/api/__tests__/parseAPIError.test.tsx @@ -0,0 +1,174 @@ +import { APIError } from '@/api'; + +import { + parseAPIError, + parseAPIErrorCause, + parseServerAPIError, +} from '../parseAPIError'; + +describe('parseAPIError', () => { + const handleErrorMock = jest.fn(); + const handleServerErrorMock = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should handle specific API error and return no unhandled causes', () => { + const error = new APIError('client error', { + cause: ['Mail domain with this name already exists.'], + status: 400, + }); + + const result = parseAPIError({ + error, + errorParams: { + name: { + causes: ['Mail domain with this name already exists.'], + handleError: handleErrorMock, + }, + }, + serverErrorParams: { + defaultMessage: 'Server error', + handleError: handleServerErrorMock, + }, + }); + + expect(handleErrorMock).toHaveBeenCalled(); + expect(result).toEqual([]); + }); + + it('should return unhandled causes when no match is found', () => { + const error = new APIError('client error', { + cause: ['Unhandled error'], + status: 400, + }); + + const result = parseAPIError({ + error, + errorParams: { + name: { + causes: ['Mail domain with this name already exists.'], + handleError: handleErrorMock, + }, + }, + serverErrorParams: { + defaultMessage: 'Server error', + handleError: handleServerErrorMock, + }, + }); + + expect(handleErrorMock).not.toHaveBeenCalled(); + expect(result).toEqual(['Unhandled error']); + }); + + it('should handle server errors correctly and prepend server error message', () => { + const error = new APIError('server error', { status: 500 }); + + const result = parseAPIError({ + error, + errorParams: undefined, + serverErrorParams: { + defaultMessage: 'Server error occurred', + handleError: handleServerErrorMock, + }, + }); + + expect(handleServerErrorMock).toHaveBeenCalled(); + expect(result).toEqual(['Server error occurred']); + }); + + it('should handle absence of errors gracefully', () => { + const result = parseAPIError({ + error: null, + errorParams: { + name: { + causes: ['Mail domain with this name already exists.'], + handleError: handleErrorMock, + }, + }, + serverErrorParams: { + defaultMessage: 'Server error', + handleError: handleServerErrorMock, + }, + }); + + expect(result).toBeUndefined(); + }); +}); + +describe('parseAPIErrorCause', () => { + it('should handle specific errors and call handleError', () => { + const handleErrorMock = jest.fn(); + const causes = ['Mail domain with this name already exists.']; + + const errorParams = { + name: { + causes: ['Mail domain with this name already exists.'], + handleError: handleErrorMock, + }, + }; + + const result = parseAPIErrorCause({ causes, errorParams }); + + expect(handleErrorMock).toHaveBeenCalled(); + expect(result).toEqual([]); + }); + + it('should handle multiple causes and return unhandled causes', () => { + const handleErrorMock = jest.fn(); + const causes = [ + 'Mail domain with this name already exists.', + 'Unhandled error', + ]; + + const errorParams = { + name: { + causes: ['Mail domain with this name already exists.'], + handleError: handleErrorMock, + }, + }; + + const result = parseAPIErrorCause({ causes, errorParams }); + + expect(handleErrorMock).toHaveBeenCalled(); + expect(result).toEqual(['Unhandled error']); + }); +}); + +describe('parseServerAPIError', () => { + it('should prepend the server error message when there are other causes', () => { + const causes = ['Some other error']; + const serverErrorParams = { + defaultMessage: 'Server error', + }; + + const result = parseServerAPIError({ causes, serverErrorParams }); + + expect(result).toEqual(['Server error', 'Some other error']); + }); + + it('should only return server error message when no other causes exist', () => { + const causes: string[] = []; + const serverErrorParams = { + defaultMessage: 'Server error', + }; + + const result = parseServerAPIError({ causes, serverErrorParams }); + + expect(result).toEqual(['Server error']); + }); + + it('should call handleError when provided as a param', () => { + const handleErrorMock = jest.fn(); + const causes: string[] = []; + const serverErrorParams = { + defaultMessage: 'Server error', + handleError: handleErrorMock, + }; + + parseServerAPIError({ causes, serverErrorParams }); + + expect(handleErrorMock).toHaveBeenCalled(); + }); +}); diff --git a/src/frontend/apps/desk/src/api/parseAPIError.ts b/src/frontend/apps/desk/src/api/parseAPIError.ts new file mode 100644 index 0000000..e9b52bc --- /dev/null +++ b/src/frontend/apps/desk/src/api/parseAPIError.ts @@ -0,0 +1,83 @@ +import { APIError } from '@/api/index'; + +type ErrorParams = { + [fieldName: string]: { + causes: string[]; + causeShown?: string; + handleError: () => void; + }; +}; + +type ServerErrorParams = { + defaultMessage: string; + handleError?: () => void; +}; + +export type parseAPIErrorParams = { + error: APIError | null; + errorParams?: ErrorParams; + serverErrorParams: ServerErrorParams; +}; +export const parseAPIError = ({ + error, + errorParams, + serverErrorParams, +}: parseAPIErrorParams) => { + if (!error || !serverErrorParams?.defaultMessage) { + return; + } + + let causes: string[] = + error.cause?.length && errorParams + ? parseAPIErrorCause({ causes: error.cause, errorParams }) + : []; + + if (error?.status === 500 || !error?.status) { + causes = parseServerAPIError({ causes, serverErrorParams }); + } + + return causes; +}; + +export const parseAPIErrorCause = ({ + causes, + errorParams, +}: { + causes: string[]; + errorParams: ErrorParams; +}): string[] => + causes.reduce((arrayCauses, cause) => { + const foundErrorParams = Object.values(errorParams).find((params) => + params.causes.find((knownCause) => knownCause.match(cause)), + ); + + if (!foundErrorParams) { + arrayCauses.push(cause); + } + + if (foundErrorParams?.causeShown) { + arrayCauses.push(foundErrorParams.causeShown); + } + + if (typeof foundErrorParams?.handleError === 'function') { + foundErrorParams.handleError(); + } + + return arrayCauses; + }, [] as string[]); + +export const parseServerAPIError = ({ + causes, + serverErrorParams, +}: { + causes: string[]; + serverErrorParams: ServerErrorParams; +}): string[] => { + causes.unshift(serverErrorParams.defaultMessage); + + if (typeof serverErrorParams?.handleError === 'function') { + serverErrorParams.handleError(); + } + + return causes; +}; diff --git a/src/frontend/apps/desk/src/features/mail-domains/api/index.tsx b/src/frontend/apps/desk/src/features/mail-domains/api/index.tsx index 2c5993d..61a928c 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/api/index.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/api/index.tsx @@ -2,4 +2,4 @@ export * from './useMailDomains'; export * from './useMailDomain'; export * from './useCreateMailbox'; export * from './useMailboxes'; -export * from './useCreateMailDomain'; +export * from './useAddMailDomain';