Handle @woocommerce/data api error types properly

This commit is contained in:
Chi-Hsuan Huang 2022-05-10 16:55:11 +08:00
parent e179f8d1fe
commit 5e0be91e9c
4 changed files with 73 additions and 38 deletions

View File

@ -16,14 +16,15 @@ import { controls } from '@wordpress/data';
import { STORE_NAME } from './constants';
import { ACTION_TYPES as TYPES } from './action-types';
import { WC_ADMIN_NAMESPACE } from '../constants';
import { WPError } from '../types';
import { isRestApiError } from '../types';
import {
PaypalOnboardingStatus,
PluginNames,
SelectorKeysWithActions,
RecommendedTypes,
InstallPluginsResponse,
ActivatePluginsResponse,
PluginsResponse,
PluginNames,
} from './types';
// Can be removed in WP 5.9, wp.data is supported in >5.7.
@ -38,8 +39,16 @@ class PluginError extends Error {
}
}
type PluginResponseErrors = PluginsResponse< unknown >[ 'errors' ][ 'errors' ];
const isPluginResponseError = (
plugins: Partial< PluginNames >[],
error: unknown
): error is PluginResponseErrors =>
typeof error === 'object' && error !== null && plugins[ 0 ] in error;
const formatErrorMessage = (
pluginErrors: Record< PluginNames, string[] >,
pluginErrors: PluginResponseErrors,
actionType = 'install'
) => {
return sprintf(
@ -92,10 +101,7 @@ export function setIsRequesting(
};
}
export function setError(
selector: SelectorKeysWithActions,
error: Partial< Record< PluginNames, string[] > >
) {
export function setError( selector: SelectorKeysWithActions, error: unknown ) {
return {
type: TYPES.SET_ERROR as const,
selector,
@ -157,8 +163,40 @@ export function setRecommendedPlugins(
};
}
function* handlePluginAPIError(
actionType: string,
plugins: Partial< PluginNames >[],
error: unknown
) {
yield setError( 'installPlugins', error );
let pluginResponseError = error;
if (
( error instanceof Error || isRestApiError( error ) ) &&
plugins[ 0 ]
) {
pluginResponseError = {
[ plugins[ 0 ] ]: [ error.message ],
};
}
if ( isPluginResponseError( plugins, pluginResponseError ) ) {
throw new PluginError(
formatErrorMessage( pluginResponseError, actionType ),
pluginResponseError
);
} else {
throw new PluginError(
`Unexpected Plugin Error: ${ JSON.stringify(
pluginResponseError
) }`,
pluginResponseError
);
}
}
// Action Creator Generators
export function* installPlugins( plugins: string[] ) {
export function* installPlugins( plugins: Partial< PluginNames >[] ) {
yield setIsRequesting( 'installPlugins', true );
try {
@ -171,26 +209,18 @@ export function* installPlugins( plugins: string[] ) {
if ( results.data.installed.length ) {
yield updateInstalledPlugins( results.data.installed );
}
if ( Object.keys( results.errors.errors ).length ) {
throw results.errors.errors;
}
yield setIsRequesting( 'installPlugins', false );
return results;
} catch ( error ) {
if ( error instanceof Error && plugins.length === 1 ) {
// Incase of a network error
error = { [ plugins[ 0 ] ]: error.message };
}
const errors = error as WPError[ 'errors' ];
yield setError( 'installPlugins', errors );
throw new PluginError( formatErrorMessage( errors ), errors );
yield handlePluginAPIError( 'install', plugins, error );
}
}
export function* activatePlugins( plugins: string[] ) {
export function* activatePlugins( plugins: Partial< PluginNames >[] ) {
yield setIsRequesting( 'activatePlugins', true );
try {
@ -212,13 +242,7 @@ export function* activatePlugins( plugins: string[] ) {
return results;
} catch ( error ) {
if ( error instanceof Error && plugins.length === 1 ) {
// Incase of a network error
error = { [ plugins[ 0 ] ]: error.message };
}
const errors = error as WPError[ 'errors' ];
yield setError( 'installPlugins', errors );
throw new PluginError( formatErrorMessage( errors ), errors );
yield handlePluginAPIError( 'activate', plugins, error );
}
}

View File

@ -22,7 +22,6 @@ import {
setRecommendedPlugins,
} from './actions';
import { PaypalOnboardingStatus, RecommendedTypes } from './types';
import { WPError } from '../types';
// Can be removed in WP 5.9, wp.data is supported in >5.7.
const resolveSelect =
@ -52,7 +51,7 @@ export function* getActivePlugins() {
yield updateActivePlugins( results.plugins, true );
} catch ( error ) {
yield setError( 'getActivePlugins', error as WPError[ 'errors' ] );
yield setError( 'getActivePlugins', error );
}
}
@ -68,7 +67,7 @@ export function* getInstalledPlugins() {
yield updateInstalledPlugins( results.plugins, true );
} catch ( error ) {
yield setError( 'getInstalledPlugins', error as WPError[ 'errors' ] );
yield setError( 'getInstalledPlugins', error );
}
}
@ -84,7 +83,7 @@ export function* isJetpackConnected() {
yield updateIsJetpackConnected( results.isActive );
} catch ( error ) {
yield setError( 'isJetpackConnected', error as WPError[ 'errors' ] );
yield setError( 'isJetpackConnected', error );
}
yield setIsRequesting( 'isJetpackConnected', false );
@ -108,7 +107,7 @@ export function* getJetpackConnectUrl( query: { redirect_url: string } ) {
results.connectAction
);
} catch ( error ) {
yield setError( 'getJetpackConnectUrl', error as WPError[ 'errors' ] );
yield setError( 'getJetpackConnectUrl', error );
}
yield setIsRequesting( 'getJetpackConnectUrl', false );
@ -162,10 +161,7 @@ export function* getPaypalOnboardingStatus() {
yield setPaypalOnboardingStatus( results );
} catch ( error ) {
yield setOnboardingStatusWithOptions();
yield setError(
'getPaypalOnboardingStatus',
error as WPError[ 'errors' ]
);
yield setError( 'getPaypalOnboardingStatus', error );
}
}
@ -188,7 +184,7 @@ export function* getRecommendedPlugins( type: RecommendedTypes ) {
yield setRecommendedPlugins( type, results );
} catch ( error ) {
yield setError( 'getRecommendedPlugins', error as WPError[ 'errors' ] );
yield setError( 'getRecommendedPlugins', error );
}
yield setIsRequesting( 'getRecommendedPlugins', false );

View File

@ -63,9 +63,9 @@ export type PaypalOnboardingStatus = {
};
};
type PluginsResponse< PluginData > = {
export type PluginsResponse< PluginData > = {
data: PluginData;
errors: WPError< PluginNames >;
errors: WPError< Partial< PluginNames > >;
success: boolean;
message: string;
} & Response;

View File

@ -1,4 +1,19 @@
export type RestApiError = {
type WPApiFetchError = {
code: string;
message: string;
};
type WPInternalServerError = {
code: string;
message: string;
additional_errors: unknown[];
data: {
status: number;
};
};
export type RestApiError = WPApiFetchError | WPInternalServerError;
export const isRestApiError = ( error: unknown ): error is RestApiError =>
( error as RestApiError ).code !== undefined &&
( error as RestApiError ).message !== undefined;