Add tracks for plugin installation and activation (#37261)

* Add @woocommerce/tracks to @woocommerce/data dependencies

* Add tracks for plugin actions and handle plugin error properly

* Add changelog

* Add doc

* Update doc
This commit is contained in:
Chi-Hsuan Huang 2023-03-20 10:38:30 +08:00 committed by GitHub
parent c5564a15c1
commit 7b10baff00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 30 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: enhancement
Add tracks for plugin actions and handle plugin error properly

View File

@ -28,6 +28,7 @@
"dependencies": { "dependencies": {
"@woocommerce/date": "workspace:*", "@woocommerce/date": "workspace:*",
"@woocommerce/navigation": "workspace:*", "@woocommerce/navigation": "workspace:*",
"@woocommerce/tracks": "workspace:*",
"@wordpress/api-fetch": "wp-6.0", "@wordpress/api-fetch": "wp-6.0",
"@wordpress/compose": "wp-6.0", "@wordpress/compose": "wp-6.0",
"@wordpress/core-data": "wp-6.0", "@wordpress/core-data": "wp-6.0",

View File

@ -9,6 +9,7 @@ import {
import { _n, sprintf } from '@wordpress/i18n'; import { _n, sprintf } from '@wordpress/i18n';
import { DispatchFromMap } from '@automattic/data-stores'; import { DispatchFromMap } from '@automattic/data-stores';
import { controls } from '@wordpress/data'; import { controls } from '@wordpress/data';
import { recordEvent } from '@woocommerce/tracks';
/** /**
* Internal dependencies * Internal dependencies
@ -49,21 +50,22 @@ const isPluginResponseError = (
typeof error === 'object' && error !== null && plugins[ 0 ] in error; typeof error === 'object' && error !== null && plugins[ 0 ] in error;
const formatErrorMessage = ( const formatErrorMessage = (
pluginErrors: PluginResponseErrors, actionType: 'install' | 'activate' = 'install',
actionType = 'install' plugins: Partial< PluginNames >[],
rawErrorMessage: string
) => { ) => {
return sprintf( return sprintf(
/* translators: %(actionType): install or activate (the plugin). %(pluginName): a plugin slug (e.g. woocommerce-services). %(error): a single error message or in plural a comma separated error message list.*/ /* translators: %(actionType): install or activate (the plugin). %(pluginName): a plugin slug (e.g. woocommerce-services). %(error): a single error message or in plural a comma separated error message list.*/
_n( _n(
'Could not %(actionType)s %(pluginName)s plugin, %(error)s', 'Could not %(actionType)s %(pluginName)s plugin, %(error)s',
'Could not %(actionType)s the following plugins: %(pluginName)s with these Errors: %(error)s', 'Could not %(actionType)s the following plugins: %(pluginName)s with these Errors: %(error)s',
Object.keys( pluginErrors ).length || 1, Object.keys( plugins ).length || 1,
'woocommerce' 'woocommerce'
), ),
{ {
actionType, actionType,
pluginName: Object.keys( pluginErrors ).join( ', ' ), pluginName: plugins.join( ', ' ),
error: Object.values( pluginErrors ).join( ', \n' ), error: rawErrorMessage,
} }
); );
}; };
@ -174,35 +176,42 @@ export function setRecommendedPlugins(
} }
function* handlePluginAPIError( function* handlePluginAPIError(
actionType: string, actionType: 'install' | 'activate',
plugins: Partial< PluginNames >[], plugins: Partial< PluginNames >[],
error: unknown error: unknown
) { ) {
yield setError( 'installPlugins', error ); let rawErrorMessage;
let pluginResponseError = error; if ( isPluginResponseError( plugins, error ) ) {
if ( // Backend error messages are in the form of { plugin-slug: [ error messages ] }.
( error instanceof Error || isRestApiError( error ) ) && rawErrorMessage = Object.values( error ).join( ', \n' );
plugins[ 0 ]
) {
pluginResponseError = {
[ plugins[ 0 ] ]: [ error.message ],
};
}
if ( isPluginResponseError( plugins, pluginResponseError ) ) {
throw new PluginError(
formatErrorMessage( pluginResponseError, actionType ),
pluginResponseError
);
} else { } else {
throw new PluginError( // Other error such as API connection errors.
`Unexpected Plugin Error: ${ JSON.stringify( rawErrorMessage =
pluginResponseError isRestApiError( error ) || error instanceof Error
) }`, ? error.message
pluginResponseError : JSON.stringify( error );
);
} }
// Track the error.
switch ( actionType ) {
case 'install':
recordEvent( 'install_plugins_error', {
plugins: plugins.join( ', ' ),
message: rawErrorMessage,
} );
break;
case 'activate':
recordEvent( 'activate_plugins_error', {
plugins: plugins.join( ', ' ),
message: rawErrorMessage,
} );
}
throw new PluginError(
formatErrorMessage( actionType, plugins, rawErrorMessage ),
error
);
} }
// Action Creator Generators // Action Creator Generators
@ -225,6 +234,7 @@ export function* installPlugins( plugins: Partial< PluginNames >[] ) {
return results; return results;
} catch ( error ) { } catch ( error ) {
yield setError( 'installPlugins', error );
yield handlePluginAPIError( 'install', plugins, error ); yield handlePluginAPIError( 'install', plugins, error );
} finally { } finally {
yield setIsRequesting( 'installPlugins', false ); yield setIsRequesting( 'installPlugins', false );
@ -251,6 +261,7 @@ export function* activatePlugins( plugins: Partial< PluginNames >[] ) {
return results; return results;
} catch ( error ) { } catch ( error ) {
yield setError( 'activatePlugins', error );
yield handlePluginAPIError( 'activate', plugins, error ); yield handlePluginAPIError( 'activate', plugins, error );
} finally { } finally {
yield setIsRequesting( 'activatePlugins', false ); yield setIsRequesting( 'activatePlugins', false );
@ -305,7 +316,7 @@ export function* connectToJetpack(
} }
export function* installJetpackAndConnect( export function* installJetpackAndConnect(
errorAction: ( errorMesage: string ) => void, errorAction: ( errorMessage: string ) => void,
getAdminLink: ( endpoint: string ) => string getAdminLink: ( endpoint: string ) => string
) { ) {
try { try {
@ -329,7 +340,7 @@ export function* installJetpackAndConnect(
export function* connectToJetpackWithFailureRedirect( export function* connectToJetpackWithFailureRedirect(
failureRedirect: string, failureRedirect: string,
errorAction: ( errorMesage: string ) => void, errorAction: ( errorMessage: string ) => void,
getAdminLink: ( endpoint: string ) => string getAdminLink: ( endpoint: string ) => string
) { ) {
try { try {

View File

@ -608,6 +608,7 @@ importers:
'@woocommerce/date': workspace:* '@woocommerce/date': workspace:*
'@woocommerce/eslint-plugin': workspace:* '@woocommerce/eslint-plugin': workspace:*
'@woocommerce/navigation': workspace:* '@woocommerce/navigation': workspace:*
'@woocommerce/tracks': workspace:*
'@wordpress/api-fetch': wp-6.0 '@wordpress/api-fetch': wp-6.0
'@wordpress/compose': wp-6.0 '@wordpress/compose': wp-6.0
'@wordpress/core-data': wp-6.0 '@wordpress/core-data': wp-6.0
@ -635,6 +636,7 @@ importers:
dependencies: dependencies:
'@woocommerce/date': link:../date '@woocommerce/date': link:../date
'@woocommerce/navigation': link:../navigation '@woocommerce/navigation': link:../navigation
'@woocommerce/tracks': link:../tracks
'@wordpress/api-fetch': 6.3.1 '@wordpress/api-fetch': 6.3.1
'@wordpress/compose': 5.4.1_react@17.0.2 '@wordpress/compose': 5.4.1_react@17.0.2
'@wordpress/core-data': 4.4.5_react@17.0.2 '@wordpress/core-data': 4.4.5_react@17.0.2