Refactor errors util to TypeScript, minor typing fixes (https://github.com/woocommerce/woocommerce-blocks/pull/10754)

This commit is contained in:
Sam Seay 2023-08-30 14:56:28 +08:00 committed by GitHub
parent 2d89f4892d
commit 44f48e41e9
13 changed files with 66 additions and 46 deletions

View File

@ -117,6 +117,9 @@ const restrictedImports = [
];
module.exports = {
env: {
browser: true,
},
root: true,
extends: [
'plugin:@woocommerce/eslint-plugin/recommended',

View File

@ -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.

View File

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

View File

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

View File

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

View File

@ -197,7 +197,7 @@ const ProductControl = (
} else if ( showVariations ) {
return renderItemWithVariations;
}
return undefined;
return () => null;
};
if ( error ) {

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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[];