Skip to content

Commit

Permalink
errors on SF can now have different verbosity (#2673)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebaholesz authored Aug 1, 2023
2 parents 31071e4 + f2cbb17 commit da7f7e8
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 94 deletions.
80 changes: 52 additions & 28 deletions project-base/storefront/helpers/errors/applicationErrors.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@
export const ApplicationErrors = {
default: 'default',
'cart-not-found': 'cart-not-found',
'max-allowed-limit': 'max-allowed-limit',
'packetery-address-id-invalid': 'packetery-address-id-invalid',
'invalid-credentials': 'invalid-credentials',
'invalid-refresh-token': 'invalid-refresh-token',
'order-emails-not-sent': 'order-emails-not-sent',
'order-empty-cart': 'order-empty-cart',
'personal-data-request-type-invalid': 'personal-data-request-type-invalid',
'blog-category-not-found': 'blog-category-not-found',
'image-type-invalid': 'image-type-invalid',
'image-size-invalid': 'image-size-invalid',
'order-not-found': 'order-not-found',
'personal-data-hash-invalid': 'personal-data-hash-invalid',
'product-price-missing': 'product-price-missing',
'no-result-found-for-slug': 'no-result-found-for-slug',
'store-not-found': 'store-not-found',
'invalid-token': 'invalid-token',
'product-not-found': 'product-not-found',
'handling-with-logged-customer-comparison': 'handling-with-logged-customer-comparison',
'comparison-not-found': 'comparison-not-found',
'compared-item-not-found': 'compared-item-not-found',
'compared-item-already-exists': 'compared-item-already-exists',
'wishlist-not-found': 'wishlist-not-found',
'wishlist-item-already-exists': 'wishlist-item-already-exists',
'wishlist-item-not-found': 'wishlist-item-not-found',
'seo-page-not-found': 'seo-page-not-found',
export type ApplicationErrorVerbosityLevel = 'flash-message' | 'no-flash-message' | 'no-log';

const ApplicationErrors = {
default: 'flash-message',
'cart-not-found': 'flash-message',
'max-allowed-limit': 'flash-message',
'packetery-address-id-invalid': 'flash-message',
'invalid-credentials': 'flash-message',
'invalid-refresh-token': 'flash-message',
'order-emails-not-sent': 'flash-message',
'order-empty-cart': 'flash-message',
'personal-data-request-type-invalid': 'flash-message',
'blog-category-not-found': 'flash-message',
'image-type-invalid': 'flash-message',
'image-size-invalid': 'flash-message',
'order-not-found': 'flash-message',
'personal-data-hash-invalid': 'flash-message',
'product-price-missing': 'flash-message',
'no-result-found-for-slug': 'no-flash-message',
'store-not-found': 'flash-message',
'invalid-token': 'no-flash-message',
'product-not-found': 'flash-message',
'handling-with-logged-customer-comparison': 'flash-message',
'comparison-not-found': 'flash-message',
'compared-item-not-found': 'flash-message',
'compared-item-already-exists': 'flash-message',
'seo-page-not-found': 'no-log',
'wishlist-not-found': 'flash-message',
'wishlist-item-already-exists': 'flash-message',
'wishlist-item-not-found': 'flash-message',
} as const;

type KeysMatching<T, V extends ApplicationErrorVerbosityLevel> = {
[K in keyof T]: T[K] extends V ? K : never;
}[keyof T];

export type FlashMessageKeys = KeysMatching<typeof ApplicationErrors, 'flash-message'>;

export type NoFlashMessageKeys = KeysMatching<typeof ApplicationErrors, 'no-flash-message'>;

export type NoLogKeys = KeysMatching<typeof ApplicationErrors, 'no-log'>;

export type ApplicationErrorsType = keyof typeof ApplicationErrors;

export const isFlashMessageError = (errorCode: string): errorCode is FlashMessageKeys =>
Object.keys(ApplicationErrors).includes(errorCode) &&
ApplicationErrors[errorCode as keyof typeof ApplicationErrors] === 'flash-message';

export const isNoFlashMessageError = (errorCode: string): errorCode is NoFlashMessageKeys =>
Object.keys(ApplicationErrors).includes(errorCode) &&
ApplicationErrors[errorCode as keyof typeof ApplicationErrors] === 'no-flash-message';

export const isNoLogError = (errorCode: string): errorCode is NoLogKeys =>
Object.keys(ApplicationErrors).includes(errorCode) &&
ApplicationErrors[errorCode as keyof typeof ApplicationErrors] === 'no-log';
74 changes: 29 additions & 45 deletions project-base/storefront/helpers/errors/errorMessageMapper.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,39 @@
import { ApplicationErrors, ApplicationErrorsType } from './applicationErrors';
import { FlashMessageKeys } from './applicationErrors';
import { Translate } from 'next-translate';
import { ApplicationIgnoredErrors } from './ignoredErrors';

type ApplicationErrorsWithoutIgnoredErrorsType = Exclude<
ApplicationErrorsType,
(typeof ApplicationIgnoredErrors)[number]
>;

const getErrorMessageTranslationString = (
errorCode: ApplicationErrorsWithoutIgnoredErrorsType,
t: Translate,
): string | undefined => {
const ERROR_MESSAGES: Record<ApplicationErrorsWithoutIgnoredErrorsType, string | undefined> = {
[ApplicationErrors.default]: t('Unknown error.'),
[ApplicationErrors['cart-not-found']]: t('Cart not found.'),
[ApplicationErrors['max-allowed-limit']]: t('Max allowed limit reached.'),
[ApplicationErrors['packetery-address-id-invalid']]: t('Invalid Packetery address id.'),
[ApplicationErrors['invalid-credentials']]: t('Invalid credentials.'),
[ApplicationErrors['invalid-refresh-token']]: t('Invalid refresh token.'),
[ApplicationErrors['order-emails-not-sent']]: t('Automatic order emails was not sent.'),
[ApplicationErrors['order-empty-cart']]: t('Cart is empty.'),
[ApplicationErrors['personal-data-request-type-invalid']]: t('Invalid request type.'),
[ApplicationErrors['blog-category-not-found']]: t('Category not found.'),
[ApplicationErrors['image-type-invalid']]: t('Invalid image type.'),
[ApplicationErrors['image-size-invalid']]: t('Invalid image size.'),
[ApplicationErrors['order-not-found']]: t('Order not found.'),
[ApplicationErrors['personal-data-hash-invalid']]: t('Invalid hash.'),
[ApplicationErrors['product-price-missing']]: t('Product price is missing.'),
[ApplicationErrors['store-not-found']]: t('Store not found.'),
[ApplicationErrors['product-not-found']]: t('Product not found.'),
[ApplicationErrors['handling-with-logged-customer-comparison']]: t('Product not found.'),
[ApplicationErrors['comparison-not-found']]: t('Comparison not found.'),
[ApplicationErrors['compared-item-not-found']]: t('Compared product not found.'),
[ApplicationErrors['compared-item-already-exists']]: t('Compared product is already compared.'),
[ApplicationErrors['wishlist-not-found']]: t('Wishlist not found.'),
[ApplicationErrors['wishlist-item-already-exists']]: t('Product in wishlist already exists.'),
[ApplicationErrors['wishlist-item-not-found']]: t('Product in wishlist not found.'),
const getErrorMessageTranslationString = (errorCode: FlashMessageKeys, t: Translate): string | undefined => {
const ERROR_MESSAGES: Record<FlashMessageKeys, string> = {
default: t('Unknown error.'),
'cart-not-found': t('Cart not found.'),
'max-allowed-limit': t('Max allowed limit reached.'),
'packetery-address-id-invalid': t('Invalid Packetery address id.'),
'invalid-credentials': t('Invalid credentials.'),
'invalid-refresh-token': t('Invalid refresh token.'),
'order-emails-not-sent': t('Automatic order emails was not sent.'),
'order-empty-cart': t('Cart is empty.'),
'personal-data-request-type-invalid': t('Invalid request type.'),
'blog-category-not-found': t('Category not found.'),
'image-type-invalid': t('Invalid image type.'),
'image-size-invalid': t('Invalid image size.'),
'order-not-found': t('Order not found.'),
'personal-data-hash-invalid': t('Invalid hash.'),
'product-price-missing': t('Product price is missing.'),
'store-not-found': t('Store not found.'),
'product-not-found': t('Product not found.'),
'handling-with-logged-customer-comparison': t('Product not found.'),
'comparison-not-found': t('Comparison not found.'),
'compared-item-not-found': t('Compared product not found.'),
'compared-item-already-exists': t('Compared product is already compared.'),
'wishlist-not-found': t('Wishlist not found.'),
'wishlist-item-already-exists': t('Product in wishlist already exists.'),
'wishlist-item-not-found': t('Product in wishlist not found.'),
};

return ERROR_MESSAGES[errorCode];
};

export const hasErrorMessage = (errorCode: string, t: Translate): boolean => {
return getErrorMessageTranslationString(errorCode as ApplicationErrorsWithoutIgnoredErrorsType, t) !== undefined;
};

export const getErrorMessage = (errorCode: string, t: Translate): string => {
const translationString = getErrorMessageTranslationString(
errorCode as ApplicationErrorsWithoutIgnoredErrorsType,
t,
);
export const getErrorMessage = (errorCode: FlashMessageKeys, t: Translate): string => {
const translationString = getErrorMessageTranslationString(errorCode, t);

return translationString !== undefined ? t(translationString) : t('Unknown error.');
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ApplicationErrors, ApplicationErrorsType } from 'helpers/errors/applicationErrors';
import { getErrorMessage, hasErrorMessage } from 'helpers/errors/errorMessageMapper';
import { ApplicationIgnoredErrors } from 'helpers/errors/ignoredErrors';
import {
ApplicationErrorsType,
isFlashMessageError,
isNoFlashMessageError,
isNoLogError,
} from 'helpers/errors/applicationErrors';
import { getErrorMessage } from 'helpers/errors/errorMessageMapper';
import { Translate } from 'next-translate';
import { ParsedErrors, ValidationErrors } from 'types/error';
import { CombinedError } from 'urql';
Expand Down Expand Up @@ -34,11 +38,15 @@ export const getUserFriendlyErrors = (originalError: CombinedError, t: Translate

if ('userCode' in error.extensions) {
const errorExtensions = error.extensions as { userCode: ApplicationErrorsType };
if (ApplicationIgnoredErrors.some((ignoredError) => ignoredError === errorExtensions.userCode)) {
if (isNoLogError(errorExtensions.userCode) || isNoFlashMessageError(errorExtensions.userCode)) {
errors.applicationError = {
type: errorExtensions.userCode,
message: error.message,
};
continue;
}

if (hasErrorMessage(errorExtensions.userCode, t)) {
if (isFlashMessageError(errorExtensions.userCode)) {
errors.applicationError = {
type: errorExtensions.userCode,
message: getErrorMessage(errorExtensions.userCode, t),
Expand All @@ -47,10 +55,10 @@ export const getUserFriendlyErrors = (originalError: CombinedError, t: Translate
}
}

errors.applicationError = { type: ApplicationErrors.default, message: t('Unknown error.') };
errors.applicationError = { type: 'default', message: t('Unknown error.') };
}
} else {
errors.applicationError = { type: ApplicationErrors.default, message: t('Unknown error.') };
errors.applicationError = { type: 'default', message: t('Unknown error.') };
}

return errors;
Expand Down
8 changes: 0 additions & 8 deletions project-base/storefront/helpers/errors/ignoredErrors.ts

This file was deleted.

24 changes: 18 additions & 6 deletions project-base/storefront/urql/errorExchange.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { showErrorMessage } from 'components/Helpers/toasts';
import { CartQueryDocumentApi } from 'graphql/generated';
import { removeTokensFromCookies } from 'helpers/auth/tokens';
import { ApplicationErrors } from 'helpers/errors/applicationErrors';
import { isFlashMessageError, isNoLogError } from 'helpers/errors/applicationErrors';
import { getUserFriendlyErrors } from 'helpers/errors/friendlyErrorMessageParser';
import { logException } from 'helpers/errors/logException';
import { GetServerSidePropsContext, NextPageContext } from 'next';
Expand All @@ -26,23 +26,35 @@ export const getErrorExchange =
const isAuthError = error.response?.status === 401;
if (isAuthError) {
removeTokensFromCookies(context);

return;
}

const parsedErrors = t ? getUserFriendlyErrors(error, t) : undefined;
logException(error);

if (!parsedErrors) {
return;
}

if (parsedErrors.userError) {
logException(parsedErrors.userError);
}

const isCartError = operation.query === CartQueryDocumentApi;
if (isCartError) {
handleCartError(parsedErrors);

return;
}

if (parsedErrors.applicationError) {
if (!parsedErrors.applicationError) {
return;
}

if (!isNoLogError(parsedErrors.applicationError.type)) {
logException(parsedErrors.applicationError);
}

if (isFlashMessageError(parsedErrors.applicationError.type)) {
showErrorMessage(parsedErrors.applicationError.message);
}
}),
Expand All @@ -52,9 +64,9 @@ export const getErrorExchange =

const handleCartError = ({ userError, applicationError }: ParsedErrors) => {
switch (applicationError?.type) {
case ApplicationErrors['cart-not-found']:
case 'cart-not-found':
break;
case ApplicationErrors.default:
case 'default':
showErrorMessage(applicationError.message, GtmMessageOriginType.cart);
break;
}
Expand Down

0 comments on commit da7f7e8

Please sign in to comment.