-
Notifications
You must be signed in to change notification settings - Fork 295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor waitFor*
actions with resolveSelect
.
#9978
Changes from 17 commits
05c9fce
52e24bb
2f1270a
dc7719c
31cda3e
db72f9b
06f851e
1a83f18
e770a7e
4740af6
367c00c
62fe359
7cea708
55d2eb9
32de116
f01ef5d
df6be4e
e32c7ce
c049c5d
525d660
7b78c6f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -31,11 +31,7 @@ import { createRegistrySelector } from '@wordpress/data'; | |||||||||||||||||||||
* Internal dependencies | ||||||||||||||||||||||
*/ | ||||||||||||||||||||||
import API from 'googlesitekit-api'; | ||||||||||||||||||||||
import { | ||||||||||||||||||||||
commonActions, | ||||||||||||||||||||||
combineStores, | ||||||||||||||||||||||
createRegistryControl, | ||||||||||||||||||||||
} from 'googlesitekit-data'; | ||||||||||||||||||||||
import { commonActions, combineStores } from 'googlesitekit-data'; | ||||||||||||||||||||||
import { CORE_USER } from '../../../googlesitekit/datastore/user/constants'; | ||||||||||||||||||||||
import { CORE_SITE } from '../../../googlesitekit/datastore/site/constants'; | ||||||||||||||||||||||
import { CORE_MODULES } from '../../../googlesitekit/modules/datastore/constants'; | ||||||||||||||||||||||
|
@@ -53,7 +49,6 @@ import { | |||||||||||||||||||||
isValidPropertyID, | ||||||||||||||||||||||
isValidPropertySelection, | ||||||||||||||||||||||
} from '../utils/validation'; | ||||||||||||||||||||||
import { actions as webDataStreamActions } from './webdatastreams'; | ||||||||||||||||||||||
import { createValidatedAction } from '../../../googlesitekit/data/utils'; | ||||||||||||||||||||||
import { getItem, setItem } from '../../../googlesitekit/api/cache'; | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -202,7 +197,6 @@ const fetchSetGoogleTagIDMismatch = createFetchStore( { | |||||||||||||||||||||
} ); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// Actions | ||||||||||||||||||||||
const WAIT_FOR_PROPERTY_SUMMARIES = 'WAIT_FOR_PROPERTY_SUMMARIES'; | ||||||||||||||||||||||
const MATCHING_ACCOUNT_PROPERTY = 'MATCHING_ACCOUNT_PROPERTY'; | ||||||||||||||||||||||
const SET_HAS_MISMATCHED_TAG = 'SET_HAS_MISMATCHED_GOOGLE_TAG_ID'; | ||||||||||||||||||||||
const SET_IS_WEBDATASTREAM_AVAILABLE = 'SET_IS_WEBDATASTREAM_AVAILABLE'; | ||||||||||||||||||||||
|
@@ -294,11 +288,11 @@ const baseActions = { | |||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
yield webDataStreamActions.waitForWebDataStreams( propertyID ); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let webdatastream = registry | ||||||||||||||||||||||
.select( MODULES_ANALYTICS_4 ) | ||||||||||||||||||||||
.getMatchingWebDataStreamByPropertyID( propertyID ); | ||||||||||||||||||||||
let webdatastream = yield commonActions.await( | ||||||||||||||||||||||
registry | ||||||||||||||||||||||
.resolveSelect( MODULES_ANALYTICS_4 ) | ||||||||||||||||||||||
.getMatchingWebDataStreamByPropertyID( propertyID ) | ||||||||||||||||||||||
); | ||||||||||||||||||||||
|
||||||||||||||||||||||
if ( ! webdatastream ) { | ||||||||||||||||||||||
const webdatastreams = registry | ||||||||||||||||||||||
|
@@ -367,12 +361,12 @@ const baseActions = { | |||||||||||||||||||||
*matchAccountProperty( accountID ) { | ||||||||||||||||||||||
const registry = yield commonActions.getRegistry(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
yield baseActions.waitForPropertySummaries( accountID ); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const referenceURL = registry.select( CORE_SITE ).getReferenceSiteURL(); | ||||||||||||||||||||||
const propertySummaries = registry | ||||||||||||||||||||||
.select( MODULES_ANALYTICS_4 ) | ||||||||||||||||||||||
.getPropertySummaries( accountID ); | ||||||||||||||||||||||
const propertySummaries = yield commonActions.await( | ||||||||||||||||||||||
registry | ||||||||||||||||||||||
.resolveSelect( MODULES_ANALYTICS_4 ) | ||||||||||||||||||||||
.getPropertySummaries( accountID ) | ||||||||||||||||||||||
); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const property = yield baseActions.matchPropertyByURL( | ||||||||||||||||||||||
( propertySummaries || [] ).map( ( { _id } ) => _id ), | ||||||||||||||||||||||
|
@@ -516,18 +510,6 @@ const baseActions = { | |||||||||||||||||||||
return null; | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
|
||||||||||||||||||||||
/** | ||||||||||||||||||||||
* Waits for property summaries to be loaded for an account. | ||||||||||||||||||||||
* | ||||||||||||||||||||||
* @since 1.118.0 | ||||||||||||||||||||||
*/ | ||||||||||||||||||||||
*waitForPropertySummaries() { | ||||||||||||||||||||||
yield { | ||||||||||||||||||||||
payload: {}, | ||||||||||||||||||||||
type: WAIT_FOR_PROPERTY_SUMMARIES, | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
|
||||||||||||||||||||||
/** | ||||||||||||||||||||||
* Updates settings for a given measurement ID. | ||||||||||||||||||||||
* | ||||||||||||||||||||||
|
@@ -731,17 +713,7 @@ const baseActions = { | |||||||||||||||||||||
}, | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
const baseControls = { | ||||||||||||||||||||||
[ WAIT_FOR_PROPERTY_SUMMARIES ]: createRegistryControl( | ||||||||||||||||||||||
( { resolveSelect } ) => { | ||||||||||||||||||||||
return async () => { | ||||||||||||||||||||||
await resolveSelect( | ||||||||||||||||||||||
MODULES_ANALYTICS_4 | ||||||||||||||||||||||
).getAccountSummaries(); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
), | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
const baseControls = {}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
function baseReducer( state, { type, payload } ) { | ||||||||||||||||||||||
switch ( type ) { | ||||||||||||||||||||||
|
@@ -788,6 +760,20 @@ const baseResolvers = { | |||||||||||||||||||||
yield fetchGetPropertyStore.actions.fetchGetProperty( propertyID ); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
*getPropertySummaries( accountID ) { | ||||||||||||||||||||||
const { select, resolveSelect } = yield commonActions.getRegistry(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const summaries = | ||||||||||||||||||||||
select( MODULES_ANALYTICS_4 ).getAccountSummaries( accountID ); | ||||||||||||||||||||||
|
||||||||||||||||||||||
if ( summaries === undefined ) { | ||||||||||||||||||||||
yield commonActions.await( | ||||||||||||||||||||||
resolveSelect( MODULES_ANALYTICS_4 ).getAccountSummaries( | ||||||||||||||||||||||
accountID | ||||||||||||||||||||||
) | ||||||||||||||||||||||
); | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can and should simplify this to be unconditional since there's no downside to calling the selector here. This kind of guard is only really necessary around fetch actions, but the WP data resolver infra will only run once regardless of when this is called so it should be perfectly safe to do. Also, there could be a situation where the underlying resolver is invalidated where, using the current logic here would prevent it from running when it should. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @aaemnnosttv, it does make sense to unconditionally call the resolver, and I've applied that change. However, unless I've missed something, there are scenarios where a resolver can be called twice, as mentioned in my comment on the Lines 88 to 97 in df6be4e
This can be verified in a simple manner by isolating that test and adding a log line in the diff --git a/assets/js/modules/analytics-4/components/settings/PropertyOrWebDataStreamNotAvailableError.test.js b/assets/js/modules/analytics-4/components/settings/PropertyOrWebDataStreamNotAvailableError.test.js
index 14be657ce4..26a2837a95 100644
--- a/assets/js/modules/analytics-4/components/settings/PropertyOrWebDataStreamNotAvailableError.test.js
+++ b/assets/js/modules/analytics-4/components/settings/PropertyOrWebDataStreamNotAvailableError.test.js
@@ -62,7 +62,7 @@ describe( 'PropertyOrWebDataStreamNotAvailableError', () => {
.setMeasurementID( measurementID );
} );
- it( 'should not render when properties are not loaded yet', async () => {
+ it.only( 'should not render when properties are not loaded yet', async () => {
freezeFetch(
new RegExp(
'^/google-site-kit/v1/modules/analytics-4/data/account-summaries'
diff --git a/assets/js/modules/analytics-4/datastore/accounts.js b/assets/js/modules/analytics-4/datastore/accounts.js
index 473dd06b07..782be3271f 100644
--- a/assets/js/modules/analytics-4/datastore/accounts.js
+++ b/assets/js/modules/analytics-4/datastore/accounts.js
@@ -338,8 +338,14 @@ const baseReducer = createReducer( ( state, { type } ) => {
}
} );
+// Avoid console.log in tests.
+const log = ( ...args ) =>
+ process.stdout.write( args.map( JSON.stringify ).join( ' ' ) + '\n' );
+
const baseResolvers = {
*getAccountSummaries() {
+ log( 'getAccountSummaries resolver' );
+
const registry = yield commonActions.getRegistry();
let nextPageToken = '';
const summaries = registry
I don't think this means we need to change anything, just pointing it out as a bit of unexpected behaviour to be aware of. |
||||||||||||||||||||||
} | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
*getPropertyCreateTime() { | ||||||||||||||||||||||
const registry = yield commonActions.getRegistry(); | ||||||||||||||||||||||
// Ensure settings are available to select. | ||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intentionally defined after render? I don't think we could be sure it would work this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, yup. It was a bit of an odd one that took some digging to figure out what was going on.
I've added a clarifying comment, basically it's a side effect of adding the resolver for
getPropertySummaries()
which resolvesgetAccountSummaries()
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @techanvil – is it important that the resolver finishes? It seems to me that it would be more appropriate to update the fetch mock for the same endpoint above to freeze fetch "manually" using
fetchMock.get
rather thanonce
so that it would handle both requests. I think the context of the comment is useful, but even if we keep the same mock, we should define it before render. This should be possible since it supports defining multiple matchers/mocks as well as simple repetition. See https://www.wheresrhys.co.uk/fetch-mock/docs/legacy-api/Usage/cheatsheet#timing-and-repetitionThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not essential that the resolver finishes, and I'm happy to freeze both requests. I had initially done so, in fact, but decided to take this approach instead as I figured it spells out more clearly the precise flow of events.
However, happy to make the change and I have done so.
Although using
fetchMock.get()
to "manually" freeze the request works, I've taken the approach of modifyingfreezeFetch()
to allow a specific number of requests to be frozen. This retains the semantic value of callingfreezeFetch()
while making the test setup more explicit.