Skip to content

Commit

Permalink
Merge pull request #6322 from google/enhancement/4997-internal-errors
Browse files Browse the repository at this point in the history
Add ReportErrorActions component
  • Loading branch information
tofumatt authored Dec 15, 2022
2 parents 237e55d + e867f1d commit 5c2a7ac
Show file tree
Hide file tree
Showing 11 changed files with 492 additions and 341 deletions.
80 changes: 6 additions & 74 deletions assets/js/components/ReportError.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,66 +25,34 @@ import uniqWith from 'lodash/uniqWith';
/**
* WordPress dependencies
*/
import { Fragment, useCallback } from '@wordpress/element';
import { Fragment } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import Data from 'googlesitekit-data';
import { Button } from 'googlesitekit-components';
import { CORE_MODULES } from '../googlesitekit/modules/datastore/constants';
import {
isErrorRetryable,
isInsufficientPermissionsError,
getReportErrorMessage,
} from '../util/errors';
import { getInsufficientPermissionsErrorDescription } from '../util/insufficient-permissions-error-description';
import { purify } from '../util/purify';
import ErrorText from '../components/ErrorText';
import CTA from './notifications/CTA';
import Link from './Link';
import { CORE_SITE } from '../googlesitekit/datastore/site/constants';
import ReportErrorActions from './ReportErrorActions';
import useViewOnly from '../hooks/useViewOnly';
const { useSelect, useDispatch } = Data;
const { useSelect } = Data;

export default function ReportError( { moduleSlug, error } ) {
const isViewOnly = useViewOnly();
const module = useSelect( ( select ) =>
select( CORE_MODULES ).getModule( moduleSlug )
);
const storeName = useSelect( ( select ) =>
select( CORE_MODULES ).getModuleStoreName( moduleSlug )
);
const requestAccessURL = useSelect( ( select ) =>
typeof select( storeName )?.getServiceEntityAccessURL === 'function'
? select( storeName ).getServiceEntityAccessURL()
: null
);

const errors = Array.isArray( error ) ? error : [ error ];

const retryableErrors = errors.filter(
( err ) =>
isErrorRetryable( err, err.selectorData ) &&
err.selectorData.name === 'getReport'
);

const showRetry = !! retryableErrors.length && ! isViewOnly;

const errorTroubleshootingLinkURL = useSelect( ( select ) => {
const err = {
...( showRetry ? retryableErrors[ 0 ] : errors[ 0 ] ),
};

if ( isInsufficientPermissionsError( err ) ) {
err.code = `${ moduleSlug }_insufficient_permissions`;
}

return select( CORE_SITE ).getErrorTroubleshootingLinkURL( err );
} );

const dispatch = useDispatch();

let title;

const getMessage = ( err ) => {
Expand Down Expand Up @@ -116,7 +84,7 @@ export default function ReportError( { moduleSlug, error } ) {
);
}

return err.message;
return getReportErrorMessage( err );
};

const uniqueErrors = uniqWith(
Expand Down Expand Up @@ -167,45 +135,9 @@ export default function ReportError( { moduleSlug, error } ) {
</Fragment>
);

const handleRetry = useCallback( () => {
retryableErrors.forEach( ( err ) => {
const { selectorData } = err;
dispatch( selectorData.storeName ).invalidateResolution(
selectorData.name,
selectorData.args
);
} );
}, [ dispatch, retryableErrors ] );

const showRequestAccessURL =
requestAccessURL && hasInsufficientPermissionsError && ! isViewOnly;

return (
<CTA title={ title } description={ description } error>
<div className="googlesitekit-error-cta-wrapper">
{ showRequestAccessURL && (
<Button href={ requestAccessURL } target="_blank">
{ __( 'Request access', 'google-site-kit' ) }
</Button>
) }
{ showRetry ? (
<Fragment>
<Button onClick={ handleRetry }>
{ __( 'Retry', 'google-site-kit' ) }
</Button>
<span className="googlesitekit-error-retry-text">
{ __( 'Retry didn’t work?', 'google-site-kit' ) }{ ' ' }
</span>
<Link href={ errorTroubleshootingLinkURL } external>
{ __( 'Get help', 'google-site-kit' ) }
</Link>
</Fragment>
) : (
<Link href={ errorTroubleshootingLinkURL } external>
{ __( 'Get help', 'google-site-kit' ) }
</Link>
) }
</div>
<ReportErrorActions moduleSlug={ moduleSlug } error={ error } />
</CTA>
);
}
Expand Down
22 changes: 22 additions & 0 deletions assets/js/components/ReportError.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,28 @@ MultipleUniqueReportErrorsWithRetryButtonWith.args = {
],
};

export const ReportErrorWithCustomInternalServerErrorMessage = Template.bind(
{}
);
ReportErrorWithCustomInternalServerErrorMessage.storyName =
'ReportError with custom Internal Server Error message';
ReportErrorWithCustomInternalServerErrorMessage.args = {
error: {
code: 'internal_server_error',
message: 'Test error message',
},
};

export const ReportErrorWithCustomInvalidJSONMessage = Template.bind( {} );
ReportErrorWithCustomInvalidJSONMessage.storyName =
'ReportError with custom Invalid JSON message';
ReportErrorWithCustomInvalidJSONMessage.args = {
error: {
code: 'invalid_json',
message: 'Test error message',
},
};

export const ReportErrorViewOnlyMode = Template.bind( {} );
ReportErrorViewOnlyMode.storyName = 'ReportError with ViewOnly Mode';
ReportErrorViewOnlyMode.args = {
Expand Down
148 changes: 148 additions & 0 deletions assets/js/components/ReportErrorActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* ReportErrorActions component.
*
* Site Kit by Google, Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import PropTypes from 'prop-types';

/**
* WordPress dependencies
*/
import {
createInterpolateElement,
Fragment,
useCallback,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import Data from 'googlesitekit-data';
import { Button } from 'googlesitekit-components';
import { CORE_SITE } from '../googlesitekit/datastore/site/constants';
import { CORE_MODULES } from '../googlesitekit/modules/datastore/constants';
import {
isErrorRetryable,
isInsufficientPermissionsError,
} from '../util/errors';
import useViewOnly from '../hooks/useViewOnly';
import Link from './Link';

const { useSelect, useDispatch } = Data;

export default function ReportErrorActions( { moduleSlug, error } ) {
const isViewOnly = useViewOnly();
const storeName = useSelect( ( select ) =>
select( CORE_MODULES ).getModuleStoreName( moduleSlug )
);
const requestAccessURL = useSelect( ( select ) =>
typeof select( storeName )?.getServiceEntityAccessURL === 'function'
? select( storeName ).getServiceEntityAccessURL()
: null
);

const errors = Array.isArray( error ) ? error : [ error ];

const retryableErrors = errors.filter(
( err ) =>
isErrorRetryable( err, err.selectorData ) &&
err.selectorData.name === 'getReport'
);

const showRetry = !! retryableErrors.length && ! isViewOnly;

const errorTroubleshootingLinkURL = useSelect( ( select ) => {
const err = {
...( showRetry ? retryableErrors[ 0 ] : errors[ 0 ] ),
};

if ( isInsufficientPermissionsError( err ) ) {
err.code = `${ moduleSlug }_insufficient_permissions`;
}

return select( CORE_SITE ).getErrorTroubleshootingLinkURL( err );
} );

const dispatch = useDispatch();

const hasInsufficientPermissionsError = errors.some( ( err ) =>
isInsufficientPermissionsError( err )
);

const handleRetry = useCallback( () => {
retryableErrors.forEach( ( err ) => {
const { selectorData } = err;
dispatch( selectorData.storeName ).invalidateResolution(
selectorData.name,
selectorData.args
);
} );
}, [ dispatch, retryableErrors ] );

const showRequestAccessURL =
requestAccessURL && hasInsufficientPermissionsError && ! isViewOnly;

return (
<div className="googlesitekit-report-error-actions">
{ showRequestAccessURL && (
<Button href={ requestAccessURL } target="_blank">
{ __( 'Request access', 'google-site-kit' ) }
</Button>
) }
{ showRetry ? (
<Fragment>
<Button onClick={ handleRetry }>
{ __( 'Retry', 'google-site-kit' ) }
</Button>
<span className="googlesitekit-error-retry-text">
{ createInterpolateElement(
__(
'Retry didn’t work? <HelpLink />',
'google-site-kit'
),
{
HelpLink: (
<Link
href={ errorTroubleshootingLinkURL }
external
>
{ __( 'Get help', 'google-site-kit' ) }
</Link>
),
}
) }
</span>
</Fragment>
) : (
<Link href={ errorTroubleshootingLinkURL } external>
{ __( 'Get help', 'google-site-kit' ) }
</Link>
) }
</div>
);
}

ReportErrorActions.propTypes = {
moduleSlug: PropTypes.string.isRequired,
error: PropTypes.oneOfType( [
PropTypes.arrayOf( PropTypes.object ),
PropTypes.object,
] ).isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { __, _x, sprintf } from '@wordpress/i18n';
import ReportMetric from './ReportMetric';
import MetricsLearnMoreLink from './MetricsLearnMoreLink';
import ErrorText from '../../../../components/ErrorText';
import ReportErrorActions from '../../../../components/ReportErrorActions';
import { getReportErrorMessage } from '../../../../util/errors';
import { CATEGORY_AVERAGE } from '../../util/constants';

export default function FieldReportMetrics( { data, error } ) {
Expand All @@ -44,10 +46,17 @@ export default function FieldReportMetrics( { data, error } ) {
} = data?.loadingExperience?.metrics || {};

if ( error ) {
const errorMessage = getReportErrorMessage( error );

return (
<div className="googlesitekit-pagespeed-insights-web-vitals-metrics">
<div className="googlesitekit-pagespeed-report__row googlesitekit-pagespeed-report__row--first">
<ErrorText message={ error.message } />
<div className="googlesitekit-pagespeed-report__row googlesitekit-pagespeed-report__row--error">
<ErrorText message={ errorMessage } />

<ReportErrorActions
moduleSlug="pagespeed-insights"
error={ error }
/>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import ReportMetric from './ReportMetric';
import MetricsLearnMoreLink from './MetricsLearnMoreLink';
import { getScoreCategory } from '../../util';
import { CATEGORY_AVERAGE } from '../../util/constants';
import { getReportErrorMessage } from '../../../../util/errors';
import ReportErrorActions from '../../../../components/ReportErrorActions';
import ErrorText from '../../../../components/ErrorText';

export default function LabReportMetrics( { data, error } ) {
Expand All @@ -44,10 +46,17 @@ export default function LabReportMetrics( { data, error } ) {
data?.lighthouseResult?.audits?.[ 'total-blocking-time' ];

if ( error ) {
const errorMessage = getReportErrorMessage( error );

return (
<div className="googlesitekit-pagespeed-insights-web-vitals-metrics">
<div className="googlesitekit-pagespeed-report__row googlesitekit-pagespeed-report__row--first">
<ErrorText message={ error.message } />
<div className="googlesitekit-pagespeed-report__row googlesitekit-pagespeed-report__row--error">
<ErrorText message={ errorMessage } />

<ReportErrorActions
moduleSlug="pagespeed-insights"
error={ error }
/>
</div>
</div>
);
Expand Down
Loading

0 comments on commit 5c2a7ac

Please sign in to comment.