site-kit-wp: Add "Retry" button to ReportError component
Feature Description
Currently when the user encounters an error that causes ReportError to be displayed, there is no way to retry the request. We should add a “retry” button that invalidates the request’s resolver and forces the request to be tried again.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
- A “Retry” button should be added to the
ReportErrorcomponent whenever the return value fromgetErrorForSelectorcontains theselectorDataobject. - Other than that, there are a few exceptions where the button should not be shown if any of the following is true:
- If the
selectorNameis notgetReport(because then it could be any other type of error that is not from the report API request) - @eugene-manuilov raised this earlier today, and it makes sense to me. - If the error is an “insufficient permissions error”, i.e. the user doesn’t have access to this data. In that case, they will have to coordinate with the admin who does have access so that they can get access to. That’s not exactly a quick task (unless they’re sitting right next to them), so we probably shouldn’t show the Retry button there as it’s not going to help.
- If the error is a “permission scope error”, i.e. the user hasn’t granted certain OAuth scopes. In that case they will see a banner notification to fix this anyway, and retrying the request won’t do it.
- If the error has a
reconnectURLwith it, i.e. there is already a CTA URL present which can fix the problem - again retrying the request won’t do it in this case.
- If the
- When activated, the button should use this code:
dispatch(selectorData.storeName)
.invalidateResolution(
selectorData.name,
selectorData.args
);
to clear the error.
Implementation Brief
- In
assets/js/components/notifications/CTA.jscomponent:- Take two new props
retrywhich is a boolean andonRetrywhich is a function.- Both props are optional.
- When the
retryprop istrue, render aButtoncomponent with the following:onClickshould beonRetry.- Children should be translated string
Retry.
- Take two new props
- In
assets/js/components/ReportError.jscomponent:- When the passed
errorprop has aselectorDataobject-- Define a callback function for the retry handler which dispatches the
invalidateResolutionaction like it is shown in the AC. - Pass
retryto theCTAelement. - Pass the aforementioned retry handler function to the
onRetryprop of theCTAelement.
- Define a callback function for the retry handler which dispatches the
- When the passed
Test Coverage
- Add story with the Retry Button on
assets/js/components/ReportError.stories.js. We can do it without waiting for the blocker issue by passing an object of same shape as theerrorprop.
QA Brief
- Ensure the newly added story has a
Retrybutton. The following link will work once the PR gets merged: https://google.github.io/site-kit-wp/storybook/develop/?path=/story/components-reporterror--report-error-with-retry-button Otherwise, it can be found here: https://google.github.io/site-kit-wp/storybook/pull/5337/?path=/story/components-reporterror--report-error-with-retry-button - Verify the
Retrybutton appears as per the AC, if the error is thegetReportdata error and is not any of the following:- Is not an “insufficient permissions error”
- Is not a “permission scope error”
- Is not an auth error that has
error.data. reconnectURL
- A
getReporterror can be simulated by using an HTTP proxy/request proxy and having requests to the Site Kit API (example:http://sitekit.10uplabs.com/wp-json/google-site-kit/v1/modules/search-console/data/searchanalytics) fail/abort. This should cause the “retry” button to appear, and when clicked trigger the request again. - Refer to this comment. It’s almost the same as the AC.
Update:
@felixarntz spotted an issue with Adsense getReport errors (refer to this comment).
Also, refer the investigation and the proposed solution by @tofumatt here.
- To replicate this using Proxyman, set a breakpoint with a wildcard for the API requests like so
http://sitekit.10uplabs.com/wp-json/google-site-kit/v1/*. - The domain has to be your local or the test site’s domain.
- Abort all the
reportrequests. - Verify the
Retrybutton appears. - When clicking the
Retrybutton the requests should be sent and this time execute the requests from the Proxyman. - Verify the data appears on the widget.
Changelog entry
- Add a “retry” button for HTTP requests that encountered an error on the dashboard.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 23 (4 by maintainers)
@hussain-t @bethanylang @aaemnnosttv @tofumatt Based on today’s conversation, I suggest the following refinement here:
ReportErrors (i.e. the red border in-widget errors), we limit it by adding a number of exceptions where the button should not be shown if any of the following is true:selectorNameis notgetReport(because then it could be any other type of error that is not from the report API request) - @eugene-manuilov raised this earlier today, and it makes sense to me.reconnectURLwith it, i.e. there is already a CTA URL present which can fix the problem - again retrying the request won’t do it in this case.In order to implement the last three without duplicating too much, we should rely on helper functions like
isInsufficientPermissionsError( error ),isPermissionScopeError( error ), andisAuthError( error )which we can then call here. The last two should also be used in the existingdispatchAPIErrorfunction (which I used as a reference to define the last two conditions above). The first two helper functions already exist, so let’s add anisAuthErrorto them. We can then use these small helpers in bothReportErroranddispatchAPIErroras applicable.Let me know what you think.
@mohitwp When I tested this I was using an HTTP proxy (Proxyman, though Charles or other proxies would work too) and adding a breakpoint to all requests using Tools > Breakpoints > Add Rule:
Whatever HTTP proxy you’re using, adding breakpoints to
http://sitekit.10uplabs.com/wp-json/google-site-kit/v1/*will let you control the response from those requests. You can changehttp://sitekit.10uplabs.comto whatever your testing site’s URL is, and this will let you control whether the Site Kit API request goes through or not.If you abort the request, it will be an HTTP fetch failure that should trigger the retry button. It’s exactly what I did when testing:
Alternatively, you could probably insert code into the plugin to cause that request to fail, eg: https://github.com/google/site-kit-wp/blob/affa782aab7b74ad44d8dbb55cdc6a5f2d854806/includes/Modules/Search_Console.php#L213. Adding a
die('Foo.');right before this line should work, but I proposed the HTTP proxy because it means using exactly the production code to test.Does that help?
I’ve narrowed down the issue, and it’s here: https://github.com/google/site-kit-wp/blob/94633b1b3707fc36acdba73fe7647928224685be/assets/js/modules/adsense/components/module/ModuleOverviewWidget/index.js#L113-L127
Essentially, the issue is that we, somewhat simplistically, return the first error we encounter in the AdSense
ModuleOverviewWidgetas the error to pass to theWidgetReportErrorcomponent we render. It detects an error that can be retried and offers the user a “reload” button, which actually does reload that resolver’s request. The issue is, as soon as that request reloads successfully (without error), the next error is returned to that variable.There are four
getReportcalls with different arguments, each of which fail when the network tab is set to “offline”. If you click “Retry” four times, it actually works:https://user-images.githubusercontent.com/90871/176799449-6db673d3-9005-4d4b-93ba-aac1f114eb2c.mp4
But that’s obviously not ideal. I don’t think there’s a quick, easy fix for this though; we should probably implement a way for
WidgetReportErrorto—optionally—get an array of selectors to reset rather than just one. I think the best way to do this would be to add a way to pass multiple errors toWidgetReportError. Probably anerrorsprop or even just allowing theerrorprop to be a single error or an array of errors. When it’s an array, the retry button would invalidate every resolver.So I think to solve this, we should:
errorvariable, but instead return each error at https://github.com/google/site-kit-wp/blob/94633b1b3707fc36acdba73fe7647928224685be/assets/js/modules/adsense/components/module/ModuleOverviewWidget/index.js#L113-L127 as an entry in anerrorsarray.WidgetReportError’serrorprop to accept an array of errors or a single error. If an array is passed, we run the same checks on each error as we would a single one, ensuring that all errors in the array are “retry-able” before showing the “retry” button.erroris an array and the “Retry” button is clicked, each error’s resolver is invalidated.Doing this should fix the bug and generally make this retry functionality much more robust.
I don’t think we use this sort of “assign the first error to an
errorvariable passed toWidgetReportError” pattern elsewhere in the plugin, but if we do we should fix this behaviour there as well.QA Update: ✅
Followed the steps to recreate the issue reported by Felix.
Then, I refreshed main branch. On AdSense Widget on the dashboard, the error message appeared with ‘retry’ button. When I clicked on the button, the data appeared as expected within the widget. I also went through the other modules, i.e. analytics that errored out and ensured that when the retry button was clicked, the data was loaded.
I also ran through the tests highlighted in the original QA relating to the other error messages and the expected behaviour.
https://user-images.githubusercontent.com/73545194/177178144-9f5d6c9d-08a4-4343-a6ea-5cfca003358d.mp4
It seems a page reload (after disabling the “offline” network throttling) fixes it, eg. will allow the content to load. So it isn’t a
sessionStorageorlocalStoragecache issue, as those would persist between page refreshes.Instead, it’s likely that we aren’t clearing all of the relevant errors/invalidating all the relevant selectors for the AdSense widget’s requests. 🤔
@tofumatt Hmm, could you try with AdSense too? Maybe it’s indeed something odd there only. You should be able to mock everything around AdSense with the tester plugin to get it set up without a real account with data etc.
Make sure to also have the
adsenseSetupV2active, at least I had that active when encountering this problem.@mohitwp the
permission scope errorusing OAuth can be triggered by following these steps:Site Kit wants additional access to your Google AccountError connecting Site Kitwith a description ofThe Site Kit setup was interrupted because you did not grant the necessary permissions.Does that help you produce this error message?
QA Update ⚠️
@wpdarren I verified below listed scenarios as per QAB. Can you please review this PR if any other error is pending. I’m not able to produce "permission scope error” using OAuth.
Verified:
Newly added story has a Retry button. https://google.github.io/site-kit-wp/storybook/develop/?path=/story/components-reporterror--report-error-with-retry-button
If the error is getReport error than retry button is showing. Tested using proxy as mentioned here
If the error is an “insufficient permissions error” then Retry button not showing.
If the error has a with it then also retry button not showing.
https://user-images.githubusercontent.com/94359491/175872521-6b986389-f5f1-4cc1-a20a-f20accf7db27.mp4
@wpdarren These would specifically be in dashboard widgets, but not widget or error-specific; the functionality is generic.
The integration with the service side is specific to the learn more/help links which are not yet added here – there is no service aspect to test here 👍 As such, the current QAB should be sufficient.
@aaemnnosttv @hussain-t I’ve added these additional requirements to the ACs now.
@felixarntz This makes sense to me! Of course support will work with the QA team to test this thoroughly to ensure that this catches all of the necessary exceptions.
IB ✔️