Skip to content

Commit

Permalink
Merge pull request #2472 from woocommerce/update/enable-page-size-in-…
Browse files Browse the repository at this point in the history
…campaigns-endpoint

Enable page size in campaigns endpoint
  • Loading branch information
jorgemd24 authored Aug 5, 2024
2 parents 81caa3e + 6ff5916 commit 603211e
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 16 deletions.
29 changes: 22 additions & 7 deletions src/API/Google/AdsCampaign.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,17 @@ public function __construct( GoogleAdsClient $client, AdsCampaignBudget $budget,
/**
* Returns a list of campaigns with targeted locations retrieved from campaign criterion.
*
* @param bool $exclude_removed Exclude removed campaigns (default true).
* @param bool $fetch_criterion Combine the campaign data with criterion data (default true).
* @param bool $exclude_removed Exclude removed campaigns (default true).
* @param bool $fetch_criterion Combine the campaign data with criterion data (default true).
* @param array $args Arguments for the Ads Campaign Query for example: per_page for limiting the number of results.
* @param bool $return_pagination_params Whether to return pagination params (default false).
*
* @return array
* @throws ExceptionWithResponseData When an ApiException is caught.
*/
public function get_campaigns( bool $exclude_removed = true, bool $fetch_criterion = true ): array {
public function get_campaigns( bool $exclude_removed = true, bool $fetch_criterion = true, $args = [], $return_pagination_params = false ): array {
try {
$query = ( new AdsCampaignQuery() )->set_client( $this->client, $this->options->get_ads_id() );
$query = ( new AdsCampaignQuery( $args ) )->set_client( $this->client, $this->options->get_ads_id() );

if ( $exclude_removed ) {
$query->where( 'campaign.status', 'REMOVED', '!=' );
Expand All @@ -124,7 +126,10 @@ public function get_campaigns( bool $exclude_removed = true, bool $fetch_criteri
$campaign_results = $query->get_results();
$converted_campaigns = [];

foreach ( $campaign_results->iterateAllElements() as $row ) {
/** @var Page $page */
$page = $campaign_results->getPage();

foreach ( $page->getIterator() as $row ) {
++$campaign_count;
$campaign = $this->convert_campaign( $row );
$converted_campaigns[ $campaign['id'] ] = $campaign;
Expand All @@ -143,6 +148,16 @@ public function get_campaigns( bool $exclude_removed = true, bool $fetch_criteri
$converted_campaigns = $this->combine_campaigns_and_campaign_criterion_results( $converted_campaigns );
}

if ( $return_pagination_params ) {
// Total results across all pages.
$total_results = $page->getResponseObject()->getTotalResultsCount();
$next_page_token = $page->getNextPageToken();
return [
'campaigns' => array_values( $converted_campaigns ),
'total_results' => $total_results,
'next_page_token' => $next_page_token,
];
}
return array_values( $converted_campaigns );
} catch ( ApiException $e ) {
do_action( 'woocommerce_gla_ads_client_exception', $e, __METHOD__ );
Expand All @@ -168,14 +183,14 @@ public function get_campaigns( bool $exclude_removed = true, bool $fetch_criteri
*/
public function get_campaign( int $id ): array {
try {
$campaign_results = ( new AdsCampaignQuery() )->set_client( $this->client, $this->options->get_ads_id() )
$campaign_results = ( new AdsCampaignQuery( [ 'per_page' => 1 ] ) )->set_client( $this->client, $this->options->get_ads_id() )
->where( 'campaign.id', $id, '=' )
->get_results();

$converted_campaigns = [];

// Get only the first element from campaign results
foreach ( $campaign_results->iterateAllElements() as $row ) {
foreach ( $campaign_results->getPage()->getIterator() as $row ) {
$campaign = $this->convert_campaign( $row );
$converted_campaigns[ $campaign['id'] ] = $campaign;
break;
Expand Down
9 changes: 8 additions & 1 deletion src/API/Google/Query/AdsCampaignQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@
*/
class AdsCampaignQuery extends AdsQuery {

use ReportQueryTrait;


/**
* Query constructor.
*
* @param array $args Query arguments.
*/
public function __construct() {
public function __construct( $args = [] ) {
parent::__construct( 'campaign' );
$this->columns(
[
Expand All @@ -27,5 +32,7 @@ public function __construct() {
'campaign_budget.amount_micros',
]
);

$this->handle_query_args( $args );
}
}
8 changes: 8 additions & 0 deletions src/API/Google/Query/AdsQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,16 @@ protected function query_results() {
}

$request = new SearchGoogleAdsRequest();
// Allow us to get the total number of results for pagination.
$request->setReturnTotalResultsCount( true );

if ( ! empty( $this->search_args['pageSize'] ) ) {
$request->setPageSize( $this->search_args['pageSize'] );
}

$request->setQuery( $this->build_query() );
$request->setCustomerId( $this->id );

$this->results = $this->client->getGoogleAdsServiceClient()->search( $request );
}
}
32 changes: 29 additions & 3 deletions src/API/Site/Controllers/Ads/CampaignController.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,33 @@ public function register_routes(): void {
protected function get_campaigns_callback(): callable {
return function ( Request $request ) {
try {
$exclude_removed = $request->get_param( 'exclude_removed' );
$exclude_removed = $request->get_param( 'exclude_removed' );
$return_pagination_params = true;
$campaign_data = $this->ads_campaign->get_campaigns( $exclude_removed, true, $request->get_params(), $return_pagination_params );

return array_map(
$campaigns = array_map(
function ( $campaign ) use ( $request ) {
$data = $this->prepare_item_for_response( $campaign, $request );
return $this->prepare_response_for_collection( $data );
},
$this->ads_campaign->get_campaigns( $exclude_removed )
$campaign_data['campaigns']
);

$response = rest_ensure_response( $campaigns );

$total_campaigns = (int) $campaign_data['total_results'];
$response->header( 'X-WP-Total', $total_campaigns );
// If per_page is not set, then set it to total number of campaigns.
$per_page = $request->get_param( 'per_page' ) ?: $total_campaigns;
$max_pages = $per_page > 0 ? ceil( $total_campaigns / $per_page ) : 1;
$response->header( 'X-WP-TotalPages', (int) $max_pages );

if ( ! empty( $campaign_data['next_page_token'] ) ) {
$response->header( 'X-GLA-NextPageToken', $campaign_data['next_page_token'] );
}

return $response;

} catch ( Exception $e ) {
return $this->response_from_exception( $e );
}
Expand Down Expand Up @@ -320,6 +338,14 @@ public function get_collection_params(): array {
'default' => true,
'validate_callback' => 'rest_validate_request_arg',
],
'per_page' => [
'description' => __( 'Maximum number of rows to be returned in result data.', 'google-listings-and-ads' ),
'type' => 'integer',
'minimum' => 1,
'maximum' => 1000,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
],
];
}

Expand Down
24 changes: 21 additions & 3 deletions tests/Tools/HelperTrait/GoogleAdsClientTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
use Google\Ads\GoogleAds\V16\Services\MutateOperation;
use Google\Ads\GoogleAds\V16\Services\MutateAssetGroupResult;
use Google\Ads\GoogleAds\V16\Services\MutateAssetResult;
use Google\Ads\GoogleAds\V16\Services\SearchGoogleAdsResponse;
use Google\ApiCore\ApiException;
use Google\ApiCore\Page;
use Google\ApiCore\PagedListResponse;
Expand Down Expand Up @@ -187,17 +188,32 @@ protected function generate_ads_multiple_query_mock( array $responses ) {
*
* @param array $campaigns_responses Set of campaign data to convert.
* @param array $campaign_criterion_responses Set of campaign criterion data to convert.
* @param bool $assert_pagination Whether to assert pagination.
*/
protected function generate_ads_campaign_query_mock( array $campaigns_responses, $campaign_criterion_responses ) {
protected function generate_ads_campaign_query_mock( array $campaigns_responses, $campaign_criterion_responses, $assert_pagination = false ) {
$campaigns_row_mock = array_map( [ $this, 'generate_campaign_row_mock' ], $campaigns_responses );
$campaign_criterion_row_mock = array_map( [ $this, 'generate_campaign_criterion_row_mock' ], $campaign_criterion_responses );

$list_response = $this->createMock( PagedListResponse::class );
$list_response->method( 'iterateAllElements' )->willReturnOnConsecutiveCalls(
$page = $this->createMock( Page::class );

if ( $assert_pagination ) {
$response_object = $this->createMock( SearchGoogleAdsResponse::class );
$response_object->expects( $this->exactly( 1 ) )->method( 'getTotalResultsCount' )->willReturn( count( $campaigns_responses ) );
$page->expects( $this->exactly( 1 ) )->method( 'getNextPageToken' )->willReturn( '' );
$page->method( 'getResponseObject' )->willReturn( $response_object );
}

$page->method( 'getIterator' )->willReturn(
$campaigns_row_mock,
);

$list_response->method( 'iterateAllElements' )->willReturn(
$campaign_criterion_row_mock
);

$list_response->method( 'getPage' )->willReturn( $page );

$this->service_client
->method( 'search' )->willReturn( $list_response );
}
Expand Down Expand Up @@ -311,7 +327,9 @@ function ( MutateGoogleAdsRequest $request ) use ( $response, $label_id, $campai
*/
protected function generate_ads_campaign_query_mock_with_no_campaigns() {
$list_response = $this->createMock( PagedListResponse::class );
$list_response->method( 'iterateAllElements' )->willReturn( [] );
$page = $this->createMock( Page::class );
$page->method( 'getIterator' )->willReturn( [] );
$list_response->method( 'getPage' )->willReturn( $page );

// Method search() will only being called once by AdsCampaignQuery
// since there were no campaigns returned by AdsCampaignQuery, it
Expand Down
67 changes: 67 additions & 0 deletions tests/Unit/API/Google/AdsCampaignTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,73 @@ public function test_get_campaigns() {
$this->assertEquals( $campaigns_data, $this->campaign->get_campaigns() );
}

public function test_get_campaigns_with_search_args() {
$campaign_criterion_data = [
[
'campaign_id' => self::TEST_CAMPAIGN_ID,
'geo_target_constant' => 'geoTargetConstants/2158',
],
[
'campaign_id' => 5678901234,
'geo_target_constant' => 'geoTargetConstants/2344',
],
[
'campaign_id' => 5678901234,
'geo_target_constant' => 'geoTargetConstants/2826',
],
];

$campaigns_data = [
[
'id' => self::TEST_CAMPAIGN_ID,
'name' => 'Campaign One',
'status' => 'paused',
'type' => 'shopping',
'amount' => 10,
'country' => 'US',
'targeted_locations' => [ 'TW' ],
],
[
'id' => 5678901234,
'name' => 'Campaign Two',
'status' => 'enabled',
'type' => 'performance_max',
'amount' => 20,
'country' => 'UK',
'targeted_locations' => [ 'HK', 'GB' ],
],
];

$this->generate_ads_campaign_query_mock( $campaigns_data, $campaign_criterion_data, true );

$matcher = $this->exactly( 2 );
$this->service_client
->expects( $matcher )
->method( 'search' )
->willReturnCallback(
function ( $request ) use ( $matcher ) {
if ( $matcher->getInvocationCount() === 1 ) {
$this->assertEquals( 2, $request->getPageSize() ); // Campaigns
}

if ( $matcher->getInvocationCount() === 2 ) {
$this->assertEquals( 0, $request->getPageSize() ); // Criterions
}

return true;
}
);

$this->assertEquals(
[
'campaigns' => $campaigns_data,
'total_results' => count( $campaigns_data ),
'next_page_token' => '',
],
$this->campaign->get_campaigns( true, true, [ 'per_page' => 2 ], true )
);
}

public function test_get_campaigns_with_nonexist_location_id() {
$campaign_criterion_data = [
[
Expand Down
Loading

0 comments on commit 603211e

Please sign in to comment.