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

Create API endpoints and Redux store infrastructure for "Enable enhanced measurement" feature. #7458

Closed
techanvil opened this issue Aug 18, 2023 · 6 comments
Labels
Module: Analytics Google Analytics module related issues P1 Medium priority Type: Enhancement Improvement of an existing feature

Comments

@techanvil
Copy link
Collaborator

techanvil commented Aug 18, 2023

Feature Description

API endpoints and Redux store infrastructure should be created as required to get/set enhanced measurement settings and thus toggle the enhanced measurement enabled state for a given web data stream. It should be possible to update the local settings state, check if settings have changed locally and persist or reset the changes.


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

Acceptance criteria

API Endpoints

  • New GET/POST datapoints named enhanced-measurement-settings should be created in the analytics-4 module.
  • The GET endpoint:
  • The POST endpoint:
  • They both return the result of their GA4 API method call, which in the happy path will be an EnhancedMeasurementSettings JSON object.

Redux store

  • The following actions and selectors should be added to to the analytics-4 module. Additional store infrastructure should be added as necessary to support these top-level requirements:
    • Actions:
      • setEnhancedMeasurementStreamEnabled( propertyID: string, webDataStreamID: string, enabled: boolean )
        • Toggles the client-side state of enhanced measurement enablement (i.e. the streamEnabled setting) for the given web data stream.
      • updateEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string )
        • This results in a POST to the enhanced-measurement-settings endpoint to persist the changes for the given web data stream.
      • resetEnhancedMeasurementSettings()
        • Resets client-side enhanced measurement settings for all web data streams to their initial state.
    • Selectors:
      • isEnhancedMeasurementStreamEnabled( propertyID: string, webDataStreamID: string ): boolean
        • Retrieves the client-side state of enhanced measurement enablement (i.e. the streamEnabled setting) for the given web data stream.
        • If the state is not yet defined, this will result in a GET to the enhanced-measurement-settings endpoint to populate the initial state.
      • haveEnhancedMeasurementSettingsChanged( propertyID: string, webDataStreamID: string ): boolean
        • Checks if there are unsaved client-side changes to enhanced measurement settings for the given web data stream.

Implementation Brief

IB Note: A PoC has been created, notably resolving some of the more obscure aspects of how to call the GA4 API methods using the google-api-php-client-services PHP package.

API endpoints

Note: Although the google-api-php-client-services PHP package did at one point provide support for calling the GA4 getEnhancedMeasurementSettings and updateEnhancedMeasurementSettings methods, it was removed back in version 0.221, and since then the package has gone on to support the v1beta version of the API which doesn't provide these methods at all.

Therefore, in order to provide support for these methods, we need to reimplement some code from v0.220.0 of the package. Fortunately, although support was removed from the GoogleAnalyticsAdmin service and PropertiesWebDataStreams resource classes, the package does still provide the underlying GoogleAnalyticsAdminV1alphaEnhancedMeasurementSettings model class, which we can utilise in the following implementation.

Restore support for calling the GA4 API methods

  • Within the namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin:
    • Create a new class PropertiesEnhancedMeasurementResource which extends Google\Site_Kit_Dependencies\Google\Service\Resource.
      • See AccountsResource for an example of a class where we extend Resource.
      • Copy the getEnhancedMeasurementSettings() and updateEnhancedMeasurementSettings() methods into this class from v0.220.0 of PropertiesWebDataStreams.
    • Create a new class PropertiesEnhancedMeasurementService which extends Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin.
      • See AccountProvisioningService for an example of a class where we extend GoogleAnalyticsAdmin.
      • Add a constructor and copy the necessary parts from v0.220.0 of GoogleAnalyticsAdmin to provide support for getEnhancedMeasurementSettings and updateEnhancedMeasurementSettings - but using our own PropertiesEnhancedMeasurementResource in place of PropertiesWebDataStreams.
    • Make any tweaks as necessary/reasonable to align the code with our coding conventions.
    • This is fleshed out in the PoC, but watch out for minor naming adjustments (PropertiesEnhancedMeasurementsResource -> PropertiesEnhancedMeasurementResource etc.)

Add our own REST endpoints

  • Within the Analytics_4 class:
    • In setup_services(), add a new service called analyticsenhancedmeasurement which is an instance of PropertiesEnhancedMeasurementService.
    • In get_datapoint_definitions(), add GET and POST datapoints called enhanced-measurement-settings.
      • These should both depend on the analyticsenhancedmeasurement service.
      • The POST datapoint should require the https://www.googleapis.com/auth/analytics.edit scope.
    • In create_data_request(), add cases for the new GET and POST datapoints.
    • This is also fleshed out in the PoC, which can be referred to for more detail.

Redux store

  • Create a new store definition in assets/js/modules/analytics-4/datastore/enhanced-measurement.js.
    • This should be merged with the main GA4 store definition in assets/js/modules/analytics-4/datastore/index.js.
    • The initial shape should be as follows.
{
  enhancedMeasurement: {
  }
}
  • Note that when populated it will have the following shape (note that savedSettings may be omitted if the store hasn't been populated via a call to the GET endpoint). See EnhancedMeasurementSettings.
{
  // 12345 = property ID, 67890 = web data stream ID.
  enhancedMeasurement: {
    12345: {
      67890: {
        settings: Partial<EnhancedMeasurementSettings>,
        savedSettings: EnhancedMeasurementSettings
      }
    }
  }
}
  • Create a new fetch store for each of the new GET and POST endpoints, with respective basenames getEnhancedMeasurementSettings and updateEnhancedMeasurementSettings.
  • These can share the same reducer. The reducer should set both settings and savedSettings to be the EnhancedMeasurementSettings JSON object returned by the endpoint.
  • Add the following actions and selectors. Implement the reducer and resolvers as required.

Actions

  • setEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string, settings Partial<EnhancedMeasurementSettings> )
    • This should result in the settings state for the given propertyID and webDataStreamID being updated with the provided object. The given object should entirely replace the current state.
    • Note that a partial may be provided, this is to cater for the fact we may want to update some client-side settings (e.g. streamEnabled) without first retrieving the current settings from the backend, and it would not make sense to attempt to provide all of the settings in this case as we wouldn't have a meaningful value for them.
  • setEnhancedMeasurementStreamEnabled( propertyID: string, webDataStreamID: string, enabled: boolean )
    • This is a convenience action that should toggle the streamEnabled setting for the data stream, via a retrieval of the current settings, inversion of the streamEnabled flag, and subsequent dispatch of the setEnhancedMeasurementSettings() action.
  • updateEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string )
    • This should call the fetch store generated action fetchUpdateEnhancedMeasurementSettings() with the current settings state for the given propertyID and webDataStreamID, thus updating the settings via the GA4 API.
  • resetEnhancedMeasurementSettings()
    • This should result in the settings state for all web data streams being reverted to the corresponding savedSettings state. If savedSettings is not defined, the state for the given web data stream should be entirely removed.

Selectors

  • getEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string ): EnhancedMeasurementSettings
    • This should return the current settings state for the given propertyID and webDataStreamID.
    • There should be a resolver for this selector where, if the state for given propertyID and webDataStreamID is not yet defined, the fetch store generated action fetchGetEnhancedMeasurementSettings() is dispatched to populate the settings from the GA4 API.
  • isEnhancedMeasurementStreamEnabled( propertyID: string, webDataStreamID: string ): boolean
    • This is a convenience selector that retrieves the current settings state via getEnhancedMeasurementSettings() and returns the streamEnabled property.
  • haveEnhancedMeasurementSettingsChanged( propertyID: string, webDataStreamID: string ): boolean
    • This should return the result of a comparison of the settings and savedSettings state for the given propertyID and webDataStreamID.

Again, some of the Redux implementation has been fleshed out in the PoC.

Test Coverage

  • Add PHPUnit test coverage for the new datapoints.
  • Add JS unit tests for the new Redux actions & selectors.

QA Brief

  • Test the behavior of the GoogleTagIDMismatchNotification banner and ensure no regression occurred. This is due to the changes made to the getAnalyticsConfigByMeasurementIDs selector used in the banner.
  • Make sure you have Site Kit set up with Google Analytics 4.
  • Open the developer tools in your browser and navigate to the console tab.
  • Execute the following newly added enhanced measurement settings actions and selectors in the console.

Actions

Action: setEnhancedMeasurementStreamEnabled( propertyID: string, webDataStreamID: string, enabled: boolean )

  1. Execute the action by running the following command:

    googlesitekit.data.dispatch('modules/analytics-4').setEnhancedMeasurementStreamEnabled('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID', true)

    Replace YOUR_PROPERTY_ID and YOUR_WEBDATASTREAM_ID with actual values.

  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').isEnhancedMeasurementStreamEnabled('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID')

    It should return true.

Action: updateEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string )

  1. Execute the following selector to get the current settings:

    googlesitekit.data.select('modules/analytics-4').getEnhancedMeasurementSettings('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID')
  2. Execute the action by running the following command:

    googlesitekit.data.dispatch('modules/analytics-4').updateEnhancedMeasurementSettings('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID')
  3. Check the network tab for a POST request to enhanced-measurement-settings and verify that it contains the correct data and that the response is a 200 OK.

Action: resetEnhancedMeasurementSettings()

  1. Execute the action by running the following command:

    googlesitekit.data.dispatch('modules/analytics-4').resetEnhancedMeasurementSettings()
  2. Use the selector to check if the settings have indeed been reset.

    googlesitekit.data.select('modules/analytics-4').isEnhancedMeasurementStreamEnabled('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID')

    It should return the initial state.

Selectors

Selector: getEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string ): EnhancedMeasurementSettings

  1. Execute the selector:

    googlesitekit.data.select('modules/analytics-4').getEnhancedMeasurementSettings('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID')
  2. Verify that it either returns the correct EnhancedMeasurementSettings object or triggers a GET request to enhanced-measurement-settings if the state is not yet defined.

Selector: isEnhancedMeasurementStreamEnabled( propertyID: string, webDataStreamID: string ): boolean

  1. Execute the selector:

    googlesitekit.data.select('modules/analytics-4').isEnhancedMeasurementStreamEnabled('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID')
  2. Verify that it either returns the correct boolean value.

Selector: haveEnhancedMeasurementSettingsChanged( propertyID: string, webDataStreamID: string ): boolean

  1. Execute the selector:

    googlesitekit.data.select('modules/analytics-4').haveEnhancedMeasurementSettingsChanged('YOUR_PROPERTY_ID', 'YOUR_WEBDATASTREAM_ID')
  2. Verify that it returns true if there are unsaved changes and false otherwise.

Changelog entry

  • Create API endpoints and Redux store infrastructure for "Enable enhanced measurement" feature.
@aaemnnosttv
Copy link
Collaborator

aaemnnosttv commented Aug 18, 2023

Thanks @techanvil !

There are a few inconsistencies in the naming EnhancedMeasurements vs EnhancedMeasurement (I think we should align on the later`).

  • getEnhancedMeasurementsEnabled( propertyID: string, webDataStreamID: string ): boolean

For selectors returning a boolean, these are usually starting with is|are|has rather than get. For this selector, we might want to make it a bit more flexible to allow for checking other settings as well since we're likely looking to consider more than just streamEnabled. This could also be a different selector if we want to have one specific to checking whether or not EM is enabled although this setting is just one of the many settings for EM so we probably don't need to make it a special case.

Perhaps more importantly, we should think about which of the sub-settings are important to us if not "all" since EM can be enabled while all of the additional measurements it has can be disabled which would be silly but possible. Similarly, what if some of the sub-settings are turned off? Does "enabled" for us mean everything is enabled or are there certain events that are important?

  • saveEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string )

We tend to keep the naming for these kinds of actions close to the underlying endpoint, so it might be more consistent to use updateEnhancedMeasurementSettings instead.

@aaemnnosttv aaemnnosttv added the Module: Analytics Google Analytics module related issues label Aug 18, 2023
@techanvil techanvil added the P0 High priority label Aug 21, 2023
@techanvil techanvil self-assigned this Aug 21, 2023
@mxbclang mxbclang added P1 Medium priority and removed P0 High priority labels Aug 21, 2023
@techanvil
Copy link
Collaborator Author

techanvil commented Aug 21, 2023

Thanks for taking a look at this @aaemnnosttv.

There are a few inconsistencies in the naming EnhancedMeasurements vs EnhancedMeasurement (I think we should align on the later`).

  • getEnhancedMeasurementsEnabled( propertyID: string, webDataStreamID: string ): boolean

For selectors returning a boolean, these are usually starting with is|are|has rather than get.

Both good points, thanks! I've updated accordingly.

For this selector, we might want to make it a bit more flexible to allow for checking other settings as well since we're likely looking to consider more than just streamEnabled. This could also be a different selector if we want to have one specific to checking whether or not EM is enabled although this setting is just one of the many settings for EM so we probably don't need to make it a special case.

Based on #7459 and the edge-case handling issues currently in Triage (#7475, #7476 and #7477, see below) I think that to start with we'll just be needing to check two values: one, the simple state of streamEnabled, and two, the computed state of whether any of the events are enabled.

To provide a streamlined API in keeping with other similar cases where we provide individual getter/setter selectors and actions for an underlying collection (e.g. various settings, site info etc), I've renamed getEnhancedMeasurementsEnabled() / setEnhancedMeasurementsEnabled() to isEnhancedMeasurementStreamEnabled() / setEnhancedMeasurementStreamEnabled(), with the idea being that we can add more individual getter/setters as we need them. The implication is that there will be a general purpose getEnhancedMeasurementSettings() selector/resolver behind the scenes to support these, but I didn't want to spec that in the AC, rather keep it as an IB detail.

My thinking is that we should also spec a hasSomeEnhancedMeasurementEventsEnabled() selector as part of #7475.

Perhaps more importantly, we should think about which of the sub-settings are important to us if not "all" since EM can be enabled while all of the additional measurements it has can be disabled which would be silly but possible. Similarly, what if some of the sub-settings are turned off? Does "enabled" for us mean everything is enabled or are there certain events that are important?

Thanks for pointing this out, you've made me realise there are some edge cases that we need to determine the approach for. I've re-read the related discussion on Asana, and given it some further thought. As a result, and as mentioned above I've created three additional issues which are currently in Triage where we can flesh out the approach to these cases.

  • saveEnhancedMeasurementSettings( propertyID: string, webDataStreamID: string )

We tend to keep the naming for these kinds of actions close to the underlying endpoint, so it might be more consistent to use updateEnhancedMeasurementSettings instead.

Sounds good - I did take a look to see if I could spot anything analagous naming wise, but nothing stood out at the time so I went with the common get/save combination. However, as a principle that certainly SGTM and I've made the change to updateEnhancedMeasurementSettings().

@techanvil techanvil changed the title Create API endpoints and Redux store infrastructure for "Enable enhanced measurements" feature. Create API endpoints and Redux store infrastructure for "Enable enhanced measurement" feature. Aug 21, 2023
@techanvil techanvil removed their assignment Aug 22, 2023
@tofumatt tofumatt self-assigned this Aug 23, 2023
@tofumatt
Copy link
Collaborator

This looks good, and I know there's an existing proof-of-concept PR here, but this is a pretty involved issue so I'm gonna up the estimate a bit just to be safe.

IB ✅

@techanvil
Copy link
Collaborator Author

techanvil commented Sep 12, 2023

Hi @hussain-t, as discussed, I've assigned this back to you because while working on #7459, having branched off your branch, I've noticed an issue that I/we didn't spot during the PoC, IB or initial execution.

The problem is that the GoogleAnalyticsAdminV1alphaEnhancedMeasurementSettings model class that the apiclient-services package provides has evidently been generated from an old version of the API, as it is not fully aligned with the current EnhancedMeasurementSettings schema.

As can be seen in the version of GoogleAnalyticsAdminV1alphaEnhancedMeasurementSettings that we're using - which also appears unchanged even in the latest version of apiclient-services - it provides a couple of fields that are no longer in use, pageLoadsEnabled and pageViewsEnabled, and is also missing the formInteractionsEnabled field.

Although you've implemented this issue as per the spec, we should address this missing aspect here in order to be able to QA this properly, as this inconsistent model can otherwise cause problems when testing.

As per our conversation, the solution should be quite straightforward, we simply need to use our own model implementation that's essentially a copy of GoogleAnalyticsAdminV1alphaEnhancedMeasurementSettings with the correct set of fields.

As an aside, thanks for jumping on a call so late to discuss this, definitely above and beyond the call of duty but certainly appreciated :)

@hussain-t
Copy link
Collaborator

Thanks, @techanvil. The above issue has been addressed.

@hussain-t hussain-t removed their assignment Sep 13, 2023
@techanvil techanvil assigned techanvil and hussain-t and unassigned techanvil Sep 13, 2023
@hussain-t hussain-t assigned techanvil and unassigned hussain-t Sep 13, 2023
techanvil added a commit that referenced this issue Sep 14, 2023
…asurement

Infrastructure/#7458 - Create API endpoints and Redux store infrastructure for "Enable enhanced measurement" feature
@techanvil techanvil removed their assignment Sep 14, 2023
@mohitwp mohitwp self-assigned this Sep 14, 2023
@wpdarren wpdarren assigned wpdarren and unassigned mohitwp Sep 18, 2023
@wpdarren
Copy link
Collaborator

wpdarren commented Sep 19, 2023

QA Update: ✅

Verified:

Checked with Hussain via Slack - this is to be tested outside of the enhancedMeasurement feature flag.

  • Completed regression testing on the behavior of the GoogleTagIDMismatchNotification banner to ensure no regression occurred. Worked as expected.

image

  • I went through all of the steps in the QAB, and verified the points highlighted when the code was run.
  • Confirmed the network output for enhanced-measurement-settings with code 200. Details were correct.
  • Confirmed that Enhanced Measurement was enabled in my Analytics account.
Screenshots

image
image
image
image

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 P1 Medium priority Type: Enhancement Improvement of an existing feature
Projects
None yet
Development

No branches or pull requests

7 participants