From fa1dff112a321c083b6f34494cf59a7dc15b03f1 Mon Sep 17 00:00:00 2001 From: mikkamp Date: Fri, 13 Oct 2023 14:50:28 +0100 Subject: [PATCH 1/5] Track customer ID and campaign count --- src/API/Google/MerchantMetrics.php | 32 ++++++++++++++++++++++++++++++ src/Tracking/TrackerSnapshot.php | 10 +++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/API/Google/MerchantMetrics.php b/src/API/Google/MerchantMetrics.php index 09317ca309..997196b696 100644 --- a/src/API/Google/MerchantMetrics.php +++ b/src/API/Google/MerchantMetrics.php @@ -4,6 +4,7 @@ namespace Automattic\WooCommerce\GoogleListingsAndAds\API\Google; use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\Query\AdsCampaignReportQuery; +use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\Query\AdsCampaignQuery; use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\Query\MerchantFreeListingReportQuery; use Automattic\WooCommerce\GoogleListingsAndAds\Google\Ads\GoogleAdsClient; use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface; @@ -187,6 +188,37 @@ public function get_cached_ads_metrics(): array { return $value; } + /** + * Return amount of active campaigns for the connected Ads account. + * + * @since x.x.x + * + * @return int + */ + public function get_campaign_count(): int { + if ( ! $this->options->get_ads_id() ) { + return 0; + } + + $campaign_count = 0; + + try { + $query = ( new AdsCampaignQuery() )->set_client( $this->ads_client, $this->options->get_ads_id() ); + $query->where( 'campaign.status', 'REMOVED', '!=' ); + + $campaign_results = $query->get_results(); + + // Iterate through all paged results (total results count is not set). + foreach ( $campaign_results->iterateAllElements() as $row ) { + $campaign_count++; + } + } catch ( Exception $e ) { + $campaign_count = 0; + } + + return $campaign_count; + } + /** * Get tomorrow's date to ensure we include any metrics from the current day. * diff --git a/src/Tracking/TrackerSnapshot.php b/src/Tracking/TrackerSnapshot.php index 01210592bf..a625e98ef1 100644 --- a/src/Tracking/TrackerSnapshot.php +++ b/src/Tracking/TrackerSnapshot.php @@ -4,6 +4,7 @@ namespace Automattic\WooCommerce\GoogleListingsAndAds\Tracking; use Automattic\WooCommerce\GoogleListingsAndAds\Ads\AdsService; +use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\MerchantMetrics; use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Conditional; use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Registerable; use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service; @@ -20,7 +21,10 @@ * Include Google Listings and Ads data in the WC Tracker snapshot. * * ContainerAware used to access: - * - MerchantStatuses + * - AdsService + * - MerchantCenterService + * - MerchantMetrics + * - TargetAudience * * @package Automattic\WooCommerce\GoogleListingsAndAds\Tracking */ @@ -83,6 +87,8 @@ protected function get_settings(): array { $ads_service = $this->container->get( AdsService::class ); /** @var MerchantCenterService $mc_service */ $mc_service = $this->container->get( MerchantCenterService::class ); + /** @var MerchantMetrics $merchant_metrics */ + $merchant_metrics = $this->container->get( MerchantMetrics::class ); return [ 'version' => $this->get_version(), @@ -98,6 +104,8 @@ protected function get_settings(): array { 'has_account_issue' => $mc_service->is_connected() && $mc_service->has_account_issues() ? 'yes' : 'no', 'has_at_least_one_synced_product' => $mc_service->is_connected() && $mc_service->has_at_least_one_synced_product() ? 'yes' : 'no', 'ads_setup_started' => $ads_service->is_setup_started() ? 'yes' : 'no', + 'ads_customer_id' => $this->options->get_ads_id(), + 'ads_campaign_count' => $merchant_metrics->get_campaign_count(), ]; } From d3977491b20ac20d5ce492d1a4b1e77b52cfa83a Mon Sep 17 00:00:00 2001 From: mikkamp Date: Fri, 13 Oct 2023 15:02:00 +0100 Subject: [PATCH 2/5] Cache campaign count --- src/API/Google/MerchantMetrics.php | 5 +++++ src/Options/TransientsInterface.php | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/API/Google/MerchantMetrics.php b/src/API/Google/MerchantMetrics.php index 997196b696..b5447affb2 100644 --- a/src/API/Google/MerchantMetrics.php +++ b/src/API/Google/MerchantMetrics.php @@ -201,6 +201,10 @@ public function get_campaign_count(): int { } $campaign_count = 0; + $cached_count = $this->transients->get( TransientsInterface::ADS_CAMPAIGN_COUNT ); + if ( null !== $cached_count ) { + return (int) $cached_count; + } try { $query = ( new AdsCampaignQuery() )->set_client( $this->ads_client, $this->options->get_ads_id() ); @@ -216,6 +220,7 @@ public function get_campaign_count(): int { $campaign_count = 0; } + $this->transients->set( TransientsInterface::ADS_CAMPAIGN_COUNT, $campaign_count, HOUR_IN_SECONDS * 12 ); return $campaign_count; } diff --git a/src/Options/TransientsInterface.php b/src/Options/TransientsInterface.php index 589fa1c146..7ec945eaec 100644 --- a/src/Options/TransientsInterface.php +++ b/src/Options/TransientsInterface.php @@ -10,6 +10,7 @@ */ interface TransientsInterface { + public const ADS_CAMPAIGN_COUNT = 'ads_campaign_count'; public const ADS_METRICS = 'ads_metrics'; public const FREE_LISTING_METRICS = 'free_listing_metrics'; public const MC_ACCOUNT_REVIEW = 'mc_account_review'; @@ -18,6 +19,7 @@ interface TransientsInterface { public const URL_MATCHES = 'url_matches'; public const VALID_OPTIONS = [ + self::ADS_CAMPAIGN_COUNT => true, self::ADS_METRICS => true, self::FREE_LISTING_METRICS => true, self::MC_ACCOUNT_REVIEW => true, From ca4bc4d6771ab5f426a9bd78e5afa1b029fb1cdc Mon Sep 17 00:00:00 2001 From: mikkamp Date: Fri, 13 Oct 2023 15:08:04 +0100 Subject: [PATCH 3/5] Clear transient on disconnect and create campaign --- src/API/Google/AdsCampaign.php | 5 +++++ src/Ads/AccountService.php | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/API/Google/AdsCampaign.php b/src/API/Google/AdsCampaign.php index 88fbd0c39a..0663590a43 100644 --- a/src/API/Google/AdsCampaign.php +++ b/src/API/Google/AdsCampaign.php @@ -14,6 +14,7 @@ use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface; use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareTrait; use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface; +use Automattic\WooCommerce\GoogleListingsAndAds\Options\TransientsInterface; use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WC; use Google\Ads\GoogleAds\Util\FieldMasks; use Google\Ads\GoogleAds\Util\V14\ResourceNames; @@ -35,6 +36,7 @@ * * ContainerAware used for: * - AdsAssetGroup + * - TransientsInterface * - WC * * @since 1.12.2 Refactored to support PMax and (legacy) SSC. @@ -220,6 +222,9 @@ function ( $country_code ) { $campaign_id = $this->mutate( $operations ); + // Clear cached campaign count. + $this->container->get( TransientsInterface::class )->delete( TransientsInterface::ADS_CAMPAIGN_COUNT ); + return [ 'id' => $campaign_id, 'status' => CampaignStatus::ENABLED, diff --git a/src/Ads/AccountService.php b/src/Ads/AccountService.php index e63beaa48a..98b30966b0 100644 --- a/src/Ads/AccountService.php +++ b/src/Ads/AccountService.php @@ -14,6 +14,7 @@ use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface; use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareTrait; use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface; +use Automattic\WooCommerce\GoogleListingsAndAds\Options\TransientsInterface; use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\Psr\Container\ContainerInterface; use Exception; @@ -28,6 +29,7 @@ * - AdsConversionAction * - Merchant * - Middleware + * - TransientsInterface * * @since 1.11.0 * @package Automattic\WooCommerce\GoogleListingsAndAds\Ads @@ -216,6 +218,7 @@ public function disconnect() { $this->options->delete( OptionsInterface::ADS_ID ); $this->options->delete( OptionsInterface::ADS_SETUP_COMPLETED_AT ); $this->options->delete( OptionsInterface::CAMPAIGN_CONVERT_STATUS ); + $this->container->get( TransientsInterface::class )->delete( TransientsInterface::ADS_CAMPAIGN_COUNT ); } /** From 4221a3743b3a81bf6dbf855775ab4c59950aa6c2 Mon Sep 17 00:00:00 2001 From: mikkamp Date: Fri, 13 Oct 2023 15:16:09 +0100 Subject: [PATCH 4/5] Cache count when fetching campaigns --- src/API/Google/AdsCampaign.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/API/Google/AdsCampaign.php b/src/API/Google/AdsCampaign.php index 0663590a43..941e33a4ba 100644 --- a/src/API/Google/AdsCampaign.php +++ b/src/API/Google/AdsCampaign.php @@ -112,14 +112,25 @@ public function get_campaigns( bool $exclude_removed = true, bool $fetch_criteri $query->where( 'campaign.status', 'REMOVED', '!=' ); } + $campaign_count = 0; $campaign_results = $query->get_results(); $converted_campaigns = []; foreach ( $campaign_results->iterateAllElements() as $row ) { + $campaign_count++; $campaign = $this->convert_campaign( $row ); $converted_campaigns[ $campaign['id'] ] = $campaign; } + if ( $exclude_removed ) { + // Cache campaign count. + $this->container->get( TransientsInterface::class )->set( + TransientsInterface::ADS_CAMPAIGN_COUNT, + $campaign_count, + HOUR_IN_SECONDS * 12 + ); + } + if ( $fetch_criterion ) { $converted_campaigns = $this->combine_campaigns_and_campaign_criterion_results( $converted_campaigns ); } From 853eef19b6f3b3fdfec9f911539d2c28bb9da6b5 Mon Sep 17 00:00:00 2001 From: mikkamp Date: Fri, 13 Oct 2023 15:24:59 +0100 Subject: [PATCH 5/5] Handle transients for unit tests --- tests/Unit/API/Google/AdsCampaignTest.php | 6 ++++++ tests/Unit/Ads/AccountServiceTest.php | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/tests/Unit/API/Google/AdsCampaignTest.php b/tests/Unit/API/Google/AdsCampaignTest.php index 9b50a06c58..333c361f7a 100644 --- a/tests/Unit/API/Google/AdsCampaignTest.php +++ b/tests/Unit/API/Google/AdsCampaignTest.php @@ -11,6 +11,7 @@ use Automattic\WooCommerce\GoogleListingsAndAds\Google\GoogleHelper; use Automattic\WooCommerce\GoogleListingsAndAds\Exception\ExceptionWithResponseData; use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface; +use Automattic\WooCommerce\GoogleListingsAndAds\Options\TransientsInterface; use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WC; use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Framework\UnitTest; use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Tools\HelperTrait\GoogleAdsClientTrait; @@ -42,6 +43,9 @@ class AdsCampaignTest extends UnitTest { /** @var MockObject|OptionsInterface $options */ protected $options; + /** @var MockObject|TransientsInterface $transients */ + protected $transients; + /** @var AdsCampaign $campaign */ protected $campaign; @@ -69,12 +73,14 @@ public function setUp(): void { $this->budget = $this->createMock( AdsCampaignBudget::class ); $this->criterion = new AdsCampaignCriterion(); $this->options = $this->createMock( OptionsInterface::class ); + $this->transients = $this->createMock( TransientsInterface::class ); $this->wc = $this->createMock( WC::class ); $this->google_helper = new GoogleHelper( $this->wc ); $this->container = new Container(); $this->container->share( AdsAssetGroup::class, $this->asset_group ); + $this->container->share( TransientsInterface::class, $this->transients ); $this->container->share( WC::class, $this->wc ); $this->campaign = new AdsCampaign( $this->client, $this->budget, $this->criterion, $this->google_helper ); diff --git a/tests/Unit/Ads/AccountServiceTest.php b/tests/Unit/Ads/AccountServiceTest.php index dc29b25653..7a2fdcc1f4 100644 --- a/tests/Unit/Ads/AccountServiceTest.php +++ b/tests/Unit/Ads/AccountServiceTest.php @@ -12,6 +12,7 @@ use Automattic\WooCommerce\GoogleListingsAndAds\Exception\ExceptionWithResponseData; use Automattic\WooCommerce\GoogleListingsAndAds\Options\AdsAccountState; use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface; +use Automattic\WooCommerce\GoogleListingsAndAds\Options\TransientsInterface; use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Framework\UnitTest; use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\Container; use Exception; @@ -41,6 +42,9 @@ class AccountServiceTest extends UnitTest { /** @var MockObject|AdsAccountState $state */ protected $state; + /** @var MockObject|TransientsInterface $transients */ + protected $transients; + /** @var AccountService $account */ protected $account; @@ -104,6 +108,7 @@ public function setUp(): void { $this->middleware = $this->createMock( Middleware::class ); $this->state = $this->createMock( AdsAccountState::class ); $this->options = $this->createMock( OptionsInterface::class ); + $this->transients = $this->createMock( TransientsInterface::class ); $this->container = new Container(); $this->container->share( Ads::class, $this->ads ); @@ -111,6 +116,7 @@ public function setUp(): void { $this->container->share( Merchant::class, $this->merchant ); $this->container->share( Middleware::class, $this->middleware ); $this->container->share( AdsAccountState::class, $this->state ); + $this->container->share( TransientsInterface::class, $this->transients ); $this->account = new AccountService( $this->container ); $this->account->set_options_object( $this->options ); @@ -467,6 +473,12 @@ public function test_disconnect() { [ OptionsInterface::CAMPAIGN_CONVERT_STATUS ] ); + $this->transients->expects( $this->exactly( 1 ) ) + ->method( 'delete' ) + ->withConsecutive( + [ TransientsInterface::ADS_CAMPAIGN_COUNT ], + ); + $this->account->disconnect(); }