diff --git a/assets/js/googlesitekit/datastore/site/email-reporting.js b/assets/js/googlesitekit/datastore/site/email-reporting.js index f235239ffe9..a842cd8e978 100644 --- a/assets/js/googlesitekit/datastore/site/email-reporting.js +++ b/assets/js/googlesitekit/datastore/site/email-reporting.js @@ -40,6 +40,7 @@ import { stringifyObject } from '@/js/util'; const START_INVITING_USER = 'START_INVITING_USER'; const FINISH_INVITING_USER = 'FINISH_INVITING_USER'; +const RESET_ELIGIBLE_SUBSCRIBERS = 'RESET_ELIGIBLE_SUBSCRIBERS'; const DEFAULT_ELIGIBLE_SUBSCRIBERS_ARGS = { page: 1, search: '', @@ -301,6 +302,26 @@ const baseActions = { payload: { userID }, }; }, + + /** + * Resets the eligible subscribers cache. + * + * @since n.e.x.t + * + * @return {Object} Redux-style action. + */ + *resetEligibleSubscribers() { + const { dispatch } = yield commonActions.getRegistry(); + + yield { + type: RESET_ELIGIBLE_SUBSCRIBERS, + payload: {}, + }; + + return dispatch( CORE_SITE ).invalidateResolutionForStoreSelector( + 'getEligibleSubscribers' + ); + }, }; export const baseReducer = createReducer( ( state, action ) => { @@ -322,6 +343,11 @@ export const baseReducer = createReducer( ( state, action ) => { state.emailReporting.invitingUsers[ payload.userID ] = false; break; } + case RESET_ELIGIBLE_SUBSCRIBERS: { + state.emailReporting.eligibleSubscribers = + baseInitialState.emailReporting.eligibleSubscribers; + break; + } default: break; diff --git a/assets/js/googlesitekit/datastore/site/email-reporting.test.js b/assets/js/googlesitekit/datastore/site/email-reporting.test.js index 63b5780d5de..8523d4541c7 100644 --- a/assets/js/googlesitekit/datastore/site/email-reporting.test.js +++ b/assets/js/googlesitekit/datastore/site/email-reporting.test.js @@ -368,6 +368,100 @@ describe( 'core/site Email Reporting', () => { } ).toThrow( 'userID should be a positive integer.' ); } ); } ); + + describe( 'resetEligibleSubscribers', () => { + it( 'clears cached eligible subscribers', async () => { + fetchMock.get( eligibleSubscribersEndpointRegExp, { + body: createEligibleSubscribersResponse( [] ), + status: 200, + } ); + + registry + .dispatch( CORE_SITE ) + .receiveGetEligibleSubscribers( + createEligibleSubscribersResponse( [] ), + { page: 1, search: '' } + ); + registry + .dispatch( CORE_SITE ) + .receiveGetEligibleSubscribers( + createEligibleSubscribersResponse( [] ), + { page: 1, search: 'editor' } + ); + + expect( + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ) + ).toBeDefined(); + expect( + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: 'editor', + } ) + ).toBeDefined(); + + await registry.dispatch( CORE_SITE ).resetEligibleSubscribers(); + + expect( + registry.stores[ CORE_SITE ].store.getState().emailReporting + .eligibleSubscribers + ).toEqual( {} ); + } ); + + it( 'invalidates resolver state so prior args can re-fetch', async () => { + provideUserInfo( registry, { id: 1 } ); + + fetchMock.getOnce( eligibleSubscribersEndpointRegExp, { + body: createEligibleSubscribersResponse( [ + { + id: 2, + displayName: 'Eligible User', + email: 'eligible@example.com', + role: 'editor', + subscribed: false, + invited: false, + }, + ] ), + status: 200, + } ); + + registry + .select( CORE_SITE ) + .getEligibleSubscribers( defaultEligibleSubscribersArgs ); + await untilResolved( + registry, + CORE_SITE + ).getEligibleSubscribers( defaultEligibleSubscribersArgs ); + + expect( fetchMock ).toHaveFetchedTimes( 1 ); + + await registry.dispatch( CORE_SITE ).resetEligibleSubscribers(); + + fetchMock.getOnce( eligibleSubscribersEndpointRegExp, { + body: createEligibleSubscribersResponse( [ + { + id: 3, + displayName: 'Another Eligible User', + email: 'another@example.com', + role: 'author', + subscribed: false, + invited: false, + }, + ] ), + status: 200, + } ); + + registry + .select( CORE_SITE ) + .getEligibleSubscribers( defaultEligibleSubscribersArgs ); + await untilResolved( + registry, + CORE_SITE + ).getEligibleSubscribers( defaultEligibleSubscribersArgs ); + + expect( fetchMock ).toHaveFetchedTimes( 2 ); + } ); + } ); } ); describe( 'selectors', () => { diff --git a/assets/js/googlesitekit/modules/datastore/sharing-settings.js b/assets/js/googlesitekit/modules/datastore/sharing-settings.js index f3681b593d1..7ed1cc855b0 100644 --- a/assets/js/googlesitekit/modules/datastore/sharing-settings.js +++ b/assets/js/googlesitekit/modules/datastore/sharing-settings.js @@ -38,6 +38,7 @@ import { createStrictSelect, createValidationSelector, } from '@/js/googlesitekit/data/utils'; +import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants'; // Actions const SET_SHARING_MANAGEMENT = 'SET_SHARING_MANAGEMENT'; @@ -196,6 +197,10 @@ const baseActions = { } } + if ( ! error ) { + registry.dispatch( CORE_SITE ).resetEligibleSubscribers(); + } + yield { type: FINISH_SUBMIT_SHARING_CHANGES, payload: {}, @@ -222,6 +227,11 @@ const baseActions = { const { response, error } = yield fetchResetSharingSettingsStore.actions.fetchResetSharingSettings(); + if ( ! error ) { + const registry = yield commonActions.getRegistry(); + registry.dispatch( CORE_SITE ).resetEligibleSubscribers(); + } + yield { type: FINISH_SUBMIT_SHARING_CHANGES, payload: {}, diff --git a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js index 60f80910777..d00648cf2d4 100644 --- a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js +++ b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js @@ -25,6 +25,8 @@ import { freezeFetch, provideModuleRegistrations, provideModules, + provideUserInfo, + untilResolved, } from '../../../../../tests/js/utils'; import { MODULES_SEARCH_CONSOLE } from '@/js/modules/search-console/datastore/constants'; import { MODULE_SLUG_SEARCH_CONSOLE } from '@/js/modules/search-console/constants'; @@ -33,6 +35,7 @@ import { MODULE_SLUG_PAGESPEED_INSIGHTS } from '@/js/modules/pagespeed-insights/ import { MODULE_SLUG_ADSENSE } from '@/js/modules/adsense/constants'; import { MODULE_SLUG_ANALYTICS_4 } from '@/js/modules/analytics-4/constants'; import { MODULE_SLUG_TAGMANAGER } from '@/js/modules/tagmanager/constants'; +import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants'; describe( 'core/modules sharing-settings', () => { const dashboardSharingDataBaseVar = '_googlesitekitDashboardSharingData'; @@ -87,6 +90,17 @@ describe( 'core/modules sharing-settings', () => { MODULE_SLUG_SEARCH_CONSOLE, MODULE_SLUG_TAGMANAGER, ]; + const eligibleSubscribersEndpointRegExp = + /email-reporting-eligible-subscribers/; + + function createEligibleSubscribersResponse( users, args = {} ) { + return { + page: args.page || 1, + total: args.total || users.length, + totalPages: args.totalPages || 1, + users, + }; + } let registry; let store; @@ -277,6 +291,216 @@ describe( 'core/modules sharing-settings', () => { ).toBe( ownerID ); } ); + + it( 'invalidates eligible subscribers after a successful save', async () => { + global[ dashboardSharingDataBaseVar ] = dashboardSharingData; + provideUserInfo( registry, { id: 1 } ); + + registry.dispatch( CORE_SITE ).receiveGetEligibleSubscribers( + createEligibleSubscribersResponse( [ + { + id: 2, + displayName: 'Eligible User', + email: 'eligible@example.com', + role: 'editor', + subscribed: false, + invited: false, + }, + ] ), + { page: 1, search: '' } + ); + + expect( + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ) + ).toBeDefined(); + + await registry + .resolveSelect( CORE_MODULES ) + .getSharingSettings(); + + fetchMock.postOnce( + new RegExp( + '^/google-site-kit/v1/core/modules/data/sharing-settings' + ), + { + body: { + settings: sharingSettings, + newOwnerIDs: {}, + }, + } + ); + + await registry.dispatch( CORE_MODULES ).saveSharingSettings(); + + expect( + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ) + ).toBeUndefined(); + + fetchMock.getOnce( eligibleSubscribersEndpointRegExp, { + body: createEligibleSubscribersResponse( [ + { + id: 3, + displayName: 'New Eligible User', + email: 'new@example.com', + role: 'author', + subscribed: false, + invited: false, + }, + ] ), + status: 200, + } ); + + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ); + await untilResolved( + registry, + CORE_SITE + ).getEligibleSubscribers( { search: '' } ); + + expect( fetchMock ).toHaveFetched( + eligibleSubscribersEndpointRegExp + ); + } ); + + it( 'does not invalidate eligible subscribers after a failed save', async () => { + global[ dashboardSharingDataBaseVar ] = dashboardSharingData; + + registry + .dispatch( CORE_SITE ) + .receiveGetEligibleSubscribers( + createEligibleSubscribersResponse( [] ), + { page: 1, search: '' } + ); + + await registry + .resolveSelect( CORE_MODULES ) + .getSharingSettings(); + + fetchMock.postOnce( + new RegExp( + '^/google-site-kit/v1/core/modules/data/sharing-settings' + ), + { + status: 500, + body: { + code: 'internal_server_error', + message: 'Internal Server Error', + data: { status: 500 }, + }, + } + ); + + await registry.dispatch( CORE_MODULES ).saveSharingSettings(); + + expect( + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ) + ).toBeDefined(); + expect( console ).toHaveErrored(); + } ); + } ); + + describe( 'resetSharingSettings', () => { + it( 'invalidates eligible subscribers after a successful reset', async () => { + provideUserInfo( registry, { id: 1 } ); + + registry.dispatch( CORE_SITE ).receiveGetEligibleSubscribers( + createEligibleSubscribersResponse( [ + { + id: 2, + displayName: 'Eligible User', + email: 'eligible@example.com', + role: 'editor', + subscribed: false, + invited: false, + }, + ] ), + { page: 1, search: '' } + ); + + fetchMock.postOnce( + new RegExp( + '^/google-site-kit/v1/core/modules/data/sharing-settings' + ), + { + body: { + settings: {}, + }, + } + ); + + await registry.dispatch( CORE_MODULES ).resetSharingSettings(); + + expect( + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ) + ).toBeUndefined(); + + fetchMock.getOnce( eligibleSubscribersEndpointRegExp, { + body: createEligibleSubscribersResponse( [ + { + id: 3, + displayName: 'Another Eligible User', + email: 'another@example.com', + role: 'author', + subscribed: false, + invited: false, + }, + ] ), + status: 200, + } ); + + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ); + await untilResolved( + registry, + CORE_SITE + ).getEligibleSubscribers( { search: '' } ); + + expect( fetchMock ).toHaveFetched( + eligibleSubscribersEndpointRegExp + ); + } ); + + it( 'does not invalidate eligible subscribers after a failed reset', async () => { + registry + .dispatch( CORE_SITE ) + .receiveGetEligibleSubscribers( + createEligibleSubscribersResponse( [] ), + { page: 1, search: '' } + ); + + fetchMock.postOnce( + new RegExp( + '^/google-site-kit/v1/core/modules/data/sharing-settings' + ), + { + status: 500, + body: { + code: 'internal_server_error', + message: 'Internal Server Error', + data: { status: 500 }, + }, + } + ); + + await registry.dispatch( CORE_MODULES ).resetSharingSettings(); + + expect( + registry.select( CORE_SITE ).getEligibleSubscribers( { + search: '', + } ) + ).toBeDefined(); + expect( console ).toHaveErrored(); + } ); } ); describe( 'receiveGetSharingSettings', () => {