-
Notifications
You must be signed in to change notification settings - Fork 291
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
Enhancement/9363 publication onboarding state sync server side #9673
base: develop
Are you sure you want to change the base?
Enhancement/9363 publication onboarding state sync server side #9673
Conversation
Build files for a6a2ffb are ready:
|
Size Change: +581 B (+0.03%) Total Size: 1.88 MB
ℹ️ View Unchanged
|
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.
Hi @ankitrox, this mostly works but I've realised there's a bug we didn't consider while drafting the IB and we'll need to diverge from the IB to avoid it.
Also, please can you add a scenario to the QAB for testing the sync on the settings screen (i.e. when clicking the Complete publication setup CTA and returning to the page after 15 seconds) to ensure the AC are verified.
if ( !! response?.publicationOnboardingState ) { | ||
const newOnboardingState = response.publicationOnboardingState; | ||
|
||
if ( | ||
!! currentOnboardingState && | ||
newOnboardingState !== currentOnboardingState && | ||
newOnboardingState === | ||
PUBLICATION_ONBOARDING_STATES.ONBOARDING_COMPLETE | ||
) { | ||
setValue( | ||
UI_KEY_READER_REVENUE_MANAGER_SHOW_PUBLICATION_APPROVED_NOTIFICATION, | ||
true | ||
); | ||
} | ||
} |
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.
This can be marginally simplified:
if ( !! response?.publicationOnboardingState ) { | |
const newOnboardingState = response.publicationOnboardingState; | |
if ( | |
!! currentOnboardingState && | |
newOnboardingState !== currentOnboardingState && | |
newOnboardingState === | |
PUBLICATION_ONBOARDING_STATES.ONBOARDING_COMPLETE | |
) { | |
setValue( | |
UI_KEY_READER_REVENUE_MANAGER_SHOW_PUBLICATION_APPROVED_NOTIFICATION, | |
true | |
); | |
} | |
} | |
const newOnboardingState = response?.publicationOnboardingState; | |
if ( | |
currentOnboardingState && | |
newOnboardingState !== currentOnboardingState && | |
newOnboardingState === | |
PUBLICATION_ONBOARDING_STATES.ONBOARDING_COMPLETE | |
) { | |
setValue( | |
UI_KEY_READER_REVENUE_MANAGER_SHOW_PUBLICATION_APPROVED_NOTIFICATION, | |
true | |
); | |
} |
reducerCallback: ( | ||
state, | ||
{ publicationOnboardingState, isSavedSetting } | ||
) => { | ||
if ( ! publicationOnboardingState ) { | ||
return state; | ||
} | ||
|
||
return { | ||
...state, | ||
settings: { | ||
...state.settings, | ||
publicationOnboardingState, | ||
// eslint-disable-next-line sitekit/no-direct-date | ||
publicationOnboardingStateLastSyncedAtMs: Date.now(), | ||
}, | ||
savedSettings: { | ||
publicationOnboardingState: isSavedSetting | ||
? publicationOnboardingState | ||
: state.savedSettings?.publicationOnboardingState, | ||
publicationOnboardingStateLastSyncedAtMs: isSavedSetting | ||
? // eslint-disable-next-line sitekit/no-direct-date | ||
Date.now() | ||
: state.savedSettings | ||
?.publicationOnboardingStateLastSyncedAtMs, | ||
}, | ||
}; | ||
}, |
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.
This is implemented pretty much as the IB specifies, however I've realised that it actually introduces a bug and we need to diverge from the IB to avoid it.
At present, on the setup and settings screens, if the user selects a different publication while the sync request is in flight, the new onboarding state will be incorrectly applied to the newly selected publication. I've recorded a screencast to illustrate this:
9363-wrong-publication-update.mp4
To mitigate this we need to return the publicationID
in the sync response and use that to determine which state gets updated (and so there's no need to return isSavedSetting
). We'll also need to update the onboarding state for the publication found in state.publications
. I'd recommend using createReducer()
from the googlesitekit-data
module to use Immer for the reducer so we can update the state directly, simplifying the reducer logic.
reducerCallback: ( | |
state, | |
{ publicationOnboardingState, isSavedSetting } | |
) => { | |
if ( ! publicationOnboardingState ) { | |
return state; | |
} | |
return { | |
...state, | |
settings: { | |
...state.settings, | |
publicationOnboardingState, | |
// eslint-disable-next-line sitekit/no-direct-date | |
publicationOnboardingStateLastSyncedAtMs: Date.now(), | |
}, | |
savedSettings: { | |
publicationOnboardingState: isSavedSetting | |
? publicationOnboardingState | |
: state.savedSettings?.publicationOnboardingState, | |
publicationOnboardingStateLastSyncedAtMs: isSavedSetting | |
? // eslint-disable-next-line sitekit/no-direct-date | |
Date.now() | |
: state.savedSettings | |
?.publicationOnboardingStateLastSyncedAtMs, | |
}, | |
}; | |
}, | |
reducerCallback: createReducer( | |
( state, { publicationID, publicationOnboardingState } ) => { | |
if ( ! publicationID ) { | |
return; | |
} | |
// eslint-disable-next-line sitekit/no-direct-date | |
const publicationOnboardingStateLastSyncedAtMs = Date.now(); | |
if ( state.settings.publicationID === publicationID ) { | |
state.settings.publicationOnboardingState = | |
publicationOnboardingState; | |
state.settings.publicationOnboardingStateLastSyncedAtMs = | |
publicationOnboardingStateLastSyncedAtMs; | |
} | |
if ( state.savedSettings.publicationID === publicationID ) { | |
state.savedSettings.publicationOnboardingState = | |
publicationOnboardingState; | |
state.savedSettings.publicationOnboardingStateLastSyncedAtMs = | |
publicationOnboardingStateLastSyncedAtMs; | |
} | |
const publication = state.publications.find( | |
// eslint-disable-next-line sitekit/acronym-case | |
( { publicationId: id } ) => id === publicationID | |
); | |
if ( publication ) { | |
publication.onboardingState = publicationOnboardingState; | |
} | |
} | |
), |
Cc @nfmohit
$saved_settings = false; | ||
$settings = $this->get_settings(); | ||
|
||
if ( $data['publicationID'] === $settings->get()['publicationID'] ) { | ||
$settings->merge( | ||
array( | ||
'publicationOnboardingState' => $new_onboarding_state, | ||
) | ||
); | ||
$saved_settings = true; | ||
} | ||
|
||
$return_data = array( | ||
'publicationOnboardingState' => $new_onboarding_state, | ||
); | ||
|
||
if ( $saved_settings ) { | ||
$return_data['isSavedSetting'] = true; | ||
} |
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.
As per my comment above, we should diverge from the IB and return publicationID
here, while not returning isSavedSetting
:
$saved_settings = false; | |
$settings = $this->get_settings(); | |
if ( $data['publicationID'] === $settings->get()['publicationID'] ) { | |
$settings->merge( | |
array( | |
'publicationOnboardingState' => $new_onboarding_state, | |
) | |
); | |
$saved_settings = true; | |
} | |
$return_data = array( | |
'publicationOnboardingState' => $new_onboarding_state, | |
); | |
if ( $saved_settings ) { | |
$return_data['isSavedSetting'] = true; | |
} | |
$settings = $this->get_settings(); | |
if ( $data['publicationID'] === $settings->get()['publicationID'] ) { | |
$settings->merge( | |
array( | |
'publicationOnboardingState' => $new_onboarding_state, | |
) | |
); | |
} | |
$return_data = array( | |
'publicationID' => $data['publicationID'], | |
'publicationOnboardingState' => $new_onboarding_state, | |
); |
Cc @nfmohit
tests/phpunit/integration/Modules/Reader_Revenue_ManagerTest.php
Outdated
Show resolved
Hide resolved
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 @ankitrox, just a few last comments to address and this should be good to go.
@@ -96,7 +94,7 @@ describe( 'modules/reader-revenue-manager publications', () => { | |||
.dispatch( MODULES_READER_REVENUE_MANAGER ) | |||
.syncPublicationOnboardingState(); | |||
|
|||
expect( syncStatus ).toBeUndefined(); | |||
expect( syncStatus ).toEqual( {} ); | |||
} ); | |||
|
|||
it( 'should update the settings and call the saveSettings endpoint', async () => { |
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.
This test should be renamed as the action is no longer saving the settings. I'd suggest something like this:
it( 'should call the `sync-publication-onboarding-state` endpoint and update the settings in state', async () => {
If renaming as suggested, please make the assertion for calling the endpoint more explicit:
site-kit-wp/assets/js/modules/reader-revenue-manager/datastore/publications.test.js
Lines 126 to 127 in a6a2ffb
// Set expectations for settings endpoint. | |
expect( fetchMock ).toHaveFetchedTimes( 1 ); |
} ); | ||
|
||
it( 'should not update the timestamp, call the saveSettings endpoint, and set UI_KEY_SHOW_RRM_PUBLICATION_APPROVED_NOTIFICATION to true in CORE_UI store if there is no saved publication', async () => { | ||
it( 'should not update the timestamp if onboarding state has not changed', async () => { |
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.
This test can be removed as there's no longer any timestamp specific logic in the syncPublicationOnboardingState()
action.
throw new Missing_Required_Param_Exception( 'publicationOnboardingState' ); | ||
} | ||
|
||
$return_data = array(); |
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.
There no real need to define $return_data
here.
$return_data = array(); |
return function () use ( $return_data ) { | ||
return (object) $return_data; | ||
}; |
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.
As per my comment above we don't need to refer to $return_data
here, it's easier to follow if we just return an empty array directly:
return function () use ( $return_data ) { | |
return (object) $return_data; | |
}; | |
return function () { | |
return (object) array(); | |
}; |
@@ -231,6 +232,92 @@ function ( $domain ) { | |||
$this->assertEquals( $expected_filter, urldecode( $filter ) ); | |||
} | |||
|
|||
public function test_sync_publication_onboarding_state_returns_empty_object() { |
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.
This test name is not so useful as it simply describes the return value of the request, rather than the scenario that's being tested. Please can you make it more descriptive of the actual scenario.
This might illustrate the need for some additional tests to cover scenarios which are not currently tested.
$this->assertEquals( (object) array(), $result ); | ||
} | ||
|
||
public function test_sync_publication_onboarding_state_returns_new_state() { |
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.
As per my comment above please can you update this test name to be more descriptive of the scenario that's being tested.
Summary
Addresses issue:
Relevant technical choices
PR Author Checklist
Do not alter or remove anything below. The following sections will be managed by moderators only.
Code Reviewer Checklist
Merge Reviewer Checklist