-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Label campaigns for the web version and the WC Mobile app #2514
Changes from 3 commits
0eab6b5
34a4c32
14f1070
bdfbae5
6fb9af7
7678e21
959a0b1
4feded9
288b8b8
7ce5d62
9ccf440
aa6185e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,6 +8,7 @@ | |||||||||||||||||||||||||
use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\CampaignType; | ||||||||||||||||||||||||||
use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\BaseController; | ||||||||||||||||||||||||||
use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\CountryCodeTrait; | ||||||||||||||||||||||||||
use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\MobileTrait; | ||||||||||||||||||||||||||
use Automattic\WooCommerce\GoogleListingsAndAds\API\TransportMethods; | ||||||||||||||||||||||||||
use Automattic\WooCommerce\GoogleListingsAndAds\Google\GoogleHelperAwareInterface; | ||||||||||||||||||||||||||
use Automattic\WooCommerce\GoogleListingsAndAds\Internal\Interfaces\ISO3166AwareInterface; | ||||||||||||||||||||||||||
|
@@ -27,6 +28,7 @@ | |||||||||||||||||||||||||
class CampaignController extends BaseController implements GoogleHelperAwareInterface, ISO3166AwareInterface { | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
use CountryCodeTrait; | ||||||||||||||||||||||||||
use MobileTrait; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||
* @var AdsCampaign | ||||||||||||||||||||||||||
|
@@ -152,6 +154,14 @@ protected function create_campaign_callback(): callable { | |||||||||||||||||||||||||
); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if ( $this->is_wc_mobile_app( $request ) ) { | ||||||||||||||||||||||||||
if ( $this->is_wc_ios( $request ) ) { | ||||||||||||||||||||||||||
$fields['label'] = 'wc-ios'; | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
$fields['label'] = 'wc-android'; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PHP will do some optimization but if we follow the code path this will technically fetch the user agent header and then check if it contains the string
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
$campaign = $this->ads_campaign->create_campaign( $fields ); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
declare( strict_types=1 ); | ||
|
||
namespace Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers; | ||
|
||
use WP_REST_Request; | ||
|
||
defined( 'ABSPATH' ) || exit; | ||
|
||
/** | ||
* Trait MobileTrait | ||
* | ||
* @package Automattic\WooCommerce\GoogleListingsAndAds\API | ||
*/ | ||
trait MobileTrait { | ||
|
||
/** | ||
* Check if the request is from WooCommerce iOS app. | ||
* | ||
* @param WP_REST_Request $request | ||
* | ||
* @return bool | ||
*/ | ||
public function is_wc_ios( WP_REST_Request $request ) { | ||
return $request->get_header( 'User-Agent' ) && stristr( $request->get_header( 'User-Agent' ), 'wc-ios' ); | ||
} | ||
|
||
/** | ||
* Check if the request is from WooCommerce Android app. | ||
* | ||
* @param WP_REST_Request $request | ||
* | ||
* @return bool | ||
*/ | ||
public function is_wc_android( WP_REST_Request $request ) { | ||
return $request->get_header( 'User-Agent' ) && stristr( $request->get_header( 'User-Agent' ), 'wc-android' ); | ||
} | ||
|
||
/** | ||
* Check if the request is from WooCommerce mobile app. | ||
* | ||
* @param WP_REST_Request $request | ||
* | ||
* @return bool | ||
*/ | ||
public function is_wc_mobile_app( WP_REST_Request $request ) { | ||
return $this->is_wc_ios( $request ) || $this->is_wc_android( $request ); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -332,6 +332,99 @@ public function test_create_campaign() { | |||||||
$this->assertEquals( 200, $response->get_status() ); | ||||||||
} | ||||||||
|
||||||||
public function test_create_campaign_with_label() { | ||||||||
$campaign_data = [ | ||||||||
'name' => 'New Campaign', | ||||||||
'amount' => 20, | ||||||||
'targeted_locations' => [ 'US', 'GB', 'TW' ], | ||||||||
'label' => 'wc-web', | ||||||||
]; | ||||||||
|
||||||||
$expected = [ | ||||||||
'id' => self::TEST_CAMPAIGN_ID, | ||||||||
'status' => 'enabled', | ||||||||
'type' => 'performance_max', | ||||||||
'country' => self::BASE_COUNTRY, | ||||||||
'name' => 'New Campaign', | ||||||||
'amount' => 20, | ||||||||
'targeted_locations' => [ 'US', 'GB', 'TW' ], | ||||||||
]; | ||||||||
|
||||||||
$this->ads_campaign->expects( $this->once() ) | ||||||||
->method( 'create_campaign' ) | ||||||||
->with( $campaign_data ) | ||||||||
->willReturn( $expected ); | ||||||||
|
||||||||
$this->expect_track_event( | ||||||||
'created_campaign', | ||||||||
[ | ||||||||
'id' => self::TEST_CAMPAIGN_ID, | ||||||||
'status' => 'enabled', | ||||||||
'name' => 'New Campaign', | ||||||||
'amount' => 20, | ||||||||
'country' => self::BASE_COUNTRY, | ||||||||
'targeted_locations' => 'US,GB,TW', | ||||||||
] | ||||||||
); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just looking at this track here. Wouldn't it be useful to also pass the source? Or is it going to still include the user agent string so it can be extracted from there? |
||||||||
|
||||||||
$response = $this->do_request( self::ROUTE_CAMPAIGNS, 'POST', $campaign_data ); | ||||||||
|
||||||||
$this->assertEquals( $expected, $response->get_data() ); | ||||||||
$this->assertEquals( 200, $response->get_status() ); | ||||||||
} | ||||||||
|
||||||||
public function test_create_campaign_with_mobile() { | ||||||||
$user_agents = [ | ||||||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 wc-ios/19.7.1', | ||||||||
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 wc-android/19.7.1', | ||||||||
]; | ||||||||
|
||||||||
$campaign_data = [ | ||||||||
'name' => 'New Campaign', | ||||||||
'amount' => 20, | ||||||||
'targeted_locations' => [ 'US', 'GB', 'TW' ], | ||||||||
]; | ||||||||
|
||||||||
$expected = [ | ||||||||
'id' => self::TEST_CAMPAIGN_ID, | ||||||||
'status' => 'enabled', | ||||||||
'type' => 'performance_max', | ||||||||
'country' => self::BASE_COUNTRY, | ||||||||
'name' => 'New Campaign', | ||||||||
'amount' => 20, | ||||||||
'targeted_locations' => [ 'US', 'GB', 'TW' ], | ||||||||
]; | ||||||||
|
||||||||
$matcher = $this->exactly( 2 ); | ||||||||
$this->ads_campaign->expects( $matcher ) | ||||||||
->method( 'create_campaign' ) | ||||||||
->willReturnCallback( | ||||||||
function ( $data ) use ( $campaign_data, $matcher, $expected ) { | ||||||||
$invokation = $matcher->getInvocationCount(); | ||||||||
if ( $invokation === 1 ) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a big deal, but I would spell the variable with a
Suggested change
|
||||||||
$this->assertEquals( $data, $campaign_data + [ 'label' => 'wc-ios' ] ); | ||||||||
} else { | ||||||||
$this->assertEquals( $data, $campaign_data + [ 'label' => 'wc-android' ] ); | ||||||||
} | ||||||||
return $expected; | ||||||||
} | ||||||||
); | ||||||||
|
||||||||
foreach ( $user_agents as $user_agent ) { | ||||||||
$response = $this->do_request( | ||||||||
self::ROUTE_CAMPAIGNS, | ||||||||
'POST', | ||||||||
$campaign_data, | ||||||||
[ | ||||||||
'User-Agent' => $user_agent, | ||||||||
] | ||||||||
); | ||||||||
|
||||||||
$this->assertEquals( $expected, $response->get_data() ); | ||||||||
$this->assertEquals( 200, $response->get_status() ); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
public function test_create_campaign_without_name() { | ||||||||
$campaign_data = [ | ||||||||
'amount' => 30, | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php | ||
declare( strict_types=1 ); | ||
|
||
namespace Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\API\Site\Controllers; | ||
|
||
use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\MobileTrait; | ||
use PHPUnit\Framework\TestCase; | ||
use WP_REST_Request; | ||
|
||
/** | ||
* Class MobileTraitTest | ||
* | ||
* @package Automattic\WooCommerce\GoogleListingsAndAds\Tests\Unit\API\Site\Controllers | ||
*/ | ||
class MobileTraitTest extends TestCase { | ||
|
||
/** @var MobileTrait $trait */ | ||
protected $trait; | ||
|
||
/** | ||
* Runs before each test is executed. | ||
*/ | ||
public function setUp(): void { | ||
parent::setUp(); | ||
|
||
// phpcs:ignore Universal.Classes.RequireAnonClassParentheses.Missing | ||
$this->trait = new class { | ||
use MobileTrait; | ||
}; | ||
} | ||
|
||
/** | ||
* Test is a WooCommerce iOS app. | ||
*/ | ||
public function test_is_wc_ios() { | ||
$request = new WP_REST_Request(); | ||
$request->set_header( 'User-Agent', 'Mozilla/5.0 AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 wc-ios/19.7.1' ); | ||
$this->assertTrue( $this->trait->is_wc_ios( $request ) ); | ||
$this->assertFalse( $this->trait->is_wc_android( $request ) ); | ||
$this->assertTrue( $this->trait->is_wc_mobile_app( $request ) ); | ||
} | ||
|
||
/** | ||
* Test is a WooCommerce Android app. | ||
*/ | ||
public function test_is_wc_android() { | ||
$request = new WP_REST_Request(); | ||
$request->set_header( 'User-Agent', 'Mozilla/5.0 (Linux; Android 10; K) Chrome/114.0.0.0 Mobile Safari/537.36 wc-android/19.7.1' ); | ||
$this->assertTrue( $this->trait->is_wc_android( $request ) ); | ||
$this->assertFalse( $this->trait->is_wc_ios( $request ) ); | ||
$this->assertTrue( $this->trait->is_wc_mobile_app( $request ) ); | ||
} | ||
|
||
/** | ||
* Test is not a WooCommerce mobile app. | ||
*/ | ||
public function test_is_not_wc_app() { | ||
$request = new WP_REST_Request(); | ||
$request->set_header( 'User-Agent', 'Mozilla/5.0 AppleWebKit/605.1.15 (KHTML, like Gecko)' ); | ||
$this->assertFalse( $this->trait->is_wc_ios( $request ) ); | ||
$this->assertFalse( $this->trait->is_wc_android( $request ) ); | ||
$this->assertFalse( $this->trait->is_wc_mobile_app( $request ) ); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming this means we are defaulting the label to
wc-web
even on mobile, and then only overwriting the value once we process the request in PHP. It also means that when testing for mobile if I view the requests it's sending a request withwc-web
but the final campaign gets a different label.Is that in preparation for the mobile eventually calling the API directly? I was imagining that in this implementation we'd fetch the user agent in JS and then populate this label accordingly. Fine either approach though if it's better to detect the user agent in PHP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! My initial thought was to detect the header directly on the server, but I like the idea of moving the logic to the client. It keeps the API agnostic to the labels and lets the client handle setting them. If the mobile team wants to use the API to create campaigns, they can simply add the label parameter in the body.