site-kit-wp icon indicating copy to clipboard operation
site-kit-wp copied to clipboard

Add REST and datastore APIs for audience settings

Open techanvil opened this issue 1 year ago • 14 comments

Feature Description

Add REST and datastore APIs for audience settings. This essentially means the following REST endpoints:

  • GET audience-settings
  • POST audience-settings

And the following datastore selectors and actions (including additional Redux infra as needed):

  • getConfiguredAudiences()
  • haveConfiguredAudiencesChanged()
  • setConfiguredAudiences()
  • isAudienceSegmentationWidgetHidden()
  • setAudienceSegmentationWidgetHidden()
  • saveAudienceSettings()

See REST infrastructure and datastore configuration in the design doc.


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

Acceptance criteria

REST API Endpoints Implementation

  • Implement the GET audience-settings endpoint in the analytics-4 module to fetch user settings for audiences. This endpoint should return an object containing the following:
    • configuredAudiences: An array, with a default value of null.
    • isAudienceSegmentationWidgetHidden: A boolean, with a default value of false.
  • Implement the POST audience-settings endpoint in the analytics-4 module to update user settings for audiences. This endpoint should accept an object that may contain the configuredAudiences array and the isAudienceSegmentationWidgetHidden boolean value, and update the corresponding settings.

Datastore Implementation

  • Implement getConfiguredAudiences() selector in the analytics-4 partial datastore. This selector should retrieve the configuredAudiences array from the GET audience-settings endpoint.
  • Implement haveConfiguredAudiencesChanged() in the analytics-4 partial datastore to check if the local state for the configured audiences has changed.
  • Implement setConfiguredAudiences( audienceResourceNames ) in the analytics-4 partial datastore to set the local state of the configured audiences.
  • Implement isAudienceSegmentationWidgetHidden() selector in the analytics-4 partial datastore. This selector should retrieve the current value of the isAudienceSegmentationWidgetHidden setting via the GET audience-settings endpoint.
  • Implement setAudienceSegmentationWidgetHidden( isWidgetHidden ) action in the analytics-4 partial datastore. This action should update the local state of the isAudienceSegmentationWidgetHidden setting.
  • Implement saveAudienceSettings() action in the analytics-4 partial datastore. This action should persist the local state of the audience settings via the POST audience-settings endpoint.

Implementation Brief

REST API Endpoints

In includes/Modules/Analytics_4:

  • [x] Create new class Audience_Settings that extends User_Setting class with the following:
    • [x] It should largely mirror the Key_Metrics_Settings class.
    • [x] Replace the OPTION CONSTANT with the value googlesitekit_audience_settings.
    • [x] Replace isWidgetHidden with isAudienceSegmentationWidgetHidden and widgetSlugs with configuredAudiences.

In includes/Modules/Analytics_4.php:

  • [x] In the get_datapoint_definitions() method within the audienceSegmentation feature flag enabled block:
    • [x] Add a new GET:audience-settings endpoint to fetch audience user settings.
    • [x] Add a new POST:audience-settings endpoint to update audience user settings.
  • [x] In the create_data_request() method:
    • [x] Add a case for the new GET:audience-settings endpoint to fetch audience user settings.
      • [x] Use the Audience_Settings::get() method to retrieve the googlesitekit_audience_settings option.
      • [x] Return the configuredAudiences and isAudienceSegmentationWidgetHidden settings.
    • [x] Add a case for the new POST:audience-settings endpoint to update the user settings for audiences.
      • [x] Validate the request data and ensure the configuredAudiences is an array and isAudienceSegmentationWidgetHidden is a boolean.
      • [x] Use the Audience_Settings::merge() method to update the googlesitekit_audience_settings option.
      • [x] Return the updated configuredAudiences and isAudienceSegmentationWidgetHidden settings.

Fetch Stores

  • [x] Create a new file named audience-settings.js within the assets/js/modules/analytics-4/datastore/ directory.
  • [x] Utilize createFetchStore to establish fetch stores for the GET audience-settings and POST audience-settings endpoints. These stores will handle fetching and updating the audience settings from the REST API.
  • [x] These can share the same reducer using the createReducer function from the Immer library. It should set both the audienceSettings.settings and audienceSettings.savedSettings properties in the state.
    • [x] For GET audience-settings:
      • [x] Define a base name, such as getAudienceSettings.
      • [x] The store should manage to fetch the audience settings, including configuredAudiences and isAudienceSegmentationWidgetHidden.
    • [x] For POST audience-settings:
      • [x] Define a base name, such as saveAudienceSettings.
      • [x] Validate the request data and ensure the configuredAudiences is an array and isAudienceSegmentationWidgetHidden is a boolean.

Base Initial State

  • [x] Add audienceSettings to the initial state with the value of an empty object.
  • [x] Eventually, the audienceSettings object will contain settings and savedSettings properties.

Resolvers

  • [x] Implement a resolver for *getAudienceSettings() to ensure that the audience settings are fetched from the server when the data is not already present in the state.
  • [x] The resolver should check the existing state to determine if the fetch has already occurred to prevent unnecessary network requests.

Selectors

  • [x] getAudienceSettings(): This selector should return the audienceSettings.settings state.
  • [x] getConfiguredAudiences(): Use the getAudienceSettings selector to retrieve the configuredAudiences array from the audience settings.
  • [x] isAudienceSegmentationWidgetHidden(): Use the getAudienceSettings selector to retrieve the isAudienceSegmentationWidgetHidden setting from the audience settings.
  • [x] haveConfiguredAudiencesChanged(): This selector should return the result of a comparison between the settings and savedSettings properties of the audienceSettings state.

Actions

  • [x] setConfiguredAudiences( audienceResourceNames ): This action should locally update the list of configured audiences of the audienceSettings.settings state.
  • [x] setAudienceSegmentationWidgetHidden( isWidgetHidden ): This action should locally update the isAudienceSegmentationWidgetHidden setting of the audienceSettings.settings state.
  • [x] saveAudienceSettings(): This action should call the fetch store-generated fetchSaveAudienceSettings action with the current audienceSettings.settings state.

Test Coverage

  • [x] Add tests for the Audience_Settings class methods.
  • [x] Unit tests should be created for the new selectors and actions.

QA Brief

  • Make sure you have Site Kit set up with Google Analytics 4.
  • Ensure the audienceSegmentation feature flag is enabled.
  • Open the developer tools in your browser and navigate to the console tab.
  • Execute the following newly added audience settings actions and selectors in the console.

Actions

Action: setConfiguredAudiences( audienceResourceNames: Array<string> )

  1. Execute the action by running the following command:

    googlesitekit.data.dispatch('modules/analytics-4').setConfiguredAudiences( [ 'audienceResourceName1', 'audienceResourceName2' ] )
    
  2. Verify that the action has updated the client-side state correctly.

  3. Run the selector to double-check:

    googlesitekit.data.select('modules/analytics-4').getConfiguredAudiences()
    

    It should return the updated array.

Action: setAudienceSegmentationWidgetHidden( isWidgetHidden: boolean )

  1. Execute the action by running the following command:

    googlesitekit.data.dispatch('modules/analytics-4').setAudienceSegmentationWidgetHidden( true )
    
  2. Verify that the action has updated the client-side state correctly.

  3. Run the selector to double-check:

    googlesitekit.data.select('modules/analytics-4').isAudienceSegmentationWidgetHidden()
    

    It should return the updated boolean value.

Action: saveAudienceSettings()

  1. Execute the action by running the following command:

    googlesitekit.data.dispatch('modules/analytics-4').saveAudienceSettings()
    
  2. Check the network tab for a POST request to audience-settings and verify that the request payload contains the correct data if the state has changed.

Selectors

Selector: getAudienceSettings(): Object

  1. Execute the selector:

    googlesitekit.data.select('modules/analytics-4').getAudienceSettings()
    
  2. Verify that it either returns the correct object with configuredAudiences and isAudienceSegmentationWidgetHidden properties or triggers a network request to fetch the audience settings.

Selector: getConfiguredAudiences(): Array<string>

  1. Execute the selector:

    googlesitekit.data.select('modules/analytics-4').getConfiguredAudiences()
    
  2. Verify that it returns the array of configured audience resource names or an empty array.

Selector: isAudienceSegmentationWidgetHidden(): boolean

  1. Execute the selector:

    googlesitekit.data.select('modules/analytics-4').isAudienceSegmentationWidgetHidden()
    
  2. Verify that it returns the boolean value of isAudienceSegmentationWidgetHidden.

Selector: haveConfiguredAudiencesChanged(): boolean

  1. Execute the selector:

    googlesitekit.data.select('modules/analytics-4').haveConfiguredAudiencesChanged()
    
  2. Verify that it returns a boolean value indicating whether the configuredAudiences have changed.

Changelog entry

  • Introduce infrastructure for managing Audience settings.

techanvil avatar Jan 25 '24 12:01 techanvil

  • A new GET audience-settings endpoint should be implemented to fetch user settings for audiences in the analytics-4 module. This endpoint should return an object containing the key isAudienceSegmentationWidgetHidden with a boolean value. The default value for this key should be false.

@hussain-t, I think the default value should be the one selected by the primary admin (module owner), right? See the design doc:

When the feature has been set up, all subsequent users (i.e. view-only and secondary admin users)
will see the audience selection of the primary admin until they make a change themselves. After this
point, their own settings will take precedence and there will be no way to revert to the primary
admin’s settings.

https://docs.google.com/document/d/1MGD5Djy6AeeZC4zBtHqS-lQEWD9jw0kf-IIWw-jLCFU/edit?tab=t.0#heading=h.q4hnjsnlu3ha

eugene-manuilov avatar Jan 30 '24 13:01 eugene-manuilov

@eugene-manuilov @hussain-t actually, per-user settings are fine here.

For one thing this setting was not intended to follow the primary admin's.

More importantly to be aware of though, is we're actually removing this primary/secondary user settings aspect altogether due to complications with that approach.

Instead, we are going to take the approach where each user's settings are initialised according to some logic and don't attempt to follow a root setting at all (see this comment thread on the design doc).

I will be updating the design doc soon, apologies for the confusion in the meantime!

techanvil avatar Jan 30 '24 13:01 techanvil

Ok, thanks, @techanvil. So the default value should be neither false nor the primary admin's selection, but the value set in the Analytics settings, right? Then how is it going to work for the beginning, when nobody has selected it yet?

For example, if I am the first one who selects to show the feature, then my user settings will be updated as well as the Analytics settings, right? But then, if I decide to turn it off, then it will be turned off only for me and all other users will continue seeing it, right? So, to turn it off for all users, everyone will need to go to the settings and manually turn it off for themselves?

eugene-manuilov avatar Jan 30 '24 14:01 eugene-manuilov

@eugene-manuilov, I think the default value should be false - it's similar to the isWidgetHidden Key Metrics setting which is a user setting:

https://github.com/google/site-kit-wp/blob/c173f6c206ce0b7f2eb0777b0acfe3bfe649c385/includes/Core/Key_Metrics/Key_Metrics_Settings.php#L48-L53

This KM setting already works in the same way - each (admin) user controls their own setting, there's no way to toggle it for all users.

There is admittedly a bit of a functionality gap for view-only users - both for Audience Segmentation, and for KM as it stands. I have raised this with Mariya in a thread on the design doc. Seeing as KM already works like this I suspect we'll continue as specced for the most part, but it might be worth waiting for her thoughts on this before we finalise the AC here.

techanvil avatar Jan 30 '24 16:01 techanvil

Update: Due to ongoing changes to the design doc, we may well want to expand the scope of this issue to include the additional configuredAudiences setting.

Let's hold off until this additional thread on the design doc is also resolved.

techanvil avatar Jan 30 '24 19:01 techanvil

Update: With Evan having approved the changes, and Mariya already having given her general approval for the direction (so really just needing to sign off on the details), it's clear we will be including configuredAudiences in these settings and not continuing with the previous specced two tier settings approach.

Therefore, I have updated the datastore configuration in the design doc, and updated the Feature Description for this issue accordingly. @hussain-t, please note that the definition for setAudienceSegmentationWidgetHidden() has changed as a result for consistency with the configuredAudiences related functions. The GET:audience-settings endpoint definition has also been tweaked to include configuredAudiences.

Although there are still a few last details to approve in the threads linked in the previous two comments, I think we can now safely proceed with this issue as specified.

techanvil avatar Jan 31 '24 11:01 techanvil

Thanks, @techanvil 👍

hussain-t avatar Jan 31 '24 11:01 hussain-t

Thanks, @hussain-t and @techanvil. AC ✔️

eugene-manuilov avatar Jan 31 '24 17:01 eugene-manuilov

@hussain-t, as discussed on Slack the AC does need a small tweak:

We can't use getConfiguredAudiences() to return the value for isAudienceSegmentationWidgetHidden() because getConfiguredAudiences() only returns configuredAudiences and not isAudienceSegmentationWidgetHidden.

I've assigned this back to you in AC for an update.

techanvil avatar Jan 31 '24 17:01 techanvil

Thanks, @techanvil. I have updated the AC to retrieve the current value of the isAudienceSegmentationWidgetHidden setting directly via the GET audience-settings endpoint.

hussain-t avatar Feb 01 '24 05:02 hussain-t

Thanks @hussain-t, the AC LGTM :white_check_mark:

Assigning to you in IB as requested. Remember, we don't literally have to directly fetch the value from GET audience-settings in each selector, we can create additional Redux infrastructure as needed e.g. a common getAudienceSettings() and so forth.

techanvil avatar Feb 01 '24 10:02 techanvil

In includes/Modules/Analytics_4.php:

  • [ ] Create a constant for audience-settings with the value googlesitekit_audience_settings.
  • [ ] ...
  • [ ] In the create_data_request() method:
    • [ ] Add a case for the new GET:audience-settings endpoint to fetch audience user settings.
      • [ ] Use the User_Options::get() method to retrieve the googlesitekit_audience_settings option.
      • [ ] ...
    • [ ] Add a case for the new POST:audience-settings endpoint to update the user settings for audiences.
      • [ ] ...
      • [ ] Use the User_Options::set() method to update the googlesitekit_audience_settings option.
      • [ ] ...

@hussain-t, we should not use the User_Options class directly. Instead we need to create a new class that extends User_Setting in the Analytics_4 namespace and use it in the Analytics_4 module. Everything else is good.

eugene-manuilov avatar Feb 13 '24 18:02 eugene-manuilov

IB ✔️

eugene-manuilov avatar Feb 15 '24 20:02 eugene-manuilov

QA Update ✅

  • Tested on dev environment.
  • Verified all Actions and Selectors under QAB.
  • Verified all Actions and Selectors giving expected results.

Action: setConfiguredAudiences( audienceResourceNames: Array )

image

image

Action: setAudienceSegmentationWidgetHidden( isWidgetHidden: boolean )

image

image

Action: saveAudienceSettings()

Changed boolean value from true to false -

image

image

image

Change settings -

image

image

SELECTORS

Selector: getAudienceSettings(): Object

image

image

Selector: getConfiguredAudiences(): Array

image

Selector: isAudienceSegmentationWidgetHidden(): boolean

image

Selector: haveConfiguredAudiencesChanged(): boolean

image

On changing the configured audiences it return the value true -

image

mohitwp avatar Mar 27 '24 13:03 mohitwp