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

Add GA4 availableCustomDimensions module setting and relevant datastore infrastructure #7598

Closed
nfmohit opened this issue Sep 20, 2023 · 23 comments
Labels
Module: Analytics Google Analytics module related issues P0 High priority Type: Enhancement Improvement of an existing feature

Comments

@nfmohit
Copy link
Collaborator

nfmohit commented Sep 20, 2023

Feature Description

The analytics-4 module should have a new module setting added that will persistently store an array of custom dimensions (only parameter names, e.g. googlesitekit_post_author) that have been created/are available for the GA property, say availableCustomDimensions.

The intention is to use this module setting as a reference to check whether a certain custom dimension is available from the site’s database instead of the Google service, as this is a check that would be made quite frequently by the metric tiles that depend on custom dimensions.

Besides the module setting, datastore infrastructure should also be added that will populate and retrieve this module setting.

See relevant sections in design doc:

  1. Module setting
  2. Datastore infrastructure

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

Acceptance criteria

  • All the following changes should be made if the newsKeyMetrics feature flag is on.
  • A new module setting should be added to the analytics-4 module that should store an array of Site Kit-specific custom dimensions that have been created/are available for the connected GA property, say availableCustomDimensions.
    • The default value for this setting should be null.
    • Its expected format should either be null for default/unset, or an array of strings all starting with a googlesitekit_ prefix.
    • If the connected Analytics property (analytics-4 propertyID) is changed in Site Kit, this module setting should be reset to null.
  • Besides the other Analytics_4 datapoints (being implemented in #7573, another datapoint should be added as follows:
  • Besides the other datastore infrastructure for custom dimensions (being implemented in #7574), some more infrastructure should be added as follows:
    • syncAvailableCustomDimensions action that should interact with the POST:sync-custom-dimensions datapoint.
    • The default behaviour of the getAvailableCustomDimensions selector’s (available by default based on the module setting) resolver should be extended so that if the new availableCustomDimensions analytics-4 module setting is null and the current user has the MANAGE_OPTIONS capability, it should dispatch the syncAvailableCustomDimensions action, wait for it to complete, and return the updated module setting value.
    • hasCustomDimensions selector that should accept a custom dimension names name (string) or an array of custom dimension names (e.g. googlesitekit_post_categories). It should call the getAvailableCustomDimensions selector and check if the accepted name(s) exist in the array returned from the getAvailableCustomDimensions selector.
    • The createCustomDimension action (introduced in #7574) should be removed, however, the fetch store should be persisted.
    • createCustomDimensions action that should do the following:
      • Use the CORE_USER getKeyMetrics selector to get a list of the currently selected key metric widget tiles.
      • Find if any of the metric tiles require custom dimensions to function, and what those custom dimensions are. The required custom dimensions for a key metric tile should be available in its definition within the KEY_METRICS_WIDGETS constant (in assets/js/components/KeyMetrics/key-metrics-widgets.js at the time of writing) under a new requiredCustomDimensions array property.
      • Once the list of custom dimensions required by all selected metric tiles has been found, cross-check it with the list of available custom dimensions in the availableCustomDimensions analytics-4 module setting to find out what required custom dimensions have not been created yet.
      • Once the list of required custom dimensions that have not been created is found, use the createCustomDimension fetch store (introduced in #7574) to create them incrementally.
        • The structure for each possible custom dimensions creation request body are described below (also available in the design doc here):
          • googlesitekit_post_date:
            • parameterName: googlesitekit_post_date
            • displayName: WordPress Post Creation Date
            • description: Date of which this post was published
            • scope: event
          • googlesitekit_post_author:
            • parameterName: googlesitekit_post_author
            • displayName: WordPress Post Author
            • description: User ID of the author for this post
            • scope: event
          • googlesitekit_post_categories:
            • parameterName: googlesitekit_post_categories
            • displayName: WordPress Post Categories
            • description: Comma-separated list of category IDs assigned to this post
            • scope: event
          • googlesitekit_post_type:
            • parameterName: googlesitekit_post_type
            • displayName: WordPress Post Type
            • description: Content type for this post
            • scope: event
      • Once the process has completed, dispatch the syncAvailableCustomDimensions action to update the list of available custom dimensions.

Implementation Brief

  • If the newsKeyMetrics feature flag is enabled:
    • In includes/Modules/Analytics_4/Settings.php:
      • Add a new module setting named availableCustomDimensions having a default value of null.
      • In the get_sanitize_callback method, add a new case for this setting so that only the following are accepted as values:
        • null
        • Empty array.
        • Array with strings, with each string having a googlesitekit_ prefix.
    • In includes/Modules/Analytics_4.php:
      • In the register method, update the callback function for the update_option_googlesitekit_analytics-4_settings hook such that when the propertyID module setting changes to a new value, the availableCustomDimensions module setting should be reset to null.
      • In the get_datapoint_definitions method, add a new POST datapoint called sync-custom-dimensions which should use the analyticsadmin service.
      • In the create_data_request method:
        • Add a case for the new POST:sync-custom-dimensions datapoint.
        • Use the Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\Resource\PropertiesCustomDimensions::listPropertiesCustomDimensions method passing the propertyID module setting to get a list of the available custom dimensions for the GA4 property.
      • In the parse_data_response method:
        • Add a case for the new POST:sync-custom-dimensions datapoint.
        • The listPropertiesCustomDimensions method response should include an array of objects for the custom dimensions available for the GA4 property.
        • Filter the array to find out custom dimension objects that have the googlesitekit_ prefix in the parameterName property and has an EVENT scope.
        • Update the availableCustomDimensions module setting with an array of matching parameterName (string) values, or an empty array if there are no available or matching custom dimensions.
    • In assets/js/modules/analytics-4/datastore/base.js:
      • Include availableCustomDimensions as the module setting in the datastore.
    • In assets/js/modules/analytics-4/datastore/custom-dimensions.js:
      • Create a new fetch store named syncAvailableCustomDimensions that should interact with the POST:sync-custom-dimensions datapoint.
      • Create a new action named syncAvailableCustomDimensions that should interact with the fetch store with the same name.
      • Even though the createModuleStore utility would automatically provide infrastructure to retrive the value of the availableCustomDimensions module setting, add a resolver named getAvailableCustomDimensions that should extend this default behaviour.
        • If the value of the availableCustomDimensions module setting is null, the current user is authenticated and has the MANAGE_OPTIONS capability, dispatch the syncAvailableCustomDimensions action to update the module setting, wait for it to complete, and return the updated module setting value.
        • Otherwise, return whatever is the module setting value.
      • Create a new selector named hasCustomDimensions that should return if one or more passed custom dimensions are available for the connected GA4 property. It should accept a string as a custom dimension name, or an array of one or more custom dimension names. It should call the automatically available getAvailableCustomDimensions selector, check if the accepted name(s) exist in the array returned and return true if so, otherwise return false. If the selector is used to check if multiple custom dimensions are available, it should return true only if ALL of them are available.
      • Create a new action named createCustomDimensions that should do the following:
        • Use the CORE_USER getKeyMetrics selector to get a list of the currently selected key metric widget tiles.
        • Find if any of the metric tiles require custom dimensions to function, and what those custom dimensions are. The required custom dimensions for a key metric tile should be available in its definition within the KEY_METRICS_WIDGETS constant (in assets/js/components/KeyMetrics/key-metrics-widgets.js at the time of writing) under a new requiredCustomDimensions array property.
        • Once the list of custom dimensions required by all selected metric tiles has been found, cross-check it with the list of available custom dimensions in the availableCustomDimensions module setting to find out what required custom dimensions have not been created yet.
        • Once the list of required custom dimensions that have not been created is found, use the createCustomDimension fetch store to create each of them.
          • The structure for each possible custom dimension is mentioned in the IB and can be stored as an array of objects in assets/js/modules/analytics-4/datastore/constants.js so that it can be referenced in the code, stories, and tests.
        • Once the fetch requests for all required custom dimensions have completed, dispatch the syncAvailableCustomDimensions action to update the list of available custom dimensions.

Test Coverage

  • In tests/phpunit/integration/Modules/Analytics_4/SettingsTest.php:
    • Update test cases to reflect the addition of the new module setting and its sanitization.
  • In tests/phpunit/integration/Modules/Analytics_4Test.php:
    • Add test case to validate that availableCustomDimensions module setting is reset to null when propertyID is changed.
    • Update test case(s) to reflect the addition of the POST:sync-custom-dimensions datapoint.
    • Add test case(s) for any new methods added.
  • In assets/js/modules/analytics-4/datastore/custom-dimensions.test.js:
    • Add test case for the syncAvailableCustomDimensions action.
    • Add test case for the getAvailableCustomDimensions selector to verify that the extended resolver works to update the availableCustomDimensions module setting when it is null.
    • Add test case for the hasCustomDimensions selector.
    • Add test case for the createCustomDimensions action.

QA Brief

  • Make sure you have Site Kit set up with Google Analytics 4.
  • Ensure the newsKeyMetrics feature flag is enabled.
  • Set up Key Metric widgets. Select Least engaging pages and Pages per visit widgets from the selection panel. For testing purposes, these widgets have the required custom dimensions.
  • Open the developer tools in your browser and navigate to the console tab.
  • Execute the following newly added custom dimensions actions and selectors in the console.

Selectors

1. getAvailableCustomDimensions

  • Purpose: This selector retrieves available custom dimensions.
  • Console Command:
    googlesitekit.data.select('modules/analytics-4').getAvailableCustomDimensions()
  • Tests:
    • If custom dimensions are not set (null), invoking this selector should trigger a network request to the sync-custom-dimensions datapoint and update the available custom dimensions.
    • If custom dimensions are already available in the state, no network request should be triggered.

2. hasCustomDimensions

  • Purpose: Determines if a given list (or single string) of custom dimensions is available. Use the parameterName property of the custom dimension object (AC) to check its availability.
  • Console Command:
    googlesitekit.data.select('modules/analytics-4').hasCustomDimensions( yourDimensionArrayOrString )
  • Tests:
    • Pass a single custom dimension string and ensure it checks its availability correctly.
    • Pass an array of custom dimensions and ensure all of them are checked.
    • The selector should return false if any of the given dimensions is unavailable.

Actions

1. syncAvailableCustomDimensions

  • Purpose: Synchronizes available custom dimensions.
  • Console Command:
    googlesitekit.data.dispatch('modules/analytics-4').syncAvailableCustomDimensions( yourGA4PropertyID )
  • Tests:
    • On dispatching, it should make a network request to the sync-custom-dimensions datapoint.
    • The datastore should be updated with the newly fetched custom dimensions if any.

2. createCustomDimensions

  • Purpose: This action ensures that required custom dimensions are present, and if not, creates them.
  • Console Command:
    googlesitekit.data.dispatch('modules/analytics-4').createCustomDimensions()
  • Tests:
    • Before dispatching, ensure that googlesitekit_post_author and requiredCustomDimensions custom dimensions are not available for the GA4 property.
    • On dispatching, the action should make a network request(s) to create missing custom dimensions.
    • If two custom dimensions are missing, two network requests should be made to the create-custom-dimension datapoint.
    • A network call to the sync-custom-dimensions datapoint should be made after the custom dimensions are created.
    • The datastore should reflect these changes and show newly created dimensions upon invoking the getAvailableCustomDimensions selector.

Specific Scenarios

  • Create some custom dimensions in the current GA4 property if they don't exist already.
  • Go to the settings page and change the GA4 property.
  • Ensure the custom dimensions are reset to null in the state.
  • Upon invoking the getAvailableCustomDimensions selector, ensure that the custom dimensions are fetched again if they are null.

Changelog entry

  • Add GA4 availableCustomDimensions module setting and relevant datastore infrastructure.
@nfmohit nfmohit added Type: Enhancement Improvement of an existing feature Module: Analytics Google Analytics module related issues labels Sep 20, 2023
@nfmohit
Copy link
Collaborator Author

nfmohit commented Sep 20, 2023

To clarify, even though this issue says that it is blocked by #7114, this can still be worked on and carried forward. It's just that without #7114, this module setting won't be available for view-only users, and thus SK will not be able to make report requests for them without seeing if the custom dimensions are available via the module setting. It will still work for regular users.

CC: @bethanylang

@nfmohit nfmohit added the P0 High priority label Sep 20, 2023
@bethanylang
Copy link
Collaborator

Also flagging the above comment for @wpdarren and @mohitwp as this will impact QA on this issue!

@nfmohit nfmohit assigned nfmohit and aaemnnosttv and unassigned nfmohit Sep 21, 2023
@nfmohit
Copy link
Collaborator Author

nfmohit commented Sep 21, 2023

I've assigned this to @aaemnnosttv for review as Evan had some final comments in the design doc about this section and I didn't want to hold off adding ACs here until those were resolved.

@nfmohit nfmohit assigned nfmohit and aaemnnosttv and unassigned aaemnnosttv and nfmohit Sep 21, 2023
@aaemnnosttv
Copy link
Collaborator

@nfmohit the first point reads good to me, especially the detail about resetting this if the connected property changes, which is indeed important and something I hadn't considered yet 👍

The second point reads a bit closer to an IB. As far as AC is concerned, we're mainly concerned with the interfaces of selectors and actions. Additional thoughts:

  • A availableCustomDimensions key should be added to the initialState with a default value of undefined.

We shouldn't need to add this to state separately – it should remain as part of the settings state as any other module setting. The main difference that we want to call for is a change to extend the default behavior of its resolver. If the setting's value is null and the user can MANAGE_OPTIONS (requires authentication) then it should call the sync action.

  • syncAvailableCustomDimensions action that should call the getCustomDimensions selector and parse its response to look for custom dimensions that start with the googlesitekit_ prefix. It will then update the availableCustomDimensions module setting with an array of the parameter names of matching custom dimensions.

This should always call the fetch action to list custom dimensions rather than use a selector. I think it should also save the updated settings. It might be simpler to implement this as a POST:sync-custom-dimensions endpoint which would request the dimensions on the backend, process and return the full updated settings. WDYT?

  • hasCustomDimensions selector that should accept an array of parameter names (e.g. googlesitekit_post_categories) of custom dimensions

We can also refer to these as custom dimension names since they're 1:1 with their parameters. As an aside, it might be nice to allow for using the selector with a single dimension as we'll have cases for using it in both ways.

@aaemnnosttv aaemnnosttv assigned nfmohit and unassigned aaemnnosttv Sep 21, 2023
@nfmohit
Copy link
Collaborator Author

nfmohit commented Sep 21, 2023

I have updated the ACs (and relevant parts of the design doc) accordingly, thank you @aaemnnosttv. With a new POST:sync-custom-dimensions endpoint, I think this makes the GET:custom-dimensions datapoint and getCustomDimensions selector redundant (being added in #7573). But I think let's not change the ACs for that issue anymore and we can clean it up later. What do you think?

@nfmohit nfmohit assigned aaemnnosttv and unassigned nfmohit Sep 21, 2023
@aaemnnosttv
Copy link
Collaborator

@nfmohit the pattern of calling the GET endpoint to list all the custom dimensions in the resolver of the selector is consistent with the approach we use during module setup, where we're configuring the state of the module before saving at the end. These datapoints usually also require the account and other IDs needed to make these requests because the module isn't connected yet. In this case, we really just want to call out to the API to update the list of dimensions we're keeping track of in module settings for the connected property.

If we don't have any other need for GET:custom-dimensions then we shouldn't add it until we do.

@nfmohit
Copy link
Collaborator Author

nfmohit commented Sep 22, 2023

If we don't have any other need for GET:custom-dimensions then we shouldn't add it until we do.

I agree, thank you @aaemnnosttv. I have updated the ACs for #7573 & #7574 to remove the requirement for the datapoint and its relevant selectors.

@aaemnnosttv
Copy link
Collaborator

AC LGTM @nfmohit – some of it borders on IB territory but we do have some technical ACs sometimes so it's not a big deal.

A few thoughts:

  • createCustomDimension this action probably isn't necessary since it's mostly an alias for the underlying fetch action and I don't think it would be needed outside of this action
  • createCustomDimensions is only 1 character different which can be easy to miss if both actions were to exist. It's less of a concern if the above action goes away and we just use the fetch action internally. Otherwise, I'd make the name a bit more explicit such as createAllRequiredCustomDimensions, something like that.

This doesn't block this from moving forward though, so I'll send back to you for any potential revisions, but aside from the above, this is ✅

@hussain-t hussain-t removed their assignment Oct 6, 2023
eugene-manuilov added a commit that referenced this issue Oct 6, 2023
…imensions-module-setting

Enhance/#7598 - Add GA4 `availableCustomDimensions` module setting and relevant datastore infrastructure
@mohitwp mohitwp self-assigned this Oct 9, 2023
@hussain-t hussain-t assigned hussain-t and unassigned mohitwp Oct 9, 2023
@hussain-t hussain-t removed their assignment Oct 9, 2023
@aaemnnosttv aaemnnosttv self-assigned this Oct 9, 2023
@aaemnnosttv
Copy link
Collaborator

I was curious to test this out, and have just tried creating a custom dimension with a duplicate name but different scope - the GA4 API actually does allow it in my testing

Thanks for checking @techanvil that's good to know. I believe this is due to being passed through different parts of the config so they're not actually competing for the same parameters as I had thought. See https://support.google.com/analytics/answer/12370404?sjid=8553378467151786531-NA#zippy=%2Cgoogle-tag-websites:~:text=Use%20the%20following%20drop%2Ddowns%20to%20see%20information%20about%20how%20to%20set%20user%20properties

--

This one is ready for QA again now that the follow-up PR has been merged.

@mohitwp
Copy link
Collaborator

mohitwp commented Oct 20, 2023

QA Update ⚠️

@nfmohit @hussain-t

As per AC - The createCustomDimension action (introduced in https://github.com/google/site-kit-wp/issues/7574) should be removed, however, the fetch store should be persisted. and in QAB mentioned selector for creating custom dimension is googlesitekit.data.dispatch('modules/analytics-4').createCustomDimensions()`

But when I tested I found that above selector is not working for creating Custom dimensions.
I used below code as per QAB-

googlesitekit.data.dispatch('modules/analytics-4').createCustomDimensions( "412625154",
{
    "description": "User ID of the author for this post",
    "disallowAdsPersonalization": false,
    "displayName": "WordPress Post Author",
    "parameterName": "googlesitekit_post_author",
    "scope": "EVENT"
  })

`
Above code is not creating any custom dimension and no request to create custom dimension endpoint is made.

image

But when I used selector which is mentioned under (introduced in #7574) then custom dimension request made successfully and custom dimesnsion created successfully.

googlesitekit.data.dispatch('modules/analytics-4').fetchCreateCustomDimension("412625154",
  {
    "description": "Custom Dimension Description",
    "disallowAdsPersonalization": false,
    "displayName": "Custom Dimension",
    "parameterName": "googlesitekit_post_author",
    "scope": "EVENT"
  })

It is conflicting with QAB and AC so Can you please confirm that fetchCreateCustomDimension() is correct action for creating custom dimension?

image

@hussain-t
Copy link
Collaborator

hussain-t commented Oct 20, 2023

I used below code as per QAB-
googlesitekit.data.dispatch('modules/analytics-4').createCustomDimensions( "412625154", {... })

@mohitwp, It appears there might be a misunderstanding. The createCustomDimensions action is designed to function without any arguments, as the QAB precisely describes.

But when I tested I found that above selector is not working for creating Custom dimensions.

The createCustomDimensions action internally checks if custom dimensions are already available for the specified GA4 property. If they exist, an API call to recreate them isn't made. It's worth noting that for domains like oi.ie, all necessary custom dimensions are already present. This could explain the behavior you observed. Refer to the snapshot for clarity:

Screenshot 2023-10-20 at 3 00 14 PM

But when I used selector which is mentioned under (introduced in #7574) then custom dimension request made successfully and custom dimesnsion created successfully.

Yes, this fetchCreateCustomDimension action creates a custom dimension. We internally use it in the createCustomDimensions to create custom dimensions when they are missing.

@hussain-t hussain-t removed their assignment Oct 23, 2023
@mohitwp
Copy link
Collaborator

mohitwp commented Oct 30, 2023

QA Update ⚠️

  • Tested on dev environment.
  • Verified that if feature flag is not enabled then selectors and actions are not getting trigger.\
  • Verified both selector and trigger as per AC.
  • Done some additional testing around custom dimensions.

@hussain-t

Question
As per QAB - For The selector should return false if any of the given dimensions is unavailable. As you can see in below image test dimension is not available but response showing true because other two dimensions are available. Is this correct ?

image

Pass Cases -

  1. getAvailableCustomDimensions
    Verified that if feature flag is not enabled then selectors not getting trigger.
    Verified if Feature flag enabled and Analytics already have custom dimension.
    Verified that no request trigger when custom dimensions are not set to Null

image

image

image

  1. hasCustomDimensions
  • Passed a single custom dimension string and verified checks its availability correctly.
  • Pass an array of custom dimensions and verified all of them are checked.

image

  1. syncAvailableCustomDimensions
  • As confimed with @nfmohit . Action code mentioned under QAB is not correct.
  • Correct action is googlesitekit.data.dispatch('modules/analytics-4').fetchAvailableCustomDimensions( )
  • Verified that Sync action is working as per AC. Sync dimension request gets trigger successfully and display newly added custom dimensions.

image

image

image

  1. createCustomDimensions

image

image

  1. When I tried to create duplicate custom dimension with all same details.

image

@hussain-t
Copy link
Collaborator

As per QAB - For The selector should return false if any of the given dimensions is unavailable. As you can see in below image test dimension is not available but response showing true because other two dimensions are available. Is this correct ?

@mohitwp, I see where the confusion is coming from. When you pass the categories as individual arguments (strings), the selector evaluates based on the first argument. Since your first dimension is valid, it returns true.

To validate multiple dimensions together, you should pass them as an array:

googlesitekit.data.select('modules/analytics-4').hasCustomDimensions( ['googlesitekit_post_author', 'googlesitekit_post_categories', 'test'] )

By using the array format, it will correctly check if all dimensions within the array are available before returning a result.

@mohitwp
Copy link
Collaborator

mohitwp commented Oct 30, 2023

Thanks @hussain-t !

QA Update ✅

  • Tested on dev environment.
  • Verified that if feature flag is not enabled then selectors and actions are not getting trigger.\
  • Verified both selector and trigger as per AC.
  • Done some additional testing around custom dimensions.

Pass Cases -

  1. getAvailableCustomDimensions
    Verified that if feature flag is not enabled then selectors not getting trigger.
    Verified if Feature flag enabled and Analytics already have custom dimension.
    Verified that no request trigger when custom dimensions are not set to Null

image

image

image

  1. hasCustomDimensions
  • Passed a single custom dimension string and verified checks its availability correctly.
  • Pass an array of custom dimensions and verified all of them are checked.

image

image

  1. syncAvailableCustomDimensions
  • As confimed with @nfmohit . Action code mentioned under QAB is not correct.
  • Correct action is googlesitekit.data.dispatch('modules/analytics-4').fetchAvailableCustomDimensions( )
  • Verified that Sync action is working as per AC. Sync dimension request gets trigger successfully and display newly added custom dimensions.

image

image

image

  1. createCustomDimensions

image

image

  1. When I tried to create duplicate custom dimension with all same details.

image

@aaemnnosttv
Copy link
Collaborator

Question
As per QAB - For The selector should return false if any of the given dimensions is unavailable. As you can see in below image test dimension is not available but response showing true because other two dimensions are available. Is this correct ?

@mohitwp – I see @hussain-t explained this in his previous comment so it looks like this was just accidentally copied in your last update as it seems like this was the only clarification needed from your previous pass.

I double checked this and it does work as @hussain-t explained 👍

@mohitwp
Copy link
Collaborator

mohitwp commented Nov 7, 2023

Thank you @aaemnnosttv !
Yes, It is copied by mistake. Updated my comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Module: Analytics Google Analytics module related issues P0 High priority Type: Enhancement Improvement of an existing feature
Projects
None yet
Development

No branches or pull requests

8 participants