From 44f48e41e9c61134ac88e766586a26802cff81e8 Mon Sep 17 00:00:00 2001 From: Sam Seay Date: Wed, 30 Aug 2023 14:56:28 +0800 Subject: [PATCH] Refactor errors util to TypeScript, minor typing fixes (https://github.com/woocommerce/woocommerce-blocks/pull/10754) --- plugins/woocommerce-blocks/.eslintrc.js | 3 ++ .../assets/js/base/hocs/with-reviews.js | 2 +- .../assets/js/base/utils/errors.js | 30 -------------- .../assets/js/base/utils/errors.ts | 41 +++++++++++++++++++ .../assets/js/base/utils/test/errors.js | 18 ++++---- .../product-control/index.tsx | 2 +- .../search-list-control/types.ts | 4 +- .../assets/js/hocs/with-attributes.js | 2 +- .../assets/js/hocs/with-categories.js | 2 +- .../assets/js/hocs/with-category.js | 2 +- .../assets/js/hocs/with-product-variations.js | 2 +- .../assets/js/hocs/with-product.js | 2 +- .../assets/js/hocs/with-searched-products.tsx | 2 +- 13 files changed, 66 insertions(+), 46 deletions(-) delete mode 100644 plugins/woocommerce-blocks/assets/js/base/utils/errors.js create mode 100644 plugins/woocommerce-blocks/assets/js/base/utils/errors.ts diff --git a/plugins/woocommerce-blocks/.eslintrc.js b/plugins/woocommerce-blocks/.eslintrc.js index 9e126556193..992bc75ccca 100644 --- a/plugins/woocommerce-blocks/.eslintrc.js +++ b/plugins/woocommerce-blocks/.eslintrc.js @@ -117,6 +117,9 @@ const restrictedImports = [ ]; module.exports = { + env: { + browser: true, + }, root: true, extends: [ 'plugin:@woocommerce/eslint-plugin/recommended', diff --git a/plugins/woocommerce-blocks/assets/js/base/hocs/with-reviews.js b/plugins/woocommerce-blocks/assets/js/base/hocs/with-reviews.js index 7dd8271bc68..021117caf30 100644 --- a/plugins/woocommerce-blocks/assets/js/base/hocs/with-reviews.js +++ b/plugins/woocommerce-blocks/assets/js/base/hocs/with-reviews.js @@ -9,7 +9,7 @@ import isShallowEqual from '@wordpress/is-shallow-equal'; * Internal dependencies */ import { getReviews } from '../../blocks/reviews/utils'; -import { formatError } from '../utils/errors.js'; +import { formatError } from '../utils/errors'; /** * HOC that queries reviews for a component. diff --git a/plugins/woocommerce-blocks/assets/js/base/utils/errors.js b/plugins/woocommerce-blocks/assets/js/base/utils/errors.js deleted file mode 100644 index a2bbb56b926..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/utils/errors.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Given a JS error or a fetch response error, parse and format it, so it can be displayed to the user. - * - * @param {Object} error Error object. - * @param {Function} [error.json] If a json method is specified, it will try parsing the error first. - * @param {string} [error.message] If a message is specified, it will be shown to the user. - * @param {string} [error.type] The context in which the error was triggered. - * @return {Promise<{message:string;type:string;}>} Error object containing a message and type. - */ -export const formatError = async ( error ) => { - if ( typeof error.json === 'function' ) { - try { - const parsedError = await error.json(); - return { - message: parsedError.message, - type: parsedError.type || 'api', - }; - } catch ( e ) { - return { - message: e.message, - type: 'general', - }; - } - } - - return { - message: error.message, - type: error.type || 'general', - }; -}; diff --git a/plugins/woocommerce-blocks/assets/js/base/utils/errors.ts b/plugins/woocommerce-blocks/assets/js/base/utils/errors.ts new file mode 100644 index 00000000000..1879f3d4047 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/utils/errors.ts @@ -0,0 +1,41 @@ +export interface ErrorObject { + /** + * Human-readable error message to display. + */ + message: string; + /** + * Context in which the error was triggered. That will determine how the error is displayed to the user. + */ + type: 'api' | 'general' | string; +} + +type SimpleError = { + message: string; + type?: string; +}; + +export const formatError = async ( + error: SimpleError | Response +): Promise< ErrorObject > => { + if ( 'json' in error ) { + try { + const parsedError = await error.json(); + return { + message: parsedError.message, + type: parsedError.type || 'api', + }; + } catch ( e ) { + return { + // We could only return this if e is instanceof Error but, to avoid changing runtime + // behaviour, we'll just cast it instead. + message: ( e as Error ).message, + type: 'general', + }; + } + } else { + return { + message: error.message, + type: error.type || 'general', + }; + } +}; diff --git a/plugins/woocommerce-blocks/assets/js/base/utils/test/errors.js b/plugins/woocommerce-blocks/assets/js/base/utils/test/errors.js index f9e56a7f96f..50b5bd6a346 100644 --- a/plugins/woocommerce-blocks/assets/js/base/utils/test/errors.js +++ b/plugins/woocommerce-blocks/assets/js/base/utils/test/errors.js @@ -4,6 +4,9 @@ import { formatError } from '../errors'; describe( 'formatError', () => { + const mockResponseBody = JSON.stringify( { message: 'Lorem Ipsum' } ); + const mockMalformedJson = '{ "message": "Lorem Ipsum"'; + test( 'should format general errors', async () => { const error = await formatError( { message: 'Lorem Ipsum', @@ -17,9 +20,9 @@ describe( 'formatError', () => { } ); test( 'should format API errors', async () => { - const error = await formatError( { - json: () => Promise.resolve( { message: 'Lorem Ipsum' } ), - } ); + const mockResponse = new Response( mockResponseBody, { status: 400 } ); + + const error = await formatError( mockResponse ); const expectedError = { message: 'Lorem Ipsum', type: 'api', @@ -29,11 +32,12 @@ describe( 'formatError', () => { } ); test( 'should format JSON parse errors', async () => { - const error = await formatError( { - json: () => Promise.reject( { message: 'Lorem Ipsum' } ), - } ); + const mockResponse = new Response( mockMalformedJson, { status: 400 } ); + + const error = await formatError( mockResponse ); const expectedError = { - message: 'Lorem Ipsum', + message: + 'invalid json response body at reason: Unexpected end of JSON input', type: 'general', }; diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx b/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx index a6dc1c3e75c..405217b27ec 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx @@ -197,7 +197,7 @@ const ProductControl = ( } else if ( showVariations ) { return renderItemWithVariations; } - return undefined; + return () => null; }; if ( error ) { diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/types.ts b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/types.ts index 3362fc8c97f..38d54285555 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/types.ts +++ b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/types.ts @@ -124,7 +124,9 @@ export interface SearchListControlProps< T extends object = object > { // Callback fired when the search field is used. onSearch?: ( ( search: string ) => void ) | undefined; // Callback to render each item in the selection list, allows any custom object-type rendering. - renderItem?: ( ( args: renderItemArgs< T > ) => JSX.Element ) | undefined; + renderItem?: + | ( ( args: renderItemArgs< T > ) => JSX.Element | null ) + | undefined; // The list of currently selected items. selected: SearchListItem< T >[]; // Whether to show a text field or a token field as search diff --git a/plugins/woocommerce-blocks/assets/js/hocs/with-attributes.js b/plugins/woocommerce-blocks/assets/js/hocs/with-attributes.js index b39aa7bd2b2..0cb8698fbcf 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/with-attributes.js +++ b/plugins/woocommerce-blocks/assets/js/hocs/with-attributes.js @@ -7,7 +7,7 @@ import { getAttributes, getTerms } from '@woocommerce/editor-components/utils'; /** * Internal dependencies */ -import { formatError } from '../base/utils/errors.js'; +import { formatError } from '../base/utils/errors'; /** * Get attribute data (name, taxonomy etc) from server data. diff --git a/plugins/woocommerce-blocks/assets/js/hocs/with-categories.js b/plugins/woocommerce-blocks/assets/js/hocs/with-categories.js index 39684c64cb6..590e79bfb2f 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/with-categories.js +++ b/plugins/woocommerce-blocks/assets/js/hocs/with-categories.js @@ -8,7 +8,7 @@ import { getCategories } from '@woocommerce/editor-components/utils'; /** * Internal dependencies */ -import { formatError } from '../base/utils/errors.js'; +import { formatError } from '../base/utils/errors'; /** * HOC that queries categories for a component. diff --git a/plugins/woocommerce-blocks/assets/js/hocs/with-category.js b/plugins/woocommerce-blocks/assets/js/hocs/with-category.js index baff961b57f..58d7f50d08c 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/with-category.js +++ b/plugins/woocommerce-blocks/assets/js/hocs/with-category.js @@ -8,7 +8,7 @@ import { getCategory } from '@woocommerce/editor-components/utils'; /** * Internal dependencies */ -import { formatError } from '../base/utils/errors.js'; +import { formatError } from '../base/utils/errors'; /** * HOC that queries a category for a component. diff --git a/plugins/woocommerce-blocks/assets/js/hocs/with-product-variations.js b/plugins/woocommerce-blocks/assets/js/hocs/with-product-variations.js index d8c1acb4d3f..422aa6a67c6 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/with-product-variations.js +++ b/plugins/woocommerce-blocks/assets/js/hocs/with-product-variations.js @@ -10,7 +10,7 @@ import { getProductVariations } from '@woocommerce/editor-components/utils'; /** * Internal dependencies */ -import { formatError } from '../base/utils/errors.js'; +import { formatError } from '../base/utils/errors'; /** * HOC that queries variations for a component. diff --git a/plugins/woocommerce-blocks/assets/js/hocs/with-product.js b/plugins/woocommerce-blocks/assets/js/hocs/with-product.js index 4586c51629f..358432ac1a2 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/with-product.js +++ b/plugins/woocommerce-blocks/assets/js/hocs/with-product.js @@ -8,7 +8,7 @@ import { getProduct } from '@woocommerce/editor-components/utils'; /** * Internal dependencies */ -import { formatError } from '../base/utils/errors.js'; +import { formatError } from '../base/utils/errors'; /** * HOC that queries a product for a component. diff --git a/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx b/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx index cb93a4997e9..fc04576347b 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx +++ b/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx @@ -13,7 +13,7 @@ import type { /** * Internal dependencies */ -import { formatError } from '../base/utils/errors.js'; +import { formatError } from '../base/utils/errors'; interface WithSearchedProductProps { selected: number[];