diff --git a/src/API/Google/AdsCampaign.php b/src/API/Google/AdsCampaign.php index 88fbd0c39a..941e33a4ba 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. @@ -110,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 ); } @@ -220,6 +233,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/API/Google/MerchantMetrics.php b/src/API/Google/MerchantMetrics.php index 09317ca309..b5447affb2 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,42 @@ 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; + $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() ); + $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; + } + + $this->transients->set( TransientsInterface::ADS_CAMPAIGN_COUNT, $campaign_count, HOUR_IN_SECONDS * 12 ); + return $campaign_count; + } + /** * Get tomorrow's date to ensure we include any metrics from the current day. * 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 ); } /** 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, 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(), ]; } 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(); }