Skip to content
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

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from

Conversation

ankitrox
Copy link
Collaborator

Summary

Addresses issue:

Relevant technical choices

PR Author Checklist

  • My code is tested and passes existing unit tests.
  • My code has an appropriate set of unit tests which all pass.
  • My code is backward-compatible with WordPress 5.2 and PHP 7.4.
  • My code follows the WordPress coding standards.
  • My code has proper inline documentation.
  • I have added a QA Brief on the issue linked above.
  • I have signed the Contributor License Agreement (see https://cla.developers.google.com/).

Do not alter or remove anything below. The following sections will be managed by moderators only.

Code Reviewer Checklist

  • Run the code.
  • Ensure the acceptance criteria are satisfied.
  • Reassess the implementation with the IB.
  • Ensure no unrelated changes are included.
  • Ensure CI checks pass.
  • Check Storybook where applicable.
  • Ensure there is a QA Brief.
  • Ensure there are no unexpected significant changes to file sizes.

Merge Reviewer Checklist

  • Ensure the PR has the correct target branch.
  • Double-check that the PR is okay to be merged.
  • Ensure the corresponding issue has a ZenHub release assigned.
  • Add a changelog message to the issue.

Copy link

github-actions bot commented Nov 12, 2024

Build files for a6a2ffb are ready:

Copy link

github-actions bot commented Nov 12, 2024

Size Change: +581 B (+0.03%)

Total Size: 1.88 MB

Filename Size Change
./dist/assets/js/googlesitekit-activation-********************.js 24 kB +2 B (+0.01%)
./dist/assets/js/googlesitekit-ad-blocking-recovery-********************.js 55 kB +11 B (+0.02%)
./dist/assets/js/googlesitekit-adminbar-********************.js 34.6 kB +1 B (0%)
./dist/assets/js/googlesitekit-api-********************.js 10.1 kB +2 B (+0.02%)
./dist/assets/js/googlesitekit-data-********************.js 2.37 kB +1 B (+0.04%)
./dist/assets/js/googlesitekit-datastore-forms-********************.js 8.97 kB -1 B (-0.01%)
./dist/assets/js/googlesitekit-datastore-site-********************.js 20.5 kB +3 B (+0.01%)
./dist/assets/js/googlesitekit-datastore-ui-********************.js 10.1 kB -1 B (-0.01%)
./dist/assets/js/googlesitekit-datastore-user-********************.js 27.3 kB -1 B (0%)
./dist/assets/js/googlesitekit-entity-dashboard-********************.js 81.9 kB +110 B (+0.13%)
./dist/assets/js/googlesitekit-main-dashboard-********************.js 161 kB +147 B (+0.09%)
./dist/assets/js/googlesitekit-modules-ads-********************.js 33.4 kB +1 B (0%)
./dist/assets/js/googlesitekit-modules-adsense-********************.js 117 kB +5 B (0%)
./dist/assets/js/googlesitekit-modules-analytics-4-********************.js 184 kB -9 B (0%)
./dist/assets/js/googlesitekit-modules-********************.js 22.2 kB +1 B (0%)
./dist/assets/js/googlesitekit-modules-pagespeed-insights-********************.js 22.8 kB +2 B (+0.01%)
./dist/assets/js/googlesitekit-modules-reader-revenue-manager-********************.js 41.1 kB +289 B (+0.71%)
./dist/assets/js/googlesitekit-modules-search-console-********************.js 64.9 kB +4 B (+0.01%)
./dist/assets/js/googlesitekit-settings-********************.js 126 kB +4 B (0%)
./dist/assets/js/googlesitekit-splash-********************.js 68.9 kB +8 B (+0.01%)
./dist/assets/js/googlesitekit-user-input-********************.js 43.7 kB +3 B (+0.01%)
./dist/assets/js/googlesitekit-vendor-********************.js 322 kB +1 B (0%)
./dist/assets/js/googlesitekit-widgets-********************.js 96.2 kB -1 B (0%)
./dist/assets/js/googlesitekit-wp-dashboard-********************.js 62.6 kB -1 B (0%)
ℹ️ View Unchanged
Filename Size
./dist/assets/css/googlesitekit-admin-css-********************.min.css 60 kB
./dist/assets/css/googlesitekit-adminbar-css-********************.min.css 11.8 kB
./dist/assets/css/googlesitekit-authorize-application-css-********************.min.css 846 B
./dist/assets/css/googlesitekit-wp-dashboard-css-********************.min.css 8.2 kB
./dist/assets/js/32-********************.js 2.76 kB
./dist/assets/js/33-********************.js 2.25 kB
./dist/assets/js/34-********************.js 3.64 kB
./dist/assets/js/35-********************.js 935 B
./dist/assets/js/36-********************.js 893 B
./dist/assets/js/37-********************.js 1.61 kB
./dist/assets/js/38-********************.js 1.57 kB
./dist/assets/js/39-********************.js 1.61 kB
./dist/assets/js/40-********************.js 1.59 kB
./dist/assets/js/41-********************.js 1.83 kB
./dist/assets/js/42-********************.js 3.11 kB
./dist/assets/js/analytics-advanced-tracking-********************.js 901 B
./dist/assets/js/googlesitekit-components-gm2-********************.js 6.17 kB
./dist/assets/js/googlesitekit-components-gm3-********************.js 10.1 kB
./dist/assets/js/googlesitekit-consent-mode-********************.js 25.6 kB
./dist/assets/js/googlesitekit-datastore-location-********************.js 2.08 kB
./dist/assets/js/googlesitekit-events-provider-contact-form-7-********************.js 646 B
./dist/assets/js/googlesitekit-events-provider-easy-digital-downloads-********************.js 624 B
./dist/assets/js/googlesitekit-events-provider-mailchimp-********************.js 630 B
./dist/assets/js/googlesitekit-events-provider-ninja-forms-********************.js 712 B
./dist/assets/js/googlesitekit-events-provider-optin-monster-********************.js 675 B
./dist/assets/js/googlesitekit-events-provider-popup-maker-********************.js 634 B
./dist/assets/js/googlesitekit-events-provider-woocommerce-********************.js 657 B
./dist/assets/js/googlesitekit-events-provider-wpforms-********************.js 633 B
./dist/assets/js/googlesitekit-i18n-********************.js 3.93 kB
./dist/assets/js/googlesitekit-modules-sign-in-with-google-********************.js 24.3 kB
./dist/assets/js/googlesitekit-modules-tagmanager-********************.js 32.2 kB
./dist/assets/js/googlesitekit-notifications-********************.js 22.8 kB
./dist/assets/js/googlesitekit-polyfills-********************.js 377 B
./dist/assets/js/runtime-********************.js 1.4 kB

compressed-size-action

Copy link
Collaborator

@techanvil techanvil left a 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.

Image

Comment on lines 125 to 139
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
);
}
}
Copy link
Collaborator

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:

Suggested change
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
);
}

Comment on lines 81 to 108
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,
},
};
},
Copy link
Collaborator

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.

Suggested change
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

Comment on lines 273 to 291
$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;
}
Copy link
Collaborator

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:

Suggested change
$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

Copy link
Collaborator

@techanvil techanvil left a 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 () => {
Copy link
Collaborator

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:

// 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 () => {
Copy link
Collaborator

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();
Copy link
Collaborator

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.

Suggested change
$return_data = array();

Comment on lines +278 to +280
return function () use ( $return_data ) {
return (object) $return_data;
};
Copy link
Collaborator

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:

Suggested change
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() {
Copy link
Collaborator

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() {
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants