diff --git a/assets/js/modules/analytics-4/components/common/EnhancedMeasurementSwitch.js b/assets/js/modules/analytics-4/components/common/EnhancedMeasurementSwitch.js index 2f1a6163f4c..a724605a159 100644 --- a/assets/js/modules/analytics-4/components/common/EnhancedMeasurementSwitch.js +++ b/assets/js/modules/analytics-4/components/common/EnhancedMeasurementSwitch.js @@ -45,22 +45,20 @@ export default function EnhancedMeasurementSwitch( { onClick, disabled = false, loading = false, + formName = ENHANCED_MEASUREMENT_FORM, } ) { const isEnhancedMeasurementEnabled = useSelect( ( select ) => - select( CORE_FORMS ).getValue( - ENHANCED_MEASUREMENT_FORM, - ENHANCED_MEASUREMENT_ENABLED - ) + select( CORE_FORMS ).getValue( formName, ENHANCED_MEASUREMENT_ENABLED ) ); const { setValues } = useDispatch( CORE_FORMS ); const handleClick = useCallback( () => { - setValues( ENHANCED_MEASUREMENT_FORM, { + setValues( formName, { [ ENHANCED_MEASUREMENT_ENABLED ]: ! isEnhancedMeasurementEnabled, } ); onClick?.(); - }, [ isEnhancedMeasurementEnabled, onClick, setValues ] ); + }, [ formName, isEnhancedMeasurementEnabled, onClick, setValues ] ); return (
{ @@ -109,4 +110,12 @@ describe( 'getAccountDefaults', () => { ).toBe( fallbackTimezone ); } ); } ); + + describe( 'enhanced measurement enabled', () => { + it( 'defaults to true', () => { + expect( + getAccountDefaults( { siteName, siteURL, timezone } ) + ).toHaveProperty( ENHANCED_MEASUREMENT_ENABLED, true ); + } ); + } ); } ); diff --git a/assets/js/modules/analytics/components/common/AccountCreate/index.js b/assets/js/modules/analytics/components/common/AccountCreate/index.js index 8c1b68e50be..f58cefc4254 100644 --- a/assets/js/modules/analytics/components/common/AccountCreate/index.js +++ b/assets/js/modules/analytics/components/common/AccountCreate/index.js @@ -58,10 +58,12 @@ import CountrySelect from './CountrySelect'; import WebDataStreamField from './WebDataStreamField'; import useViewContext from '../../../../../hooks/useViewContext'; import { useFeature } from '../../../../../hooks/useFeature'; +import EnhancedMeasurementSwitch from '../../../../analytics-4/components/common/EnhancedMeasurementSwitch'; const { useDispatch, useSelect } = Data; export default function AccountCreate() { const ga4ReportingEnabled = useFeature( 'ga4Reporting' ); + const enhancedMeasurementEnabled = useFeature( 'enhancedMeasurement' ); const [ isNavigating, setIsNavigating ] = useState( false ); const accounts = useSelect( ( select ) => @@ -300,6 +302,14 @@ export default function AccountCreate() {
+ { enhancedMeasurementEnabled && ( +
+ +
+ ) } +

{ hasRequiredScope && ( diff --git a/includes/Modules/Analytics/Account_Ticket.php b/includes/Modules/Analytics/Account_Ticket.php index fae071ea1cc..c5b98422da3 100644 --- a/includes/Modules/Analytics/Account_Ticket.php +++ b/includes/Modules/Analytics/Account_Ticket.php @@ -51,6 +51,14 @@ class Account_Ticket { */ protected $timezone; + /** + * Whether or not enhanced measurement should be enabled. + * + * @since n.e.x.t + * @var boolean + */ + protected $enhanced_measurement_stream_enabled; + /** * Constructor. * @@ -157,6 +165,28 @@ public function set_timezone( $timezone ) { $this->timezone = (string) $timezone; } + /** + * Gets the enabled state of enhanced measurement for the data stream. + * + * @since n.e.x.t + * + * @return bool $enabled Enabled state. + */ + public function get_enhanced_measurement_stream_enabled() { + return $this->enhanced_measurement_stream_enabled; + } + + /** + * Sets the enabled state of enhanced measurement for the data stream. + * + * @since n.e.x.t + * + * @param bool $enabled Enabled state. + */ + public function set_enhanced_measurement_stream_enabled( $enabled ) { + $this->enhanced_measurement_stream_enabled = (bool) $enabled; + } + /** * Gets the array representation of the instance values. * diff --git a/includes/Modules/Analytics_4.php b/includes/Modules/Analytics_4.php index aae623dacc8..c44e1123a73 100644 --- a/includes/Modules/Analytics_4.php +++ b/includes/Modules/Analytics_4.php @@ -506,6 +506,20 @@ private function handle_provisioning_callback( $account_id, $account_ticket ) { ) ); + if ( Feature_Flags::enabled( 'enhancedMeasurement' ) && $account_ticket->get_enhanced_measurement_stream_enabled() ) { + $this->set_data( + 'enhanced-measurement-settings', + array( + 'propertyID' => $property->_id, + 'webDataStreamID' => $web_datastream->_id, + 'enhancedMeasurementSettings' => array( + // We can hardcode this to `true` here due to the conditional invocation. + 'streamEnabled' => true, + ), + ) + ); + } + $this->sync_google_tag_settings(); } @@ -988,6 +1002,7 @@ function( $property ) { $account_ticket->set_property_name( $data['propertyName'] ); $account_ticket->set_data_stream_name( $data['dataStreamName'] ); $account_ticket->set_timezone( $data['timezone'] ); + $account_ticket->set_enhanced_measurement_stream_enabled( ! empty( $data['enhancedMeasurementStreamEnabled'] ) ); // Cache the create ticket id long enough to verify it upon completion of the terms of service. set_transient( Analytics::PROVISION_ACCOUNT_TICKET_ID . '::' . get_current_user_id(), diff --git a/tests/phpunit/integration/Modules/Analytics_4Test.php b/tests/phpunit/integration/Modules/Analytics_4Test.php index 76dcab37cac..53fd179b773 100644 --- a/tests/phpunit/integration/Modules/Analytics_4Test.php +++ b/tests/phpunit/integration/Modules/Analytics_4Test.php @@ -21,7 +21,6 @@ use Google\Site_Kit\Core\Modules\Module_With_Scopes; use Google\Site_Kit\Core\Modules\Module_With_Settings; use Google\Site_Kit\Core\Modules\Module_With_Service_Entity; -use Google\Site_Kit\Core\Modules\Modules; use Google\Site_Kit\Core\Permissions\Permissions; use Google\Site_Kit\Core\Storage\Options; use Google\Site_Kit\Core\Storage\User_Options; @@ -39,6 +38,7 @@ use Google\Site_Kit\Tests\TestCase; use Google\Site_Kit\Tests\UserAuthenticationTrait; use Google\Site_Kit_Dependencies\Google\Service\Exception; +use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1alphaEnhancedMeasurementSettings; use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaConversionEvent; use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaCustomDimension; use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaDataStream; @@ -113,6 +113,7 @@ class Analytics_4Test extends TestCase { public function set_up() { parent::set_up(); + $this->request_handler_calls = array(); $this->enable_feature( 'ga4Reporting' ); @@ -449,6 +450,143 @@ function ( Request $request ) use ( $property_id, $webdatastream_id, $measuremen ); } + public function test_handle_provisioning_callback__with_enhancedMeasurement_streamEnabled() { + $this->enable_feature( 'enhancedMeasurement' ); + $account_id = '12345678'; + $property_id = '1001'; + $webdatastream_id = '2001'; + $measurement_id = '1A2BCD345E'; + + $options = new Options( $this->context ); + $options->set( + Settings::OPTION, + array( + 'accountID' => $account_id, + 'propertyID' => '', + 'webDataStreamID' => '', + 'measurementID' => '', + ) + ); + + // TODO: Rework this giant handler into one composed of per-request handlers. + FakeHttp::fake_google_http_handler( + $this->analytics->get_client(), + function ( Request $request ) use ( $property_id, $webdatastream_id, $measurement_id ) { + $url = parse_url( $request->getUri() ); + $params = json_decode( (string) $request->getBody(), true ); + + $this->request_handler_calls[] = array( + 'url' => $url, + 'params' => $params, + ); + + if ( 'analyticsadmin.googleapis.com' !== $url['host'] ) { + return new Response( 403 ); // Includes container lookup + } + + switch ( $url['path'] ) { + case '/v1beta/properties': + return new Response( + 200, + array(), + json_encode( + array( + 'name' => "properties/{$property_id}", + ) + ) + ); + case "/v1beta/properties/{$property_id}/dataStreams": + $data = new GoogleAnalyticsAdminV1betaDataStreamWebStreamData(); + $data->setMeasurementId( $measurement_id ); + $datastream = new GoogleAnalyticsAdminV1betaDataStream(); + $datastream->setName( "properties/{$property_id}/dataStreams/{$webdatastream_id}" ); + $datastream->setType( 'WEB_DATA_STREAM' ); + $datastream->setWebStreamData( $data ); + + return new Response( + 200, + array(), + json_encode( $datastream->toSimpleObject() ) + ); + case "/v1alpha/properties/{$property_id}/dataStreams/$webdatastream_id/enhancedMeasurementSettings": + $body = json_decode( $request->getBody(), true ); + $data = new GoogleAnalyticsAdminV1alphaEnhancedMeasurementSettings( $body ); + + return new Response( + 200, + array(), + json_encode( $data->toSimpleObject() ) + ); + + default: + return new Response( 200 ); + } + } + ); + + remove_all_actions( 'googlesitekit_analytics_handle_provisioning_callback' ); + + $this->analytics->register(); + $this->authentication->get_oauth_client()->set_granted_scopes( + array_merge( + $this->authentication->get_oauth_client()->get_required_scopes(), + array( Analytics::EDIT_SCOPE ) + ) + ); + + $this->assertEqualSetsWithIndex( + array( + 'accountID' => $account_id, + 'propertyID' => '', + 'webDataStreamID' => '', + 'measurementID' => '', + 'ownerID' => 0, + 'useSnippet' => true, + 'googleTagID' => '', + 'googleTagAccountID' => '', + 'googleTagContainerID' => '', + 'googleTagLastSyncedAtMs' => 0, + ), + $options->get( Settings::OPTION ) + ); + + $account_ticket = new Analytics\Account_Ticket(); + $account_ticket->set_enhanced_measurement_stream_enabled( true ); + do_action( 'googlesitekit_analytics_handle_provisioning_callback', $account_id, $account_ticket ); + + $this->assertEqualSetsWithIndex( + array( + 'accountID' => $account_id, + 'propertyID' => $property_id, + 'webDataStreamID' => $webdatastream_id, + 'measurementID' => $measurement_id, + 'ownerID' => 0, + 'useSnippet' => true, + 'googleTagID' => '', + 'googleTagAccountID' => '', + 'googleTagContainerID' => '', + 'googleTagLastSyncedAtMs' => 0, + ), + $options->get( Settings::OPTION ) + ); + + // Reduce the handler calls to only those for enhanced measurement settings. + $enhanced_measurement_settings_requests = array_filter( + $this->request_handler_calls, + function ( $call ) { + return false !== strpos( $call['url']['path'], 'enhancedMeasurementSettings' ); + } + ); + + // Ensure the enhanced measurement settings request was made. + $this->assertCount( 1, $enhanced_measurement_settings_requests ); + list( $request ) = array_values( $enhanced_measurement_settings_requests ); + $this->assertArrayIntersection( + array( 'streamEnabled' => true ), + $request['params'] + ); + } + public function data_create_account_ticket_required_parameters() { return array( 'displayName' => array( 'displayName' ), @@ -583,6 +721,42 @@ function ( Request $request ) use ( &$provision_account_ticket_request, $account $this->assertEquals( $timezone, $account_ticket_params['timezone'] ); } + public function test_create_account_ticket__with_enhancedMeasurement() { + // TODO: Merge with above test or keep separate when feature flag is removed. + $this->enable_feature( 'enhancedMeasurement' ); + $this->analytics->register(); + $data = array( + 'displayName' => 'test account name', + 'regionCode' => 'US', + 'propertyName' => 'test property name', + 'dataStreamName' => 'test stream name', + 'timezone' => 'UTC', + 'enhancedMeasurementStreamEnabled' => true, + ); + + // Required scopes are tested above. + $this->authentication->get_oauth_client()->set_granted_scopes( + array_merge( + $this->authentication->get_oauth_client()->get_required_scopes(), + (array) Analytics::EDIT_SCOPE + ) + ); + + // No need to control response again. + FakeHttp::fake_google_http_handler( $this->analytics->get_client() ); + + $response = $this->analytics->set_data( 'create-account-ticket', $data ); + // Assert request was made with expected arguments. + $this->assertNotWPError( $response ); + + // Assert transient is set with params. + $account_ticket_params = get_transient( Analytics::PROVISION_ACCOUNT_TICKET_ID . '::' . $this->user->ID ); + $this->assertEquals( 'test property name', $account_ticket_params['property_name'] ); + $this->assertEquals( 'test stream name', $account_ticket_params['data_stream_name'] ); + $this->assertEquals( 'UTC', $account_ticket_params['timezone'] ); + $this->assertEquals( true, $account_ticket_params['enhanced_measurement_stream_enabled'] ); + } + public function test_get_scopes() { $this->assertEqualSets( array(