🥅(frontend) handle api errors

- add hook to handle api errors.
- add related component tests
This commit is contained in:
daproclaima
2024-09-04 22:03:43 +02:00
committed by Sebastien Nobour
parent b5d8e92d1e
commit 25898bbb64
3 changed files with 258 additions and 1 deletions

View File

@@ -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();
});
});

View File

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

View File

@@ -2,4 +2,4 @@ export * from './useMailDomains';
export * from './useMailDomain';
export * from './useCreateMailbox';
export * from './useMailboxes';
export * from './useCreateMailDomain';
export * from './useAddMailDomain';