From 62e9c20cc97cdd891d12d7bddee9cc902e75b6c4 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 17:16:17 +0100 Subject: [PATCH 01/19] Schedule multiple jobs with next page token --- src/Jobs/UpdateMerchantProductStatuses.php | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Jobs/UpdateMerchantProductStatuses.php b/src/Jobs/UpdateMerchantProductStatuses.php index 53934a6564..5d101d7449 100644 --- a/src/Jobs/UpdateMerchantProductStatuses.php +++ b/src/Jobs/UpdateMerchantProductStatuses.php @@ -82,18 +82,19 @@ public function can_schedule( $args = [] ): bool { * @throws JobException If the shipping settings cannot be synced. */ public function process_items( array $items ) { - $next_page_token = null; + $next_page_token = $items['next_page_token'] ?? null; - do { - $results = $this->merchant_report->get_product_view_report( $next_page_token ); - - $this->merchant_statuses->process_product_statuses( $results['statuses'] ); - - $next_page_token = $results['next_page']; - - } while ( $next_page_token ); + $results = $this->merchant_report->get_product_view_report( $next_page_token ); + $next_page_token = $results['next_page']; + $this->merchant_statuses->process_product_statuses( $results['statuses'] ); $this->merchant_statuses->update_product_stats(); + + if ( $next_page_token ) { + $this->schedule( [ [ 'next_page_token' => $next_page_token ] ] ); + } else { + $this->merchant_statuses->mark_mc_statuses_fetching_as_completed(); + } } /** @@ -102,8 +103,8 @@ public function process_items( array $items ) { * @param array $args - arguments. */ public function schedule( array $args = [] ) { - if ( $this->can_schedule() ) { - $this->action_scheduler->schedule_immediate( $this->get_process_item_hook() ); + if ( $this->can_schedule( $args ) ) { + $this->action_scheduler->schedule_immediate( $this->get_process_item_hook(), $args ); } } From 1a8f3602c4455c6c49454e7a83f887e586c19266 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 18:07:15 +0100 Subject: [PATCH 02/19] Refactor calculation of No sync products --- src/Jobs/UpdateMerchantProductStatuses.php | 12 ++- src/MerchantCenter/MerchantStatuses.php | 90 ++++++++++++++++------ src/Product/ProductRepository.php | 20 +++++ 3 files changed, 94 insertions(+), 28 deletions(-) diff --git a/src/Jobs/UpdateMerchantProductStatuses.php b/src/Jobs/UpdateMerchantProductStatuses.php index 5d101d7449..6518eca4c3 100644 --- a/src/Jobs/UpdateMerchantProductStatuses.php +++ b/src/Jobs/UpdateMerchantProductStatuses.php @@ -84,6 +84,11 @@ public function can_schedule( $args = [] ): bool { public function process_items( array $items ) { $next_page_token = $items['next_page_token'] ?? null; + // Clear the cache if we're starting from the beginning. + if ( ! $next_page_token ) { + $this->merchant_statuses->clear_cache(); + } + $results = $this->merchant_report->get_product_view_report( $next_page_token ); $next_page_token = $results['next_page']; @@ -93,7 +98,7 @@ public function process_items( array $items ) { if ( $next_page_token ) { $this->schedule( [ [ 'next_page_token' => $next_page_token ] ] ); } else { - $this->merchant_statuses->mark_mc_statuses_fetching_as_completed(); + $this->merchant_statuses->handle_complete_mc_statuses_fetching(); } } @@ -109,11 +114,12 @@ public function schedule( array $args = [] ) { } /** - * The job is considered to be scheduled if the "process_item" action is currently pending or in-progress. + * The job is considered to be scheduled if the "process_item" action is currently pending or in-progress regardless of the arguments. * * @return bool */ public function is_scheduled(): bool { - return $this->is_running(); + // We set 'args' to null so that it matches any arguments. This is because it's possible to have multiple instances of the job running with different page tokens + return $this->is_running( null ); } } diff --git a/src/MerchantCenter/MerchantStatuses.php b/src/MerchantCenter/MerchantStatuses.php index a2db6e9d76..2fae1d2095 100644 --- a/src/MerchantCenter/MerchantStatuses.php +++ b/src/MerchantCenter/MerchantStatuses.php @@ -118,10 +118,10 @@ public function get_product_statistics( bool $force_refresh = false ): array { } if ( $job->is_scheduled() || null === $this->mc_statuses ) { - // TODO: Add a notice to the client to inform that the statuses are being updated, or maybe we can pass the is_scheduled to the client. return [ 'timestamp' => $this->cache_created_time->getTimestamp(), 'statistics' => [], + 'loading' => true, ]; } @@ -589,6 +589,11 @@ protected function refresh_presync_product_issues(): void { * @see MerchantReport::get_product_view_report */ public function process_product_statuses( $statuses ): void { + $this->product_statuses = [ + 'products' => [], + 'parents' => [], + ]; + /** @var ProductHelper $product_helper */ $product_helper = $this->container->get( ProductHelper::class ); $visibility_meta_key = $this->prefix_meta_key( ProductMetaHandler::KEY_VISIBILITY ); @@ -664,7 +669,7 @@ protected function product_is_expiring( DateTime $expiration_date ): bool { } /** - * Calculate the synced product status statistics. It will group + * Sum the synced product status statistics. It will group * the variations for the same parent. * * For the case that one variation is approved and the other disapproved: @@ -675,7 +680,7 @@ protected function product_is_expiring( DateTime $expiration_date ): bool { * * @return array Product status statistics. */ - protected function calculate_synced_product_statistics(): array { + protected function sum_synced_product_statistics(): array { $product_statistics = [ MCStatus::APPROVED => 0, MCStatus::PARTIALLY_APPROVED => 0, @@ -685,6 +690,12 @@ protected function calculate_synced_product_statistics(): array { MCStatus::NOT_SYNCED => 0, ]; + // If the transient is set, use it to sum the total quantity. + $product_statistics_transient = $this->container->get( TransientsInterface::class )->get( Transients::MC_STATUSES ); + if ( $product_statistics_transient ) { + $product_statistics = $product_statistics_transient['statistics']; + } + $product_statistics_priority = [ MCStatus::APPROVED => 6, MCStatus::PARTIALLY_APPROVED => 5, @@ -723,15 +734,17 @@ protected function calculate_synced_product_statistics(): array { * Calculate the product status statistics and update the transient. */ protected function update_mc_status_statistics() { - $product_statistics = $this->calculate_synced_product_statistics(); - - /** @var ProductRepository $product_repository */ - $product_repository = $this->container->get( ProductRepository::class ); - $product_statistics[ MCStatus::NOT_SYNCED ] = count( $product_repository->find_mc_not_synced_product_ids() ); + $product_statistics = $this->sum_synced_product_statistics(); + /** + * The loading status will be updated when all the statuses are fetched. + * + * @see Automattic\WooCommerce\GoogleListingsAndAds\Jobs\UpdateMerchantProductStatuses::process_items + */ $this->mc_statuses = [ 'timestamp' => $this->cache_created_time->getTimestamp(), 'statistics' => $product_statistics, + 'loading' => true, ]; // Update the cached values @@ -742,6 +755,49 @@ protected function update_mc_status_statistics() { ); } + /** + * Calculate the total count of products in the MC using the statistics. + * + * @param array $statistics + * + * @return int + */ + protected function calculate_total_synced_product_statistics( array $statistics ): int { + if ( ! count( $statistics ) ) { + return 0; + } + + $synced_status_values = array_values( array_diff( $statistics, [ $statistics[ MCStatus::NOT_SYNCED ] ] ) ); + return array_sum( $synced_status_values ); + } + + + /** + * Handle the completion of the Merchant Center statuses fetching. + */ + public function handle_complete_mc_statuses_fetching() { + $mc_statuses = $this->container->get( TransientsInterface::class )->get( Transients::MC_STATUSES ); + + if ( $mc_statuses ) { + $total_synced_products = $this->calculate_total_synced_product_statistics( $mc_statuses['statistics'] ); + + /** @var ProductRepository $product_repository */ + $product_repository = $this->container->get( ProductRepository::class ); + $mc_statuses['statistics'][ MCStatus::NOT_SYNCED ] = count( + $product_repository->find_all_product_ids() + ) - $total_synced_products; + + $mc_statuses['loading'] = false; + + $this->container->get( TransientsInterface::class )->set( + Transients::MC_STATUSES, + $mc_statuses, + $this->get_status_lifetime() + ); + } + } + + /** * Update the Merchant Center status for each product. */ @@ -767,16 +823,11 @@ protected function update_products_meta_with_mc_status() { } ksort( $new_product_statuses ); - /** @var ProductRepository $product_repository */ - $product_repository = $this->container->get( ProductRepository::class ); - /** @var ProductMetaQueryHelper $product_meta_query_helper */ $product_meta_query_helper = $this->container->get( ProductMetaQueryHelper::class ); // Get all MC statuses. $current_product_statuses = $product_meta_query_helper->get_all_values( ProductMetaHandler::KEY_MC_STATUS ); - // Get all product IDs. - $all_product_ids = array_flip( $product_repository->find_ids() ); // Format: product_id=>status. $to_insert = []; @@ -791,20 +842,9 @@ protected function update_products_meta_with_mc_status() { // MC status not same as WC, update. $to_update[ $new_status ][] = intval( $product_id ); } - // Unset all found statuses from WC products array. - unset( $all_product_ids[ $product_id ] ); - } - - // Set products NOT FOUND in MC to NOT_SYNCED. - foreach ( array_keys( $all_product_ids ) as $product_id ) { - if ( empty( $current_product_statuses[ $product_id ] ) ) { - $to_insert[ $product_id ] = MCStatus::NOT_SYNCED; - } elseif ( $current_product_statuses[ $product_id ] !== MCStatus::NOT_SYNCED ) { - $to_update[ MCStatus::NOT_SYNCED ][] = $product_id; - } } - unset( $all_product_ids, $current_product_statuses, $new_product_statuses ); + unset( $current_product_statuses, $new_product_statuses ); // Insert and update changed MC Status postmeta. $product_meta_query_helper->batch_insert_values( ProductMetaHandler::KEY_MC_STATUS, $to_insert ); diff --git a/src/Product/ProductRepository.php b/src/Product/ProductRepository.php index 2f706cfb8f..14384f4f05 100644 --- a/src/Product/ProductRepository.php +++ b/src/Product/ProductRepository.php @@ -277,6 +277,26 @@ public function find_mc_not_synced_product_ids( int $limit = -1, int $offset = 0 return $this->find_ids( $args, $limit, $offset ); } + + /** + * Find all simple and variable product IDs regardless of MC status or visibility. + * + * @param int $limit Maximum number of results to retrieve or -1 for unlimited. + * @param int $offset Amount to offset product results. + * + * @return int[] Array of WooCommerce product IDs + */ + public function find_all_product_ids( int $limit = -1, int $offset = 0 ): array { + $args['return'] = 'ids'; + + $args = [ + 'type' => array_diff( ProductSyncer::get_supported_product_types(), [ 'variation' ] ), + 'status' => 'publish', + ]; + + return $this->find_ids( $args, $limit, $offset ); + } + /** * Returns an array of Google Product IDs associated with all synced WooCommerce products. * Note: excludes variable parent products as only the child variation products are actually synced From 4d9e0c858d49dca77eaa27b414224d21546bb8d8 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 18:14:51 +0100 Subject: [PATCH 03/19] Remove unused ProductSyncStats and add loading property --- .../ProductStatisticsController.php | 24 +++++-------------- .../RESTServiceProvider.php | 2 +- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php b/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php index 5939935c9c..553c2a5c2d 100644 --- a/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php +++ b/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php @@ -5,7 +5,6 @@ use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\BaseOptionsController; use Automattic\WooCommerce\GoogleListingsAndAds\API\TransportMethods; -use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\ProductSyncStats; use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantStatuses; use WP_REST_Response as Response; use WP_REST_Request as Request; @@ -28,25 +27,16 @@ class ProductStatisticsController extends BaseOptionsController { */ protected $merchant_statuses; - /** - * Helper class to count scheduled sync jobs. - * - * @var ProductSyncStats - */ - protected $sync_stats; - /** * ProductStatisticsController constructor. * * @param RESTServer $server * @param MerchantStatuses $merchant_statuses - * @param ProductSyncStats $sync_stats */ - public function __construct( RESTServer $server, MerchantStatuses $merchant_statuses, ProductSyncStats $sync_stats ) { + public function __construct( RESTServer $server, MerchantStatuses $merchant_statuses ) { parent::__construct( $server ); $this->merchant_statuses = $merchant_statuses; - $this->sync_stats = $sync_stats; } /** @@ -110,8 +100,6 @@ protected function get_product_status_stats( Request $request, bool $force_refre try { $response = $this->merchant_statuses->get_product_statistics( $force_refresh ); - $response['scheduled_sync'] = $this->sync_stats->get_count(); - return $this->prepare_item_for_response( $response, $request ); } catch ( Exception $e ) { return $this->response_from_exception( $e ); @@ -125,13 +113,13 @@ protected function get_product_status_stats( Request $request, bool $force_refre */ protected function get_schema_properties(): array { return [ - 'timestamp' => [ + 'timestamp' => [ 'type' => 'number', 'description' => __( 'Timestamp reflecting when the product status statistics were last generated.', 'google-listings-and-ads' ), 'context' => [ 'view' ], 'readonly' => true, ], - 'statistics' => [ + 'statistics' => [ 'type' => 'object', 'description' => __( 'Merchant Center product status statistics.', 'google-listings-and-ads' ), 'context' => [ 'view' ], @@ -164,9 +152,9 @@ protected function get_schema_properties(): array { ], ], ], - 'scheduled_sync' => [ - 'type' => 'number', - 'description' => __( 'Amount of scheduled jobs which will sync products to Google.', 'google-listings-and-ads' ), + 'loading' => [ + 'type' => 'boolean', + 'description' => __( 'Whether the product statistics are loading.', 'google-listings-and-ads' ), 'context' => [ 'view' ], 'readonly' => true, ], diff --git a/src/Internal/DependencyManagement/RESTServiceProvider.php b/src/Internal/DependencyManagement/RESTServiceProvider.php index d8cf483d77..6f2dcc2eec 100644 --- a/src/Internal/DependencyManagement/RESTServiceProvider.php +++ b/src/Internal/DependencyManagement/RESTServiceProvider.php @@ -110,7 +110,7 @@ public function register() { $this->share_with_container( AdsReportsController::class ); $this->share( GoogleAccountController::class, Connection::class ); $this->share( JetpackAccountController::class, Manager::class, Middleware::class ); - $this->share( MerchantCenterProductStatsController::class, MerchantStatuses::class, ProductSyncStats::class ); + $this->share( MerchantCenterProductStatsController::class, MerchantStatuses::class ); $this->share( MerchantCenterIssuesController::class, MerchantStatuses::class, ProductHelper::class ); $this->share( MerchantCenterProductFeedController::class, ProductFeedQueryHelper::class ); $this->share( MerchantCenterProductVisibilityController::class, ProductHelper::class, MerchantIssueQuery::class ); From 46566377f8ab71a0e4948cfb2d02978ac0d6f3ef Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 18:59:46 +0100 Subject: [PATCH 04/19] Add missing since --- src/Product/ProductRepository.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Product/ProductRepository.php b/src/Product/ProductRepository.php index 14384f4f05..7f7f22c893 100644 --- a/src/Product/ProductRepository.php +++ b/src/Product/ProductRepository.php @@ -281,6 +281,8 @@ public function find_mc_not_synced_product_ids( int $limit = -1, int $offset = 0 /** * Find all simple and variable product IDs regardless of MC status or visibility. * + * @since x.x.x + * * @param int $limit Maximum number of results to retrieve or -1 for unlimited. * @param int $offset Amount to offset product results. * From 5133be69e39897066cdb3e4a3a351be9dd0b335b Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 20:55:44 +0100 Subject: [PATCH 05/19] Check if MC is connected before retrieving product status --- src/MerchantCenter/MerchantStatuses.php | 35 +++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/MerchantCenter/MerchantStatuses.php b/src/MerchantCenter/MerchantStatuses.php index 2fae1d2095..def0507010 100644 --- a/src/MerchantCenter/MerchantStatuses.php +++ b/src/MerchantCenter/MerchantStatuses.php @@ -37,6 +37,7 @@ * - ProductHelper * - ProductRepository * - TransientsInterface + * - UpdateMerchantProductStatuses * * @package Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter */ @@ -105,9 +106,11 @@ public function __construct() { * @param bool $force_refresh Force refresh of all product status data. * * @return array The product status statistics. - * @throws Exception If the Merchant Center can't be polled for the statuses. + * @throws Exception If no Merchant Center account is connected, or account status is not retrievable. */ public function get_product_statistics( bool $force_refresh = false ): array { + $this->check_mc_is_connected(); + $this->mc_statuses = $this->container->get( TransientsInterface::class )->get( Transients::MC_STATUSES ); $job = $this->container->get( UpdateMerchantProductStatuses::class ); @@ -173,6 +176,25 @@ public function clear_cache(): void { $this->container->get( TransientsInterface::class )->delete( TransientsInterface::MC_STATUSES ); } + /** + * Check if the Merchant Center account is connected and throw an exception if it's not. + * + * @throws Exception If the Merchant Center account is not connected. + */ + protected function check_mc_is_connected() { + $mc_service = $this->container->get( MerchantCenterService::class ); + + if ( ! $mc_service->is_connected() ) { + + // Return a 401 to redirect to reconnect flow if the Google account is not connected. + if ( ! $mc_service->is_google_connected() ) { + throw new Exception( __( 'Google account is not connected.', 'google-listings-and-ads' ), 401 ); + } + + throw new Exception( __( 'Merchant Center account is not set up.', 'google-listings-and-ads' ) ); + } + } + /** * Update stale status-related data - account issues, product issues, products status stats. * @@ -188,16 +210,7 @@ public function maybe_refresh_status_data( bool $force_refresh = false ): void { } // Save a request if accounts are not connected. - $mc_service = $this->container->get( MerchantCenterService::class ); - if ( ! $mc_service->is_connected() ) { - - // Return a 401 to redirect to reconnect flow if the Google account is not connected. - if ( ! $mc_service->is_google_connected() ) { - throw new Exception( __( 'Google account is not connected.', 'google-listings-and-ads' ), 401 ); - } - - throw new Exception( __( 'Merchant Center account is not set up.', 'google-listings-and-ads' ) ); - } + $this->check_mc_is_connected(); // Update account-level issues. $this->refresh_account_issues(); From 7ecb0f8253da841385d11b5fc7855e388f85118e Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 20:56:08 +0100 Subject: [PATCH 06/19] Remove find_mc_not_synced_product_ids --- src/Product/ProductRepository.php | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/Product/ProductRepository.php b/src/Product/ProductRepository.php index 7f7f22c893..7baa0d734c 100644 --- a/src/Product/ProductRepository.php +++ b/src/Product/ProductRepository.php @@ -250,33 +250,6 @@ public function find_expiring_product_ids( int $limit = - 1, int $offset = 0 ): return $this->find_ids( $args, $limit, $offset ); } - /** - * Find and return an array of WooCommerce product IDs that are marked as MC not_synced. - * Excludes variations and variable products without variations. - * - * @param int $limit Maximum number of results to retrieve or -1 for unlimited. - * @param int $offset Amount to offset product results. - * - * @return int[] Array of WooCommerce product IDs - */ - public function find_mc_not_synced_product_ids( int $limit = -1, int $offset = 0 ): array { - $types = ProductSyncer::get_supported_product_types(); - $types = array_diff( $types, [ 'variation' ] ); - $args = [ - 'status' => 'publish', - 'type' => $types, - 'meta_query' => [ - [ - 'key' => ProductMetaHandler::KEY_MC_STATUS, - 'compare' => '=', - 'value' => MCStatus::NOT_SYNCED, - ], - ], - ]; - - return $this->find_ids( $args, $limit, $offset ); - } - /** * Find all simple and variable product IDs regardless of MC status or visibility. From 3e82e158972e5b35239e62a35889e51b90ebd183 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 20:56:54 +0100 Subject: [PATCH 07/19] Remove test from find_mc_not_synced_product_ids --- tests/Unit/Product/ProductRepositoryTest.php | 33 -------------------- 1 file changed, 33 deletions(-) diff --git a/tests/Unit/Product/ProductRepositoryTest.php b/tests/Unit/Product/ProductRepositoryTest.php index ba7918f114..852a50fb9a 100644 --- a/tests/Unit/Product/ProductRepositoryTest.php +++ b/tests/Unit/Product/ProductRepositoryTest.php @@ -259,39 +259,6 @@ public function test_find_expiring_product_ids() { ); } - public function test_find_mc_not_synced_product_ids() { - $product_1 = WC_Helper_Product::create_simple_product(); - $this->product_helper->mark_as_synced( $product_1, $this->generate_google_product_mock() ); - $this->product_meta->update_mc_status( $product_1, MCStatus::APPROVED ); - - $product_2 = WC_Helper_Product::create_simple_product(); - $this->product_meta->update_sync_status( $product_2, SyncStatus::HAS_ERRORS ); - $this->product_meta->update_visibility( $product_2, ChannelVisibility::SYNC_AND_SHOW ); - $this->product_meta->update_mc_status( $product_2, MCStatus::NOT_SYNCED ); - - $product_3 = WC_Helper_Product::create_simple_product(); - $this->product_meta->update_sync_status( $product_3, SyncStatus::HAS_ERRORS ); - $this->product_meta->update_visibility( $product_3, ChannelVisibility::DONT_SYNC_AND_SHOW ); - $this->product_meta->update_mc_status( $product_3, MCStatus::NOT_SYNCED ); - - WC_Helper_Product::create_simple_product(); - - $variable_product = WC_Helper_Product::create_variation_product(); - $this->product_meta->update_sync_status( $variable_product, SyncStatus::NOT_SYNCED ); - $this->product_meta->update_visibility( $variable_product, ChannelVisibility::SYNC_AND_SHOW ); - $this->product_meta->update_mc_status( $variable_product, MCStatus::NOT_SYNCED ); - foreach ( $variable_product->get_children() as $variation_id ) { - $this->product_meta->update_sync_status( wc_get_product( $variation_id ), SyncStatus::NOT_SYNCED ); - $this->product_meta->update_visibility( wc_get_product( $variation_id ), ChannelVisibility::SYNC_AND_SHOW ); - $this->product_meta->update_mc_status( wc_get_product( $variation_id ), MCStatus::NOT_SYNCED ); - } - - $this->assertEqualSets( - [ $product_2->get_id(), $variable_product->get_id() ], - $this->product_repository->find_mc_not_synced_product_ids() - ); - } - public function test_find_all_synced_google_ids() { $synced_google_ids = []; From be4ffede8d6dfa889fa315667c5066a50000c18c Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 20:57:39 +0100 Subject: [PATCH 08/19] Update MerchantStatusesTest --- .../MerchantCenter/MerchantStatusesTest.php | 306 +++++++++--------- 1 file changed, 160 insertions(+), 146 deletions(-) diff --git a/tests/Unit/MerchantCenter/MerchantStatusesTest.php b/tests/Unit/MerchantCenter/MerchantStatusesTest.php index 2d8c89510b..f087e86a32 100644 --- a/tests/Unit/MerchantCenter/MerchantStatusesTest.php +++ b/tests/Unit/MerchantCenter/MerchantStatusesTest.php @@ -15,7 +15,9 @@ use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Framework\UnitTest; use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\Container; use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\Google\Service\ShoppingContent; -use WP_Post; +use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\UpdateMerchantProductStatuses; +use Automattic\WooCommerce\GoogleListingsAndAds\Value\MCStatus; +use Exception; defined( 'ABSPATH' ) || exit; @@ -45,10 +47,8 @@ class MerchantStatusesTest extends UnitTest { private $merchant_statuses; private $product_repository; private $product_helper; - private $product_status; - private $product_statuses_custom_batch_response; - private $product_statuses_custom_batch_response_entry; - private $product_status_destination_status; + private $transients; + private $update_merchant_product_statuses_job; /** * Runs before each test is executed. @@ -66,19 +66,21 @@ public function setUp(): void { $this->product_statuses_custom_batch_response = $this->createMock( ShoppingContent\ProductstatusesCustomBatchResponse::class ); $this->product_statuses_custom_batch_response_entry = $this->createMock( ShoppingContent\ProductstatusesCustomBatchResponseEntry::class ); $this->product_status_destination_status = $this->createMock( ShoppingContent\ProductStatusDestinationStatus::class ); + $this->transients = $this->createMock( TransientsInterface::class ); + $this->update_merchant_product_statuses_job = $this->createMock( UpdateMerchantProductStatuses::class ); - $transients = $this->createMock( TransientsInterface::class ); $merchant_issue_table = $this->createMock( MerchantIssueTable::class ); $container = new Container(); $container->share( Merchant::class, $this->merchant ); $container->share( MerchantIssueQuery::class, $this->merchant_issue_query ); $container->share( MerchantCenterService::class, $this->merchant_center_service ); - $container->share( TransientsInterface::class, $transients ); + $container->share( TransientsInterface::class, $this->transients ); $container->share( ProductRepository::class, $this->product_repository ); $container->share( ProductMetaQueryHelper::class, $this->product_meta_query_helper ); $container->share( ProductHelper::class, $this->product_helper ); $container->share( MerchantIssueTable::class, $merchant_issue_table ); + $container->share( UpdateMerchantProductStatuses::class, $this->update_merchant_product_statuses_job ); $this->merchant_statuses = new MerchantStatuses(); $this->merchant_statuses->set_container( $container ); @@ -171,173 +173,185 @@ public function test_refresh_account_issues() { } /* - * Test get product statistics. + * Test get product statistics using the transient. * - * Test data: - * - Product ID: 100, Type: Variable, Parent: 0, MC Status: N/A - * - Product ID: 101, Type: Simple, Parent: 0, MC Status: disapproved - * - Product ID: 102, Type: Variation, Parent: 100, MC Status: approved - * - Product ID: 103, Type: Variable, Parent: 0, MC Status: N/A - * - Product ID: 104, Type: Variation, Parent: 103, MC Status: disapproved - * - Product ID: 105, Type: Variation, Parent: 100, MC Status: disapproved - * - * Note that product 102 and 105 have the same parent so they will be grouped as one. - * The MC status of 102 is approved while that of 105 is disapproved, Since "disapproved" - * has higher priority it'd be marked as disapproved. */ - public function test_get_product_statistics() { - $this->merchant_center_service->expects( $this->any() ) + public function test_get_product_statistics_when_mc_is_not_connected() { + $this->merchant_center_service->expects( $this->once() ) ->method( 'is_connected' ) - ->willReturn( true ); + ->willReturn( false ); - $this->account_status->expects( $this->any() ) - ->method( 'getAccountLevelIssues' ) - ->willReturn( [] ); + $this->merchant_center_service->expects( $this->once() ) + ->method( 'is_google_connected' ) + ->willReturn( false ); - $this->merchant->expects( $this->any() ) - ->method( 'get_accountstatus' ) - ->willReturn( $this->account_status ); + $this->transients->expects( $this->never() ) + ->method( 'get' ); + + $this->update_merchant_product_statuses_job->expects( $this->never() )->method( 'schedule' ); + + $this->update_merchant_product_statuses_job->expects( $this->never() )->method( 'is_scheduled' ); - $this->product_repository->expects( $this->once() ) - ->method( 'find_synced_product_ids' ) - ->willReturn( [ 101, 102, 104, 105 ] ); + $this->expectException( Exception::class ); + $this->expectExceptionMessage( 'Google account is not connected.' ); - $this->product_meta_query_helper->expects( $this->exactly( 3 ) ) - ->method( 'get_all_values' ) - ->willReturnOnConsecutiveCalls( + $this->merchant_statuses->get_product_statistics(); + } + + public function test_get_product_statistics_with_transient() { + $this->merchant_center_service->expects( $this->once() ) + ->method( 'is_connected' ) + ->willReturn( true ); + + $this->transients->expects( $this->once() ) + ->method( 'get' ) + ->willReturn( [ - 101 => [ 'TW' => 'online:en:TW:gla_101' ], - 102 => [ 'TW' => 'online:en:TW:gla_102' ], - 104 => [ 'TW' => 'online:en:TW:gla_104' ], - 105 => [ 'TW' => 'online:en:TW:gla_105' ], - ], - [], - [] + 'statistics' => [ + MCStatus::APPROVED => 3, + MCStatus::PARTIALLY_APPROVED => 1, + MCStatus::EXPIRING => 0, + MCStatus::PENDING => 0, + MCStatus::DISAPPROVED => 3, + MCStatus::NOT_SYNCED => 0, + ], + 'loading' => false, + ] ); - $this->product_helper->expects( $this->exactly( 12 ) ) - ->method( 'get_wc_product_id' ) - ->willReturnOnConsecutiveCalls( - 101, - 102, - 104, - 105, - 101, - 102, - 104, - 105, - 101, - 102, - 104, - 105, - ); + $this->update_merchant_product_statuses_job->expects( $this->never() ) + ->method( 'schedule' ); - $this->product_helper->expects( $this->exactly( 4 ) ) - ->method( 'get_wc_product_by_wp_post' ) - ->willReturnOnConsecutiveCalls( - new WP_Post( - (object) [ - 'ID' => 101, - 'post_type' => 'product', - 'post_parent' => 0, - ] - ), - new WP_Post( - (object) [ - 'ID' => 102, - 'post_type' => 'product', - 'post_parent' => 100, - ] - ), - new WP_Post( - (object) [ - 'ID' => 104, - 'post_type' => 'product', - 'post_parent' => 103, - ] - ), - new WP_Post( - (object) [ - 'ID' => 105, - 'post_type' => 'product', - 'post_parent' => 100, - ] - ), - ); + $this->update_merchant_product_statuses_job->expects( $this->exactly( 2 ) ) + ->method( 'is_scheduled' ); - $this->product_status_destination_status->expects( $this->exactly( 4 ) ) - ->method( 'getStatus' ) - ->willReturnOnConsecutiveCalls( - 'disapproved', // 101 - 'approved', // 102 - 'disapproved', // 104 - 'disapproved', // 105 - ); + $product_statistics = $this->merchant_statuses->get_product_statistics(); - $this->product_status_destination_status->expects( $this->exactly( 4 ) ) - ->method( 'getDestination' ) - ->willReturn( 'SurfacesAcrossGoogle' ); - - $this->product_status->expects( $this->exactly( 4 ) ) - ->method( 'getDestinationStatuses' ) - ->willReturnOnConsecutiveCalls( - [ $this->product_status_destination_status ], - [ $this->product_status_destination_status ], - [ $this->product_status_destination_status ], - [ $this->product_status_destination_status ], + $this->assertEquals( + [ + 'active' => 4, + 'expiring' => 0, + 'pending' => 0, + 'disapproved' => 3, + 'not_synced' => 0, + ], + $product_statistics['statistics'] + ); + + $this->assertEquals( + false, + $product_statistics['loading'] + ); + } + + public function test_get_product_statistics_without_transient() { + $this->merchant_center_service->expects( $this->once() ) + ->method( 'is_connected' ) + ->willReturn( true ); + + $this->transients->expects( $this->once() ) + ->method( 'get' ) + ->willReturn( + null ); - $this->product_status->expects( $this->exactly( 12 ) ) - ->method( 'getProductId' ) - ->willReturnOnConsecutiveCalls( - 'gla_101', - 'gla_102', - 'gla_104', - 'gla_105', - 'gla_101', - 'gla_102', - 'gla_104', - 'gla_105', - 'gla_101', - 'gla_102', - 'gla_104', - 'gla_105', + $this->update_merchant_product_statuses_job->expects( $this->exactly( 1 ) ) + ->method( 'schedule' ); + + $this->update_merchant_product_statuses_job->expects( $this->exactly( 2 ) ) + ->method( 'is_scheduled' ); + + $product_statistics = $this->merchant_statuses->get_product_statistics(); + + $this->assertEquals( + [], + $product_statistics['statistics'] + ); + + $this->assertEquals( + true, + $product_statistics['loading'] + ); + } + + public function test_get_product_statistics_with_product_statuses_job_running() { + $this->merchant_center_service->expects( $this->once() ) + ->method( 'is_connected' ) + ->willReturn( true ); + + $this->transients->expects( $this->once() ) + ->method( 'get' ) + ->willReturn( + [ + 'statistics' => [ + MCStatus::APPROVED => 3, + MCStatus::PARTIALLY_APPROVED => 1, + MCStatus::EXPIRING => 0, + MCStatus::PENDING => 0, + MCStatus::DISAPPROVED => 3, + MCStatus::NOT_SYNCED => 0, + ], + 'loading' => false, + ] ); - $this->product_status->expects( $this->any() ) - ->method( 'getItemLevelIssues' ) - ->willReturn( [] ); + $this->update_merchant_product_statuses_job->expects( $this->exactly( 0 ) ) + ->method( 'schedule' ); + + $this->update_merchant_product_statuses_job->expects( $this->exactly( 2 ) ) + ->method( 'is_scheduled' )->willReturn( true ); + + $product_statistics = $this->merchant_statuses->get_product_statistics(); + + $this->assertEquals( + [], + $product_statistics['statistics'] + ); + + $this->assertEquals( + true, + $product_statistics['loading'] + ); + } - $this->product_statuses_custom_batch_response_entry->expects( $this->any() ) - ->method( 'getProductStatus' ) - ->willReturn( $this->product_status ); + public function test_get_product_statistics_with_force_refresh() { + $this->merchant_center_service->expects( $this->once() ) + ->method( 'is_connected' ) + ->willReturn( true ); - $this->product_statuses_custom_batch_response->expects( $this->once() ) - ->method( 'getEntries' ) + $this->transients->expects( $this->once() ) + ->method( 'get' ) ->willReturn( [ - $this->product_statuses_custom_batch_response_entry, - $this->product_statuses_custom_batch_response_entry, - $this->product_statuses_custom_batch_response_entry, - $this->product_statuses_custom_batch_response_entry, + 'statistics' => [ + MCStatus::APPROVED => 3, + MCStatus::PARTIALLY_APPROVED => 1, + MCStatus::EXPIRING => 0, + MCStatus::PENDING => 0, + MCStatus::DISAPPROVED => 3, + MCStatus::NOT_SYNCED => 0, + ], + 'loading' => false, ] ); - $this->merchant->expects( $this->once() ) - ->method( 'get_productstatuses_batch' ) - ->willReturn( $this->product_statuses_custom_batch_response ); + $this->update_merchant_product_statuses_job->expects( $this->exactly( 1 ) ) + ->method( 'schedule' ); + + $this->update_merchant_product_statuses_job->expects( $this->exactly( 2 ) ) + ->method( 'is_scheduled' )->willReturnOnConsecutiveCalls( false, true ); - $product_statistics = $this->merchant_statuses->get_product_statistics( true ); + $force_refresh = true; + $product_statistics = $this->merchant_statuses->get_product_statistics( $force_refresh ); $this->assertEquals( - [ - 'active' => 0, - 'expiring' => 0, - 'pending' => 0, - 'disapproved' => 3, - 'not_synced' => 0, - ], + [], $product_statistics['statistics'] ); + + $this->assertEquals( + true, + $product_statistics['loading'] + ); } } From 38fb6a84ac2558560565d3ff6debb927b0f90731 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 22:09:55 +0100 Subject: [PATCH 09/19] Add UpdateMerchantProductStatusesTest --- .../UpdateMerchantProductStatusesTest.php | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php diff --git a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php new file mode 100644 index 0000000000..ddad38e8c7 --- /dev/null +++ b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php @@ -0,0 +1,210 @@ +action_scheduler = $this->createMock( ActionSchedulerInterface::class ); + $this->monitor = $this->createMock( ActionSchedulerJobMonitor::class ); + $this->merchant_center_service = $this->createMock( MerchantCenterService::class ); + $this->merchant_report = $this->createMock( MerchantReport::class ); + $this->merchant_statuses = $this->createMock( MerchantStatuses::class ); + $this->job = new UpdateMerchantProductStatuses( + $this->action_scheduler, + $this->monitor, + $this->merchant_center_service, + $this->merchant_report, + $this->merchant_statuses + ); + + $this->job->init(); + } + + public function test_update_merchant_product_statuses_not_connected() { + $this->merchant_center_service->method( 'is_connected' ) + ->willReturn( false ); + + $this->assertFalse( $this->job->can_schedule() ); + } + + public function test_update_merchant_product_statuses() { + $this->merchant_center_service->method( 'is_connected' ) + ->willReturn( true ); + + $matcher = $this->exactly( 3 ); + $this->merchant_report->expects( $matcher ) + ->method( 'get_product_view_report' ) + ->will( + $this->returnCallback( + function ( $next_page_token ) use ( $matcher ) { + $invocation_count = $matcher->getInvocationCount(); + + if ( $invocation_count === 1 ) { + + $this->assertNull( $next_page_token ); + return [ + 'statuses' => [ + [ + 'product_id' => 1, + 'status' => MCStatus::APPROVED, + ], + ], + 'next_page' => 'ABC=', + ]; + } + + if ( $invocation_count === 2 ) { + $this->assertEquals( 'ABC=', $next_page_token ); + return [ + 'statuses' => [ + [ + 'product_id' => 2, + 'status' => MCStatus::APPROVED, + ], + ], + 'next_page' => 'DEF=', + ]; + } + + if ( $invocation_count === 3 ) { + $this->assertEquals( 'DEF=', $next_page_token ); + return [ + 'statuses' => [ + [ + 'product_id' => 3, + 'status' => MCStatus::APPROVED, + ], + ], + 'next_page' => null, + ]; + } + + throw new Exception( 'Invalid next page token' ); + } + ) + ); + + $matcher = $this->exactly( 3 ); + $this->action_scheduler->expects( $matcher ) + ->method( 'schedule_immediate' ) + ->willReturnCallback( + function ( $hook_name, $args ) use ( $matcher ) { + $invocation_count = $matcher->getInvocationCount(); + + if ( $invocation_count === 1 ) { + $this->assertEquals( [], $args ); + } + + if ( $invocation_count === 2 ) { + $this->assertEquals( [ 'next_page_token' => 'ABC=' ], $args[0] ); + } + + if ( $invocation_count === 3 ) { + $this->assertEquals( [ 'next_page_token' => 'DEF=' ], $args[0] ); + } + + do_action( self::PROCESS_ITEM_HOOK, $args[0] ?? [] ); + + return $matcher->getInvocationCount(); + } + ); + + $matcher = $this->exactly( 3 ); + $this->merchant_statuses->expects( $matcher ) + ->method( 'process_product_statuses' ) + ->willReturnCallback( + function ( $statuses ) use ( $matcher ) { + $invocation_count = $matcher->getInvocationCount(); + + if ( $invocation_count === 1 ) { + $this->assertEquals( + [ + [ + 'product_id' => 1, + 'status' => MCStatus::APPROVED, + ], + ], + $statuses + ); + } + + if ( $invocation_count === 2 ) { + $this->assertEquals( + [ + [ + 'product_id' => 2, + 'status' => MCStatus::APPROVED, + ], + ], + $statuses + ); + } + + if ( $invocation_count === 3 ) { + $this->assertEquals( + [ + [ + 'product_id' => 3, + 'status' => MCStatus::APPROVED, + ], + ], + $statuses + ); + } + } + ); + + $this->merchant_statuses->expects( $this->exactly( 3 ) ) + ->method( 'update_product_stats' ); + + $this->merchant_statuses->expects( $this->exactly( 1 ) ) + ->method( 'handle_complete_mc_statuses_fetching' ); + + $this->job->schedule(); + } +} From 8ea87f967934734cc914dd060f268a8d6ade8f17 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 22:12:10 +0100 Subject: [PATCH 10/19] Test if clear_cache is deleted --- tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php index ddad38e8c7..ab8d85e72f 100644 --- a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php +++ b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php @@ -205,6 +205,9 @@ function ( $statuses ) use ( $matcher ) { $this->merchant_statuses->expects( $this->exactly( 1 ) ) ->method( 'handle_complete_mc_statuses_fetching' ); + $this->merchant_statuses->expects( $this->exactly( 1 ) ) + ->method( 'clear_cache' ); + $this->job->schedule(); } } From 9b893d926e0ca764edf5561c9858880d99cea957 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 22:53:23 +0100 Subject: [PATCH 11/19] Unify update_product_stats --- src/Jobs/UpdateMerchantProductStatuses.php | 3 +-- src/MerchantCenter/MerchantStatuses.php | 11 +++++++---- tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php | 5 +---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Jobs/UpdateMerchantProductStatuses.php b/src/Jobs/UpdateMerchantProductStatuses.php index 6518eca4c3..c3d5d67a0b 100644 --- a/src/Jobs/UpdateMerchantProductStatuses.php +++ b/src/Jobs/UpdateMerchantProductStatuses.php @@ -92,8 +92,7 @@ public function process_items( array $items ) { $results = $this->merchant_report->get_product_view_report( $next_page_token ); $next_page_token = $results['next_page']; - $this->merchant_statuses->process_product_statuses( $results['statuses'] ); - $this->merchant_statuses->update_product_stats(); + $this->merchant_statuses->update_product_stats( $results['statuses'] ); if ( $next_page_token ) { $this->schedule( [ [ 'next_page_token' => $next_page_token ] ] ); diff --git a/src/MerchantCenter/MerchantStatuses.php b/src/MerchantCenter/MerchantStatuses.php index def0507010..ae8734823d 100644 --- a/src/MerchantCenter/MerchantStatuses.php +++ b/src/MerchantCenter/MerchantStatuses.php @@ -656,10 +656,15 @@ public function process_product_statuses( $statuses ): void { } /** - * Update the product status statistics. + * Update the product status statistics for a list of products statuses. + * + * @param array[] $statuses statuses. See statuses format in MerchantReport::get_product_view_report. */ - public function update_product_stats() { + public function update_product_stats( array $statuses ): void { $this->mc_statuses = []; + + $this->process_product_statuses( $statuses ); + // Update each product's mc_status and then update the global statistics. $this->update_products_meta_with_mc_status(); $this->update_mc_status_statistics(); @@ -857,8 +862,6 @@ protected function update_products_meta_with_mc_status() { } } - unset( $current_product_statuses, $new_product_statuses ); - // Insert and update changed MC Status postmeta. $product_meta_query_helper->batch_insert_values( ProductMetaHandler::KEY_MC_STATUS, $to_insert ); foreach ( $to_update as $status => $product_ids ) { diff --git a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php index ab8d85e72f..0728aef099 100644 --- a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php +++ b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php @@ -156,7 +156,7 @@ function ( $hook_name, $args ) use ( $matcher ) { $matcher = $this->exactly( 3 ); $this->merchant_statuses->expects( $matcher ) - ->method( 'process_product_statuses' ) + ->method( 'update_product_stats' ) ->willReturnCallback( function ( $statuses ) use ( $matcher ) { $invocation_count = $matcher->getInvocationCount(); @@ -199,9 +199,6 @@ function ( $statuses ) use ( $matcher ) { } ); - $this->merchant_statuses->expects( $this->exactly( 3 ) ) - ->method( 'update_product_stats' ); - $this->merchant_statuses->expects( $this->exactly( 1 ) ) ->method( 'handle_complete_mc_statuses_fetching' ); From 1beb6b80216d4d1ad008dc848657e65d6f34d021 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 23:28:55 +0100 Subject: [PATCH 12/19] Remove unused variables --- tests/Unit/MerchantCenter/MerchantStatusesTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/Unit/MerchantCenter/MerchantStatusesTest.php b/tests/Unit/MerchantCenter/MerchantStatusesTest.php index f087e86a32..76b0b7136e 100644 --- a/tests/Unit/MerchantCenter/MerchantStatusesTest.php +++ b/tests/Unit/MerchantCenter/MerchantStatusesTest.php @@ -30,9 +30,6 @@ * @property MerchantStatuses $merchant_statuses * @property ProductRepository $product_repository * @property ProductHelper $product_helper - * @property ShoppingContent\ProductStatus $product_status - * @property ShoppingContent\ProductStatusesCustomBatchResponse $product_statuses_custom_batch_response - * @property ShoppingContent\ProductStatusesCustomBatchResponseEntry $product_statuses_custom_batch_response_entry * @property ShoppingContent\ProductStatusDestinationStatus $product_status_destination_status * @package Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\MerchantCenter * @group MerchantCenterStatuses @@ -62,9 +59,6 @@ public function setUp(): void { $this->product_meta_query_helper = $this->createMock( ProductMetaQueryHelper::class ); $this->product_repository = $this->createMock( ProductRepository::class ); $this->product_helper = $this->createMock( ProductHelper::class ); - $this->product_status = $this->createMock( ShoppingContent\ProductStatus::class ); - $this->product_statuses_custom_batch_response = $this->createMock( ShoppingContent\ProductstatusesCustomBatchResponse::class ); - $this->product_statuses_custom_batch_response_entry = $this->createMock( ShoppingContent\ProductstatusesCustomBatchResponseEntry::class ); $this->product_status_destination_status = $this->createMock( ShoppingContent\ProductStatusDestinationStatus::class ); $this->transients = $this->createMock( TransientsInterface::class ); $this->update_merchant_product_statuses_job = $this->createMock( UpdateMerchantProductStatuses::class ); From 828ca3bc6e16b6a7e8283148d64787ebe99eb049 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Sun, 11 Feb 2024 23:36:47 +0100 Subject: [PATCH 13/19] Fix phpcs --- .../MerchantCenter/MerchantStatusesTest.php | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/Unit/MerchantCenter/MerchantStatusesTest.php b/tests/Unit/MerchantCenter/MerchantStatusesTest.php index 76b0b7136e..26eddad6e1 100644 --- a/tests/Unit/MerchantCenter/MerchantStatusesTest.php +++ b/tests/Unit/MerchantCenter/MerchantStatusesTest.php @@ -30,7 +30,6 @@ * @property MerchantStatuses $merchant_statuses * @property ProductRepository $product_repository * @property ProductHelper $product_helper - * @property ShoppingContent\ProductStatusDestinationStatus $product_status_destination_status * @package Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\MerchantCenter * @group MerchantCenterStatuses */ @@ -52,16 +51,15 @@ class MerchantStatusesTest extends UnitTest { */ public function setUp(): void { parent::setUp(); - $this->merchant = $this->createMock( Merchant::class ); - $this->merchant_issue_query = $this->createMock( MerchantIssueQuery::class ); - $this->merchant_center_service = $this->createMock( MerchantCenterService::class ); - $this->account_status = $this->createMock( ShoppingContent\AccountStatus::class ); - $this->product_meta_query_helper = $this->createMock( ProductMetaQueryHelper::class ); - $this->product_repository = $this->createMock( ProductRepository::class ); - $this->product_helper = $this->createMock( ProductHelper::class ); - $this->product_status_destination_status = $this->createMock( ShoppingContent\ProductStatusDestinationStatus::class ); - $this->transients = $this->createMock( TransientsInterface::class ); - $this->update_merchant_product_statuses_job = $this->createMock( UpdateMerchantProductStatuses::class ); + $this->merchant = $this->createMock( Merchant::class ); + $this->merchant_issue_query = $this->createMock( MerchantIssueQuery::class ); + $this->merchant_center_service = $this->createMock( MerchantCenterService::class ); + $this->account_status = $this->createMock( ShoppingContent\AccountStatus::class ); + $this->product_meta_query_helper = $this->createMock( ProductMetaQueryHelper::class ); + $this->product_repository = $this->createMock( ProductRepository::class ); + $this->product_helper = $this->createMock( ProductHelper::class ); + $this->transients = $this->createMock( TransientsInterface::class ); + $this->update_merchant_product_statuses_job = $this->createMock( UpdateMerchantProductStatuses::class ); $merchant_issue_table = $this->createMock( MerchantIssueTable::class ); From 624a0a986838f1f4fe579f533b3adaaa8d7b3f69 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Mon, 12 Feb 2024 10:03:42 +0100 Subject: [PATCH 14/19] Rename variable --- src/API/Google/MerchantReport.php | 2 +- src/Jobs/UpdateMerchantProductStatuses.php | 4 ++-- .../Unit/Jobs/UpdateMerchantProductStatusesTest.php | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/API/Google/MerchantReport.php b/src/API/Google/MerchantReport.php index f295a3390b..e33013938e 100644 --- a/src/API/Google/MerchantReport.php +++ b/src/API/Google/MerchantReport.php @@ -105,7 +105,7 @@ public function get_product_view_report( $next_page_token = null ): array { } - $product_view_data['next_page'] = $response->getNextPageToken(); + $product_view_data['next_page_token'] = $response->getNextPageToken(); return $product_view_data; } catch ( GoogleException $e ) { diff --git a/src/Jobs/UpdateMerchantProductStatuses.php b/src/Jobs/UpdateMerchantProductStatuses.php index c3d5d67a0b..1c6fb2eef7 100644 --- a/src/Jobs/UpdateMerchantProductStatuses.php +++ b/src/Jobs/UpdateMerchantProductStatuses.php @@ -90,7 +90,7 @@ public function process_items( array $items ) { } $results = $this->merchant_report->get_product_view_report( $next_page_token ); - $next_page_token = $results['next_page']; + $next_page_token = $results['next_page_token']; $this->merchant_statuses->update_product_stats( $results['statuses'] ); @@ -118,7 +118,7 @@ public function schedule( array $args = [] ) { * @return bool */ public function is_scheduled(): bool { - // We set 'args' to null so that it matches any arguments. This is because it's possible to have multiple instances of the job running with different page tokens + // We set 'args' to null so it matches any arguments. This is because it's possible to have multiple instances of the job running with different page tokens return $this->is_running( null ); } } diff --git a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php index 0728aef099..e1df030242 100644 --- a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php +++ b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php @@ -88,39 +88,39 @@ function ( $next_page_token ) use ( $matcher ) { $this->assertNull( $next_page_token ); return [ - 'statuses' => [ + 'statuses' => [ [ 'product_id' => 1, 'status' => MCStatus::APPROVED, ], ], - 'next_page' => 'ABC=', + 'next_page_token' => 'ABC=', ]; } if ( $invocation_count === 2 ) { $this->assertEquals( 'ABC=', $next_page_token ); return [ - 'statuses' => [ + 'statuses' => [ [ 'product_id' => 2, 'status' => MCStatus::APPROVED, ], ], - 'next_page' => 'DEF=', + 'next_page_token' => 'DEF=', ]; } if ( $invocation_count === 3 ) { $this->assertEquals( 'DEF=', $next_page_token ); return [ - 'statuses' => [ + 'statuses' => [ [ 'product_id' => 3, 'status' => MCStatus::APPROVED, ], ], - 'next_page' => null, + 'next_page_token' => null, ]; } From dd39acb2c1e877186678945dc5fe40936cafcb6b Mon Sep 17 00:00:00 2001 From: Jorge M Date: Mon, 12 Feb 2024 10:18:35 +0100 Subject: [PATCH 15/19] Tweak some phpdocs --- src/MerchantCenter/MerchantStatuses.php | 10 +++++++++- src/Product/ProductRepository.php | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/MerchantCenter/MerchantStatuses.php b/src/MerchantCenter/MerchantStatuses.php index ae8734823d..00981f66f1 100644 --- a/src/MerchantCenter/MerchantStatuses.php +++ b/src/MerchantCenter/MerchantStatuses.php @@ -179,6 +179,8 @@ public function clear_cache(): void { /** * Check if the Merchant Center account is connected and throw an exception if it's not. * + * @since x.x.x + * * @throws Exception If the Merchant Center account is not connected. */ protected function check_mc_is_connected() { @@ -601,7 +603,7 @@ protected function refresh_presync_product_issues(): void { * @param array[] $statuses statuses. * @see MerchantReport::get_product_view_report */ - public function process_product_statuses( $statuses ): void { + public function process_product_statuses( array $statuses ): void { $this->product_statuses = [ 'products' => [], 'parents' => [], @@ -658,6 +660,8 @@ public function process_product_statuses( $statuses ): void { /** * Update the product status statistics for a list of products statuses. * + * @since x.x.x + * * @param array[] $statuses statuses. See statuses format in MerchantReport::get_product_view_report. */ public function update_product_stats( array $statuses ): void { @@ -776,6 +780,8 @@ protected function update_mc_status_statistics() { /** * Calculate the total count of products in the MC using the statistics. * + * @since x.x.x + * * @param array $statistics * * @return int @@ -792,6 +798,8 @@ protected function calculate_total_synced_product_statistics( array $statistics /** * Handle the completion of the Merchant Center statuses fetching. + * + * @since x.x.x */ public function handle_complete_mc_statuses_fetching() { $mc_statuses = $this->container->get( TransientsInterface::class )->get( Transients::MC_STATUSES ); diff --git a/src/Product/ProductRepository.php b/src/Product/ProductRepository.php index 7baa0d734c..0195df971f 100644 --- a/src/Product/ProductRepository.php +++ b/src/Product/ProductRepository.php @@ -250,7 +250,6 @@ public function find_expiring_product_ids( int $limit = - 1, int $offset = 0 ): return $this->find_ids( $args, $limit, $offset ); } - /** * Find all simple and variable product IDs regardless of MC status or visibility. * From fe94543049c1bba8062226a3507555856c44fb4e Mon Sep 17 00:00:00 2001 From: Jorge M Date: Tue, 13 Feb 2024 13:42:43 +0100 Subject: [PATCH 16/19] Add options intermediate count data --- src/Jobs/UpdateMerchantProductStatuses.php | 1 + src/MerchantCenter/MerchantStatuses.php | 73 +++++++++---------- src/Options/OptionsInterface.php | 2 + .../UpdateMerchantProductStatusesTest.php | 3 + 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/Jobs/UpdateMerchantProductStatuses.php b/src/Jobs/UpdateMerchantProductStatuses.php index 1c6fb2eef7..a322dd3d27 100644 --- a/src/Jobs/UpdateMerchantProductStatuses.php +++ b/src/Jobs/UpdateMerchantProductStatuses.php @@ -87,6 +87,7 @@ public function process_items( array $items ) { // Clear the cache if we're starting from the beginning. if ( ! $next_page_token ) { $this->merchant_statuses->clear_cache(); + $this->merchant_statuses->delete_product_status_count_intermediate_data(); } $results = $this->merchant_report->get_product_view_report( $next_page_token ); diff --git a/src/MerchantCenter/MerchantStatuses.php b/src/MerchantCenter/MerchantStatuses.php index 00981f66f1..7deabefa8f 100644 --- a/src/MerchantCenter/MerchantStatuses.php +++ b/src/MerchantCenter/MerchantStatuses.php @@ -22,6 +22,9 @@ use Automattic\WooCommerce\GoogleListingsAndAds\Value\MCStatus; use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\Google\Service\ShoppingContent\ProductStatus as GoogleProductStatus; use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\UpdateMerchantProductStatuses; +use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface; +use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareTrait; +use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface; use DateTime; use Exception; @@ -41,8 +44,9 @@ * * @package Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter */ -class MerchantStatuses implements Service, ContainerAwareInterface { +class MerchantStatuses implements Service, ContainerAwareInterface, OptionsAwareInterface { + use OptionsAwareTrait; use ContainerAwareTrait; use PluginHelper; @@ -176,6 +180,15 @@ public function clear_cache(): void { $this->container->get( TransientsInterface::class )->delete( TransientsInterface::MC_STATUSES ); } + /** + * Delete the intermediate product status count data. + * + * @since x.x.x + */ + public function delete_product_status_count_intermediate_data(): void { + $this->options->delete( OptionsInterface::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA ); + } + /** * Check if the Merchant Center account is connected and throw an exception if it's not. * @@ -671,7 +684,7 @@ public function update_product_stats( array $statuses ): void { // Update each product's mc_status and then update the global statistics. $this->update_products_meta_with_mc_status(); - $this->update_mc_status_statistics(); + $this->update_intermediate_product_statistics(); } /** @@ -691,7 +704,7 @@ protected function product_is_expiring( DateTime $expiration_date ): bool { } /** - * Sum the synced product status statistics. It will group + * Sum and update the intermediate product status statistics. It will group * the variations for the same parent. * * For the case that one variation is approved and the other disapproved: @@ -702,7 +715,7 @@ protected function product_is_expiring( DateTime $expiration_date ): bool { * * @return array Product status statistics. */ - protected function sum_synced_product_statistics(): array { + protected function update_intermediate_product_statistics(): array { $product_statistics = [ MCStatus::APPROVED => 0, MCStatus::PARTIALLY_APPROVED => 0, @@ -713,9 +726,9 @@ protected function sum_synced_product_statistics(): array { ]; // If the transient is set, use it to sum the total quantity. - $product_statistics_transient = $this->container->get( TransientsInterface::class )->get( Transients::MC_STATUSES ); - if ( $product_statistics_transient ) { - $product_statistics = $product_statistics_transient['statistics']; + $product_statistics_intermediate_data = $this->options->get( OptionsInterface::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA ); + if ( $product_statistics_intermediate_data ) { + $product_statistics = $product_statistics_intermediate_data; } $product_statistics_priority = [ @@ -749,32 +762,9 @@ protected function sum_synced_product_statistics(): array { $product_statistics[ $parent_status ] += 1; } - return $product_statistics; - } - - /** - * Calculate the product status statistics and update the transient. - */ - protected function update_mc_status_statistics() { - $product_statistics = $this->sum_synced_product_statistics(); + $this->options->update( OptionsInterface::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA, $product_statistics ); - /** - * The loading status will be updated when all the statuses are fetched. - * - * @see Automattic\WooCommerce\GoogleListingsAndAds\Jobs\UpdateMerchantProductStatuses::process_items - */ - $this->mc_statuses = [ - 'timestamp' => $this->cache_created_time->getTimestamp(), - 'statistics' => $product_statistics, - 'loading' => true, - ]; - - // Update the cached values - $this->container->get( TransientsInterface::class )->set( - Transients::MC_STATUSES, - $this->mc_statuses, - $this->get_status_lifetime() - ); + return $product_statistics; } /** @@ -802,24 +792,31 @@ protected function calculate_total_synced_product_statistics( array $statistics * @since x.x.x */ public function handle_complete_mc_statuses_fetching() { - $mc_statuses = $this->container->get( TransientsInterface::class )->get( Transients::MC_STATUSES ); + $intermediate_data = $this->options->get( OptionsInterface::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA ); + + if ( $intermediate_data ) { - if ( $mc_statuses ) { - $total_synced_products = $this->calculate_total_synced_product_statistics( $mc_statuses['statistics'] ); + $total_synced_products = $this->calculate_total_synced_product_statistics( $intermediate_data ); /** @var ProductRepository $product_repository */ - $product_repository = $this->container->get( ProductRepository::class ); - $mc_statuses['statistics'][ MCStatus::NOT_SYNCED ] = count( + $product_repository = $this->container->get( ProductRepository::class ); + $intermediate_data[ MCStatus::NOT_SYNCED ] = count( $product_repository->find_all_product_ids() ) - $total_synced_products; - $mc_statuses['loading'] = false; + $mc_statuses = [ + 'timestamp' => $this->cache_created_time->getTimestamp(), + 'statistics' => $intermediate_data, + 'loading' => false, + ]; $this->container->get( TransientsInterface::class )->set( Transients::MC_STATUSES, $mc_statuses, $this->get_status_lifetime() ); + + $this->delete_product_status_count_intermediate_data(); } } diff --git a/src/Options/OptionsInterface.php b/src/Options/OptionsInterface.php index 3dafa5a93b..4125958ee8 100644 --- a/src/Options/OptionsInterface.php +++ b/src/Options/OptionsInterface.php @@ -37,6 +37,7 @@ interface OptionsInterface { public const SITE_VERIFICATION = 'site_verification'; public const SYNCABLE_PRODUCTS_COUNT = 'syncable_products_count'; public const SYNCABLE_PRODUCTS_COUNT_INTERMEDIATE_DATA = 'syncable_products_count_intermediate_data'; + public const PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA = 'products_statuses_count_intermediate_data'; public const TARGET_AUDIENCE = 'target_audience'; public const TOURS = 'tours'; public const UPDATE_ALL_PRODUCTS_LAST_SYNC = 'update_all_products_last_sync'; @@ -68,6 +69,7 @@ interface OptionsInterface { self::SITE_VERIFICATION => true, self::SYNCABLE_PRODUCTS_COUNT => true, self::SYNCABLE_PRODUCTS_COUNT_INTERMEDIATE_DATA => true, + self::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA => true, self::TARGET_AUDIENCE => true, self::TOURS => true, self::UPDATE_ALL_PRODUCTS_LAST_SYNC => true, diff --git a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php index e1df030242..5185a3a827 100644 --- a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php +++ b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php @@ -205,6 +205,9 @@ function ( $statuses ) use ( $matcher ) { $this->merchant_statuses->expects( $this->exactly( 1 ) ) ->method( 'clear_cache' ); + $this->merchant_statuses->expects( $this->exactly( 1 ) ) + ->method( 'delete_product_status_count_intermediate_data' ); + $this->job->schedule(); } } From e2ecf1ba076d8949fc6148b55139aadbbecf8d2f Mon Sep 17 00:00:00 2001 From: Jorge M Date: Tue, 13 Feb 2024 13:51:11 +0100 Subject: [PATCH 17/19] Revert scheduled_sync --- .../ProductStatisticsController.php | 26 ++++++++++++++++--- .../RESTServiceProvider.php | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php b/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php index 553c2a5c2d..4b705bbdec 100644 --- a/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php +++ b/src/API/Site/Controllers/MerchantCenter/ProductStatisticsController.php @@ -5,6 +5,7 @@ use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\BaseOptionsController; use Automattic\WooCommerce\GoogleListingsAndAds\API\TransportMethods; +use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\ProductSyncStats; use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantStatuses; use WP_REST_Response as Response; use WP_REST_Request as Request; @@ -27,16 +28,25 @@ class ProductStatisticsController extends BaseOptionsController { */ protected $merchant_statuses; + /** + * Helper class to count scheduled sync jobs. + * + * @var ProductSyncStats + */ + protected $sync_stats; + /** * ProductStatisticsController constructor. * * @param RESTServer $server * @param MerchantStatuses $merchant_statuses + * @param ProductSyncStats $sync_stats */ - public function __construct( RESTServer $server, MerchantStatuses $merchant_statuses ) { + public function __construct( RESTServer $server, MerchantStatuses $merchant_statuses, ProductSyncStats $sync_stats ) { parent::__construct( $server ); $this->merchant_statuses = $merchant_statuses; + $this->sync_stats = $sync_stats; } /** @@ -100,6 +110,8 @@ protected function get_product_status_stats( Request $request, bool $force_refre try { $response = $this->merchant_statuses->get_product_statistics( $force_refresh ); + $response['scheduled_sync'] = $this->sync_stats->get_count(); + return $this->prepare_item_for_response( $response, $request ); } catch ( Exception $e ) { return $this->response_from_exception( $e ); @@ -113,13 +125,13 @@ protected function get_product_status_stats( Request $request, bool $force_refre */ protected function get_schema_properties(): array { return [ - 'timestamp' => [ + 'timestamp' => [ 'type' => 'number', 'description' => __( 'Timestamp reflecting when the product status statistics were last generated.', 'google-listings-and-ads' ), 'context' => [ 'view' ], 'readonly' => true, ], - 'statistics' => [ + 'statistics' => [ 'type' => 'object', 'description' => __( 'Merchant Center product status statistics.', 'google-listings-and-ads' ), 'context' => [ 'view' ], @@ -152,7 +164,13 @@ protected function get_schema_properties(): array { ], ], ], - 'loading' => [ + 'scheduled_sync' => [ + 'type' => 'number', + 'description' => __( 'Amount of scheduled jobs which will sync products to Google.', 'google-listings-and-ads' ), + 'context' => [ 'view' ], + 'readonly' => true, + ], + 'loading' => [ 'type' => 'boolean', 'description' => __( 'Whether the product statistics are loading.', 'google-listings-and-ads' ), 'context' => [ 'view' ], diff --git a/src/Internal/DependencyManagement/RESTServiceProvider.php b/src/Internal/DependencyManagement/RESTServiceProvider.php index 6f2dcc2eec..d8cf483d77 100644 --- a/src/Internal/DependencyManagement/RESTServiceProvider.php +++ b/src/Internal/DependencyManagement/RESTServiceProvider.php @@ -110,7 +110,7 @@ public function register() { $this->share_with_container( AdsReportsController::class ); $this->share( GoogleAccountController::class, Connection::class ); $this->share( JetpackAccountController::class, Manager::class, Middleware::class ); - $this->share( MerchantCenterProductStatsController::class, MerchantStatuses::class ); + $this->share( MerchantCenterProductStatsController::class, MerchantStatuses::class, ProductSyncStats::class ); $this->share( MerchantCenterIssuesController::class, MerchantStatuses::class, ProductHelper::class ); $this->share( MerchantCenterProductFeedController::class, ProductFeedQueryHelper::class ); $this->share( MerchantCenterProductVisibilityController::class, ProductHelper::class, MerchantIssueQuery::class ); From 4b76332ac50be974610823267bf00d2f3d1688ed Mon Sep 17 00:00:00 2001 From: Jorge M Date: Tue, 13 Feb 2024 16:59:51 +0100 Subject: [PATCH 18/19] Fix typo --- src/Jobs/UpdateMerchantProductStatuses.php | 2 +- src/MerchantCenter/MerchantStatuses.php | 6 +++--- src/Options/OptionsInterface.php | 2 +- tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Jobs/UpdateMerchantProductStatuses.php b/src/Jobs/UpdateMerchantProductStatuses.php index a322dd3d27..7ab06f6574 100644 --- a/src/Jobs/UpdateMerchantProductStatuses.php +++ b/src/Jobs/UpdateMerchantProductStatuses.php @@ -87,7 +87,7 @@ public function process_items( array $items ) { // Clear the cache if we're starting from the beginning. if ( ! $next_page_token ) { $this->merchant_statuses->clear_cache(); - $this->merchant_statuses->delete_product_status_count_intermediate_data(); + $this->merchant_statuses->delete_product_statuses_count_intermediate_data(); } $results = $this->merchant_report->get_product_view_report( $next_page_token ); diff --git a/src/MerchantCenter/MerchantStatuses.php b/src/MerchantCenter/MerchantStatuses.php index 7deabefa8f..c1de494e56 100644 --- a/src/MerchantCenter/MerchantStatuses.php +++ b/src/MerchantCenter/MerchantStatuses.php @@ -185,7 +185,7 @@ public function clear_cache(): void { * * @since x.x.x */ - public function delete_product_status_count_intermediate_data(): void { + public function delete_product_statuses_count_intermediate_data(): void { $this->options->delete( OptionsInterface::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA ); } @@ -725,7 +725,7 @@ protected function update_intermediate_product_statistics(): array { MCStatus::NOT_SYNCED => 0, ]; - // If the transient is set, use it to sum the total quantity. + // If the option is set, use it to sum the total quantity. $product_statistics_intermediate_data = $this->options->get( OptionsInterface::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA ); if ( $product_statistics_intermediate_data ) { $product_statistics = $product_statistics_intermediate_data; @@ -816,7 +816,7 @@ public function handle_complete_mc_statuses_fetching() { $this->get_status_lifetime() ); - $this->delete_product_status_count_intermediate_data(); + $this->delete_product_statuses_count_intermediate_data(); } } diff --git a/src/Options/OptionsInterface.php b/src/Options/OptionsInterface.php index 4125958ee8..180843bb95 100644 --- a/src/Options/OptionsInterface.php +++ b/src/Options/OptionsInterface.php @@ -37,7 +37,7 @@ interface OptionsInterface { public const SITE_VERIFICATION = 'site_verification'; public const SYNCABLE_PRODUCTS_COUNT = 'syncable_products_count'; public const SYNCABLE_PRODUCTS_COUNT_INTERMEDIATE_DATA = 'syncable_products_count_intermediate_data'; - public const PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA = 'products_statuses_count_intermediate_data'; + public const PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA = 'product_statuses_count_intermediate_data'; public const TARGET_AUDIENCE = 'target_audience'; public const TOURS = 'tours'; public const UPDATE_ALL_PRODUCTS_LAST_SYNC = 'update_all_products_last_sync'; diff --git a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php index 5185a3a827..ea70145aa3 100644 --- a/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php +++ b/tests/Unit/Jobs/UpdateMerchantProductStatusesTest.php @@ -206,7 +206,7 @@ function ( $statuses ) use ( $matcher ) { ->method( 'clear_cache' ); $this->merchant_statuses->expects( $this->exactly( 1 ) ) - ->method( 'delete_product_status_count_intermediate_data' ); + ->method( 'delete_product_statuses_count_intermediate_data' ); $this->job->schedule(); } From 2e2624273ec1148925b5a70b0d054676ae349280 Mon Sep 17 00:00:00 2001 From: Jorge M Date: Tue, 13 Feb 2024 22:39:49 +0100 Subject: [PATCH 19/19] Query all product types --- src/Product/ProductRepository.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Product/ProductRepository.php b/src/Product/ProductRepository.php index 0195df971f..2e9bea0311 100644 --- a/src/Product/ProductRepository.php +++ b/src/Product/ProductRepository.php @@ -261,11 +261,10 @@ public function find_expiring_product_ids( int $limit = - 1, int $offset = 0 ): * @return int[] Array of WooCommerce product IDs */ public function find_all_product_ids( int $limit = -1, int $offset = 0 ): array { - $args['return'] = 'ids'; - $args = [ - 'type' => array_diff( ProductSyncer::get_supported_product_types(), [ 'variation' ] ), 'status' => 'publish', + 'return' => 'ids', + 'type' => 'any', ]; return $this->find_ids( $args, $limit, $offset ); @@ -341,6 +340,11 @@ protected function prepare_query_args( array $args = [] ): array { $args['type'] = ProductSyncer::get_supported_product_types(); } + // It'll fetch all products with the post_type of 'product', excluding variations. + if ( $args['type'] === 'any' ) { + unset( $args['type'] ); + } + // use no ordering unless specified in arguments. overrides the default WooCommerce query args if ( empty( $args['orderby'] ) ) { $args['orderby'] = 'none';