From d2132f88e686348114a6423093e02e7bedb2dac3 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 18 Sep 2023 12:58:27 +0200 Subject: [PATCH 1/3] [TS migration] Migrate 'ErrorUtils.js' lib to TypeScript --- src/libs/ErrorUtils.js | 135 ------------------------------------- src/libs/ErrorUtils.ts | 113 +++++++++++++++++++++++++++++++ src/types/onyx/Response.ts | 3 +- 3 files changed, 115 insertions(+), 136 deletions(-) delete mode 100644 src/libs/ErrorUtils.js create mode 100644 src/libs/ErrorUtils.ts diff --git a/src/libs/ErrorUtils.js b/src/libs/ErrorUtils.js deleted file mode 100644 index 95bbad5f5409..000000000000 --- a/src/libs/ErrorUtils.js +++ /dev/null @@ -1,135 +0,0 @@ -import _ from 'underscore'; -import lodashGet from 'lodash/get'; -import CONST from '../CONST'; -import DateUtils from './DateUtils'; -import * as Localize from './Localize'; - -/** - * @param {Object} response - * @param {Number} response.jsonCode - * @param {String} response.message - * @returns {String} - */ -function getAuthenticateErrorMessage(response) { - switch (response.jsonCode) { - case CONST.JSON_CODE.UNABLE_TO_RETRY: - return 'session.offlineMessageRetry'; - case 401: - return 'passwordForm.error.incorrectLoginOrPassword'; - case 402: - // If too few characters are passed as the password, the WAF will pass it to the API as an empty - // string, which results in a 402 error from Auth. - if (response.message === '402 Missing partnerUserSecret') { - return 'passwordForm.error.incorrectLoginOrPassword'; - } - return 'passwordForm.error.twoFactorAuthenticationEnabled'; - case 403: - if (response.message === 'Invalid code') { - return 'passwordForm.error.incorrect2fa'; - } - return 'passwordForm.error.invalidLoginOrPassword'; - case 404: - return 'passwordForm.error.unableToResetPassword'; - case 405: - return 'passwordForm.error.noAccess'; - case 413: - return 'passwordForm.error.accountLocked'; - default: - return 'passwordForm.error.fallback'; - } -} - -/** - * Method used to get an error object with microsecond as the key. - * @param {String} error - error key or message to be saved - * @return {Object} - * - */ -function getMicroSecondOnyxError(error) { - return {[DateUtils.getMicroseconds()]: error}; -} - -/** - * @param {Object} onyxData - * @param {Object} onyxData.errors - * @returns {String} - */ -function getLatestErrorMessage(onyxData) { - if (_.isEmpty(onyxData.errors)) { - return ''; - } - return _.chain(onyxData.errors || []) - .keys() - .sortBy() - .reverse() - .map((key) => onyxData.errors[key]) - .first() - .value(); -} - -/** - * @param {Object} onyxData - * @param {Object} onyxData.errorFields - * @param {String} fieldName - * @returns {Object} - */ -function getLatestErrorField(onyxData, fieldName) { - const errorsForField = lodashGet(onyxData, ['errorFields', fieldName], {}); - - if (_.isEmpty(errorsForField)) { - return {}; - } - return _.chain(errorsForField) - .keys() - .sortBy() - .reverse() - .map((key) => ({[key]: errorsForField[key]})) - .first() - .value(); -} - -/** - * @param {Object} onyxData - * @param {Object} onyxData.errorFields - * @param {String} fieldName - * @returns {Object} - */ -function getEarliestErrorField(onyxData, fieldName) { - const errorsForField = lodashGet(onyxData, ['errorFields', fieldName], {}); - - if (_.isEmpty(errorsForField)) { - return {}; - } - return _.chain(errorsForField) - .keys() - .sortBy() - .map((key) => ({[key]: errorsForField[key]})) - .first() - .value(); -} - -/** - * Method used to generate error message for given inputID - * @param {Object} errors - An object containing current errors in the form - * @param {String} inputID - * @param {String|Array} message - Message to assign to the inputID errors - * - */ -function addErrorMessage(errors, inputID, message) { - if (!message || !inputID) { - return; - } - - const errorList = errors; - const translatedMessage = Localize.translateIfPhraseKey(message); - - if (_.isEmpty(errorList[inputID])) { - errorList[inputID] = [translatedMessage, {isTranslated: true}]; - } else if (_.isString(errorList[inputID])) { - errorList[inputID] = [`${errorList[inputID]}\n${translatedMessage}`, {isTranslated: true}]; - } else { - errorList[inputID][0] = `${errorList[inputID][0]}\n${translatedMessage}`; - } -} - -export {getAuthenticateErrorMessage, getMicroSecondOnyxError, getLatestErrorMessage, getLatestErrorField, getEarliestErrorField, addErrorMessage}; diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts new file mode 100644 index 000000000000..899a319f3c91 --- /dev/null +++ b/src/libs/ErrorUtils.ts @@ -0,0 +1,113 @@ +import CONST from '../CONST'; +import DateUtils from './DateUtils'; +import * as Localize from './Localize'; +import Response from '../types/onyx/Response'; +import {ErrorFields} from '../types/onyx/OnyxCommon'; + +function getAuthenticateErrorMessage(response: Response): string { + switch (response.jsonCode) { + case CONST.JSON_CODE.UNABLE_TO_RETRY: + return 'session.offlineMessageRetry'; + case 401: + return 'passwordForm.error.incorrectLoginOrPassword'; + case 402: + // If too few characters are passed as the password, the WAF will pass it to the API as an empty + // string, which results in a 402 error from Auth. + if (response.message === '402 Missing partnerUserSecret') { + return 'passwordForm.error.incorrectLoginOrPassword'; + } + return 'passwordForm.error.twoFactorAuthenticationEnabled'; + case 403: + if (response.message === 'Invalid code') { + return 'passwordForm.error.incorrect2fa'; + } + return 'passwordForm.error.invalidLoginOrPassword'; + case 404: + return 'passwordForm.error.unableToResetPassword'; + case 405: + return 'passwordForm.error.noAccess'; + case 413: + return 'passwordForm.error.accountLocked'; + default: + return 'passwordForm.error.fallback'; + } +} + +/** + * Method used to get an error object with microsecond as the key. + * @param error - error key or message to be saved + */ +function getMicroSecondOnyxError(error: string): Record { + return {[DateUtils.getMicroseconds()]: error}; +} + +type OnyxDataWithErrors = { + errors: Record; +}; + +function getLatestErrorMessage(onyxData: TOnyxData): string { + if (Object.keys(onyxData.errors).length === 0) { + return ''; + } + + const key = Object.keys(onyxData.errors ?? {}) + .sort() + .reverse()[0]; + + return onyxData.errors[key]; +} + +type OnyxDataWithErrorFields = { + errorFields: ErrorFields; +}; + +function getLatestErrorField(onyxData: TOnyxData, fieldName: string): Record { + const errorsForField = onyxData.errorFields[fieldName] ?? {}; + + if (Object.keys(errorsForField).length === 0) { + return {}; + } + + const key = Object.keys(errorsForField).sort().reverse()[0]; + + return {[key]: errorsForField[key]}; +} + +function getEarliestErrorField(onyxData: TOnyxData, fieldName: string): Record { + const errorsForField = onyxData.errorFields[fieldName] ?? {}; + + if (Object.keys(errorsForField).length === 0) { + return {}; + } + + const key = Object.keys(errorsForField).sort()[0]; + + return {[key]: errorsForField[key]}; +} + +type ErrorsList = Record; + +/** + * Method used to generate error message for given inputID + * @param errorList - An object containing current errors in the form + * @param message - Message to assign to the inputID errors + */ +function addErrorMessage(errors: ErrorsList, inputID?: string, message?: string) { + if (!message || !inputID) { + return; + } + + const errorList = errors; + const error = errorList[inputID]; + const translatedMessage = Localize.translateIfPhraseKey(message); + + if (!error) { + errorList[inputID] = [translatedMessage, {isTranslated: true}]; + } else if (typeof error === 'string') { + errorList[inputID] = [`${error}\n${translatedMessage}`, {isTranslated: true}]; + } else if (Array.isArray(error)) { + error[0] = `${error[0]}\n${translatedMessage}`; + } +} + +export {getAuthenticateErrorMessage, getMicroSecondOnyxError, getLatestErrorMessage, getLatestErrorField, getEarliestErrorField, addErrorMessage}; diff --git a/src/types/onyx/Response.ts b/src/types/onyx/Response.ts index c501034e971c..255ac6d9bae4 100644 --- a/src/types/onyx/Response.ts +++ b/src/types/onyx/Response.ts @@ -3,9 +3,10 @@ import {OnyxUpdate} from 'react-native-onyx'; type Response = { previousUpdateID?: number | string; lastUpdateID?: number | string; - jsonCode?: number; + jsonCode?: number | string; onyxData?: OnyxUpdate[]; requestID?: string; + message?: string; }; export default Response; From ee7aacc91ec292cf57e46cfdf670f74e19f0c3ff Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 19 Sep 2023 21:03:22 +0200 Subject: [PATCH 2/3] Fix tests --- src/libs/ErrorUtils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 899a319f3c91..66dbd923a82d 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -46,15 +46,15 @@ type OnyxDataWithErrors = { }; function getLatestErrorMessage(onyxData: TOnyxData): string { - if (Object.keys(onyxData.errors).length === 0) { + const errors = onyxData.errors ?? {}; + + if (Object.keys(errors).length === 0) { return ''; } - const key = Object.keys(onyxData.errors ?? {}) - .sort() - .reverse()[0]; + const key = Object.keys(errors).sort().reverse()[0]; - return onyxData.errors[key]; + return errors[key]; } type OnyxDataWithErrorFields = { From 982672c487917a9216b74bb3a45e4ef2831367fd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Sep 2023 09:48:11 +0200 Subject: [PATCH 3/3] Adjust code after review --- src/libs/ErrorUtils.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 66dbd923a82d..bf4fc0d810a4 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -2,9 +2,10 @@ import CONST from '../CONST'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; import Response from '../types/onyx/Response'; -import {ErrorFields} from '../types/onyx/OnyxCommon'; +import {ErrorFields, Errors} from '../types/onyx/OnyxCommon'; +import {TranslationFlatObject} from '../languages/types'; -function getAuthenticateErrorMessage(response: Response): string { +function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatObject { switch (response.jsonCode) { case CONST.JSON_CODE.UNABLE_TO_RETRY: return 'session.offlineMessageRetry'; @@ -42,7 +43,7 @@ function getMicroSecondOnyxError(error: string): Record { } type OnyxDataWithErrors = { - errors: Record; + errors?: Errors; }; function getLatestErrorMessage(onyxData: TOnyxData): string { @@ -58,11 +59,11 @@ function getLatestErrorMessage(onyxData: T } type OnyxDataWithErrorFields = { - errorFields: ErrorFields; + errorFields?: ErrorFields; }; function getLatestErrorField(onyxData: TOnyxData, fieldName: string): Record { - const errorsForField = onyxData.errorFields[fieldName] ?? {}; + const errorsForField = onyxData.errorFields?.[fieldName] ?? {}; if (Object.keys(errorsForField).length === 0) { return {}; @@ -74,7 +75,7 @@ function getLatestErrorField(onyxData } function getEarliestErrorField(onyxData: TOnyxData, fieldName: string): Record { - const errorsForField = onyxData.errorFields[fieldName] ?? {}; + const errorsForField = onyxData.errorFields?.[fieldName] ?? {}; if (Object.keys(errorsForField).length === 0) { return {};