From 016fca3b33d926d15c43000c8fa942ad25669073 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 16:16:42 +0100 Subject: [PATCH 01/10] Remove AC tests and start working on adding HubSpot --- src/Analytics/Adapter/ActiveCampaign.php | 1 - src/Analytics/Adapter/HubSpot.php | 313 +++++++++++++++++++++++ tests/Analytics/AnalyticsTest.php | 83 +++--- 3 files changed, 352 insertions(+), 45 deletions(-) create mode 100644 src/Analytics/Adapter/HubSpot.php diff --git a/src/Analytics/Adapter/ActiveCampaign.php b/src/Analytics/Adapter/ActiveCampaign.php index 71f84a4..d6df567 100644 --- a/src/Analytics/Adapter/ActiveCampaign.php +++ b/src/Analytics/Adapter/ActiveCampaign.php @@ -234,7 +234,6 @@ public function deleteAccount(string $accountId): bool public function syncAssociation(string $accountId, string $contactId, string $role = ''): bool { // See if the association already exists - try { $result = $this->call('GET', '/api/3/accountContacts', [], [ 'filters[account]' => $accountId, diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php new file mode 100644 index 0000000..45081a3 --- /dev/null +++ b/src/Analytics/Adapter/HubSpot.php @@ -0,0 +1,313 @@ +headers = [ + 'Authorization' => 'Bearer '.$token, + ]; + } + + /** + * Creates an Event on the remote analytics platform. + */ + public function send(Event $event): bool + { + if (!$this->enabled) { + return false; + } + + // HubSpot event tracking isn't possible due to their chrome based extention system + return true; + } + + public function validate(Event $event): bool + { + if (!$this->enabled) { + return false; + } + + // HubSpot event tracking isn't possible due to their chrome based extention system + return true; + } + + /** + * Gets the name of the adapter. + */ + public function getName(): string + { + return 'HubSpot'; + } + + /** + * Checks if a contact exists by the email ID. Returns the User ID if it exists and false if it doesn't. + */ + public function contactExists(string $email): bool|int + { + try { + $result = $this->call('GET', '/crm/v3/objects/contacts/search', [], [ + 'filterGroups' => [ + 'filters' => [ + [ + 'value' => $email, + 'propertyName' => 'email', + 'operator' => 'EQ', + ], + ], + ], + ]); + + $result = json_decode($result, true); + + if ($result && $result['total'] > 0) { + return $result['results'][0]['id']; + } else { + return false; + } + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Create a contact + */ + public function createContact(string $email, string $firstName = '', string $lastName = '', string $phone = ''): bool + { + $body = ['contact' => [ + 'email' => $email, + 'firstname' => $firstName, + 'lastname' => $lastName, + 'phone' => $phone, + ]]; + + try { + $this->call('POST', '/crm/v3/objects/contacts', [ + 'Content-Type' => 'application/json', + ], $body); + + return true; + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Update contact + */ + public function updateContact(string $contactId, string $email, string $firstName = '', string $lastName = '', string $phone = ''): bool + { + $body = ['contact' => [ + 'email' => $email, + 'firstname' => $firstName, + 'lastname' => $lastName, + 'phone' => $phone, + ]]; + + try { + $this->call('PATCH', '/crm/v3/objects/contacts/'.$contactId, [ + 'Content-Type' => 'application/json', + ], $body); + + return true; + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Delete a contact + */ + public function deleteContact(string $email): bool + { + $contact = $this->contactExists($email); + + if (! $contact) { + return false; + } + + try { + $this->call('DELETE', '/crm/v3/objects/contacts/'.$contact, [ + 'Content-Type' => 'application/json', + ]); + + return true; + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Account Exists + */ + public function accountExists(string $name): bool|int + { + try { + $result = $this->call('GET', '/crm/v3/objects/companies/search', [], [ + 'filterGroups' => [ + 'filters' => [ + [ + 'value' => $name, + 'propertyName' => 'name', + 'operator' => 'EQ', + ], + ], + ], + ]); + + $result = json_decode($result, true); + + if ($result && $result['total'] > 0) { + return $result['results'][0]['id']; + } else { + return false; + } + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Create an account + */ + public function createAccount(string $name, string $url = ''): bool + { + $body = [ + 'name' => $name, + 'domain' => $url, + ]; + + try { + $this->call('POST', '/crm/v3/objects/companies', [ + 'Content-Type' => 'application/json', + ], $body); + + return true; + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Update an account + */ + public function updateAccount(string $accountId, string $name, string $url = '', int $ownerId = 1, array $fields = []): bool + { + $body = [ + 'name' => $name, + 'domain' => $url, + ]; + + try { + $this->call('PATCH', '/crm/v3/objects/companies/'.$accountId, [ + 'Content-Type' => 'application/json', + ], $body); + + return true; + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Delete an account + */ + public function deleteAccount(string $accountId): bool + { + try { + $this->call('DELETE', '/crm/v3/objects/companies/'.$accountId, [ + 'Content-Type' => 'application/json', + ]); + + return true; + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } + + /** + * Sync an association + * + * Creates an association if it doesn't exist and updates it if it does + */ + public function syncAssociation(string $accountId, string $contactId, string $role = ''): bool + { + // See if the association already exists + + try { + $response = $this->call('GET', '/crm/v4/objects/contact/'.$accountId.'/associations/company'); + + $response = json_decode($response, true); + + $associationId = null; + + foreach ($response['results'] as $association) { + if ($association['from']['id'] == $contactId) { + $associationId = $association['id']; + } + } + + if (empty($associationId)) { + // Create the association + $body = [ + 'from' => [ + 'id' => $contactId, + 'type' => 'CONTACT', + ], + 'to' => [ + 'id' => $accountId, + 'type' => 'COMPANY', + ], + 'category' => 'HUBSPOT_DEFINED', + 'definitionId' => 1, + ]; + + $this->call('PUT', '/crm/v4/objects/contact/'.$accountId.'/associations/company', [ + 'Content-Type' => 'application/json', + ], $body); + } else { + // Update the association + $body = [ + 'category' => 'HUBSPOT_DEFINED', + 'definitionId' => 1, + ]; + + $this->call('PATCH', '/crm/v4/objects/contact/'.$accountId.'/associations/company/'.$associationId, [ + 'Content-Type' => 'application/json', + ], $body); + } + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } +} \ No newline at end of file diff --git a/tests/Analytics/AnalyticsTest.php b/tests/Analytics/AnalyticsTest.php index e2a1d7a..000bce1 100644 --- a/tests/Analytics/AnalyticsTest.php +++ b/tests/Analytics/AnalyticsTest.php @@ -3,7 +3,7 @@ namespace Utopia\Tests; use PHPUnit\Framework\TestCase; -use Utopia\Analytics\Adapter\ActiveCampaign; +use Utopia\Analytics\Adapter\HubSpot; use Utopia\Analytics\Adapter\GoogleAnalytics; use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Adapter\Orbit; @@ -16,9 +16,6 @@ class AnalyticsTest extends TestCase /** @var \Utopia\Analytics\Adapter\GoogleAnalytics */ public $ga; - /** @var \Utopia\Analytics\Adapter\ActiveCampaign|null */ - public $ac; - /** @var \Utopia\Analytics\Adapter\Plausible */ public $pa; @@ -28,18 +25,16 @@ class AnalyticsTest extends TestCase /** @var \Utopia\Analytics\Adapter\Mixpanel */ public $mp; + /** @var \Utopia\Analytics\Adapter\HubSpot */ + public $hs; + public function setUp(): void { $this->ga = new GoogleAnalytics(App::getEnv('GA_TID'), App::getEnv('GA_CID')); - $this->ac = new ActiveCampaign( - App::getEnv('AC_KEY'), - App::getEnv('AC_ACTID'), - App::getEnv('AC_APIKEY'), - App::getEnv('AC_ORGID') - ); $this->pa = new Plausible(App::getEnv('PA_DOMAIN'), App::getEnv('PA_APIKEY'), 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', '192.168.0.1'); $this->orbit = new Orbit(App::getEnv('OR_WORKSPACEID'), App::getEnv('OR_APIKEY'), 'Utopia Testing Suite'); $this->mp = new Mixpanel(App::getEnv('MP_PROJECT_TOKEN')); + $this->hs = new HubSpot(App::getEnv('HS_APIKEY')); } public function testGoogleAnalytics(): void @@ -83,14 +78,14 @@ public function testPlausible() $this->assertTrue($this->pa->validate($normalEvent)); } - public function testActiveCampaignCreateContact() + public function testHubSpotCreateContact() { - $this->assertTrue($this->ac->createContact('analytics2@utopiaphp.com', 'Analytics', 'Utopia')); + $this->assertTrue($this->hs->createContact('analytics2@utopiaphp.com', 'Analytics', 'Utopia')); } - public function testActiveCampaignGetContact() + public function testHubSpotGetContact() { - $contactID = $this->ac->contactExists('analytics2@utopiaphp.com'); + $contactID = $this->hs->contactExists('analytics2@utopiaphp.com'); $this->assertIsNumeric($contactID); return [ @@ -98,17 +93,17 @@ public function testActiveCampaignGetContact() ]; } - public function testActiveCampaignCreateAccount() + public function testHubSpotCreateAccount() { - $this->assertTrue($this->ac->createAccount('Example Account 1', 'https://example.com', '1234567890')); + $this->assertTrue($this->hs->createAccount('Example Account 1', 'https://example.com', '1234567890')); } /** - * @depends testActiveCampaignGetContact + * @depends testHubSpotGetContact */ - public function testActiveCampaignGetAccount($data) + public function testHubSpotGetAccount($data) { - $accountID = $this->ac->accountExists('Example Account 1'); + $accountID = $this->hs->accountExists('Example Account 1'); $this->assertIsNumeric($accountID); return array_merge([ @@ -117,33 +112,33 @@ public function testActiveCampaignGetAccount($data) } /** - * @depends testActiveCampaignGetAccount + * @depends testHubSpotGetAccount */ - public function testActiveCampaignSyncAsociation($data) + public function testHubSpotSyncAsociation($data) { - $this->assertTrue($this->ac->syncAssociation($data['accountID'], $data['contactID'], 'Owner')); - $this->assertTrue($this->ac->syncAssociation($data['accountID'], $data['contactID'], 'Software Developer')); + $this->assertTrue($this->hs->syncAssociation($data['accountID'], $data['contactID'], 'Owner')); + $this->assertTrue($this->hs->syncAssociation($data['accountID'], $data['contactID'], 'Software Developer')); } /** - * @depends testActiveCampaignGetContact + * @depends testHubSpotGetContact */ - public function testActiveCampaignUpdateContact($data) + public function testHubSpotUpdateContact($data) { - $this->assertTrue($this->ac->updateContact($data['contactID'], 'analytics2@utopiaphp.com', '', '', '7223224241')); + $this->assertTrue($this->hs->updateContact($data['contactID'], 'analytics2@utopiaphp.com', '', '', '7223224241')); } - public function testActiveCampaignDeleteContact() + public function testHubSpotDeleteContact() { - $this->assertTrue($this->ac->deleteContact('analytics2@utopiaphp.com')); + $this->assertTrue($this->hs->deleteContact('analytics2@utopiaphp.com')); } /** - * @depends testActiveCampaignGetAccount + * @depends testHubSpotGetAccount */ - public function testActiveCampaignUpdateAccount($data) + public function testHubSpotUpdateAccount($data) { - $this->assertTrue($this->ac->updateAccount( + $this->assertTrue($this->hs->updateAccount( $data['accountID'], 'Utopia', 'utopia.com', @@ -151,16 +146,16 @@ public function testActiveCampaignUpdateAccount($data) } /** - * @depends testActiveCampaignGetAccount + * @depends testHubSpotGetAccount */ - public function testActiveCampaignDeleteAccount($data) + public function testHubSpotDeleteAccount($data) { - $this->assertTrue($this->ac->deleteAccount($data['accountID'])); + $this->assertTrue($this->hs->deleteAccount($data['accountID'])); } - public function testActiveCampaign() + public function testHubSpot() { - $this->assertTrue($this->ac->createContact('analytics@utopiaphp.com', 'Analytics', 'Utopia')); + $this->assertTrue($this->hs->createContact('analytics@utopiaphp.com', 'Analytics', 'Utopia')); $event = new Event(); $event->setType('testEvent') @@ -168,9 +163,9 @@ public function testActiveCampaign() ->setUrl('https://www.appwrite.io/docs/installation') ->setProps(['category' => 'analytics:test', 'email' => 'analytics@utopiaphp.com', 'tags' => ['test', 'test2']]); - $this->assertTrue($this->ac->send($event)); + $this->assertTrue($this->hs->send($event)); sleep(10); - $this->assertTrue($this->ac->validate($event)); + $this->assertTrue($this->hs->validate($event)); } public function testOrbit(): void @@ -187,16 +182,16 @@ public function testOrbit(): void public function testCleanup(): void { - if ($this->ac->contactExists('analytics@utopiaphp.com')) { - $this->assertTrue($this->ac->deleteContact('analytics@utopiaphp.com')); + if ($this->hs->contactExists('analytics@utopiaphp.com')) { + $this->assertTrue($this->hs->deleteContact('analytics@utopiaphp.com')); } - if ($this->ac->contactExists('analytics2@utopiaphp.com')) { - $this->assertTrue($this->ac->deleteContact('analytics2@utopiaphp.com')); + if ($this->hs->contactExists('analytics2@utopiaphp.com')) { + $this->assertTrue($this->hs->deleteContact('analytics2@utopiaphp.com')); } - if ($this->ac->accountExists('Example Account 1')) { - $this->assertTrue($this->ac->deleteAccount($this->ac->accountExists('Example Account 1'))); + if ($this->hs->accountExists('Example Account 1')) { + $this->assertTrue($this->hs->deleteAccount($this->hs->accountExists('Example Account 1'))); } } From 53246eba190de1e5d1579f771f460969d8c96d86 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 21:53:26 +0100 Subject: [PATCH 02/10] Remove GA and finish hubspot tests --- src/Analytics/Adapter.php | 13 +-- src/Analytics/Adapter/HubSpot.php | 77 +++++++++-------- tests/Analytics/AnalyticsTest.php | 139 +++++++++++++++++++----------- 3 files changed, 136 insertions(+), 93 deletions(-) diff --git a/src/Analytics/Adapter.php b/src/Analytics/Adapter.php index 48b270e..65c04ba 100644 --- a/src/Analytics/Adapter.php +++ b/src/Analytics/Adapter.php @@ -124,7 +124,7 @@ public function call(string $method, string $path = '', array $headers = [], arr $responseType = ''; $responseBody = ''; - switch ($headers['Content-Type']) { + switch ($headers['Content-Type'] ?? '') { case 'application/json': $query = json_encode($params); break; @@ -170,23 +170,23 @@ public function call(string $method, string $path = '', array $headers = [], arr $responseType = $responseHeaders['Content-Type'] ?? ''; $responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); - switch(substr($responseType, 0, strpos($responseType, ';'))) { + switch (substr($responseType, 0, strpos($responseType, ';'))) { case 'application/json': $responseBody = json_decode($responseBody, true); break; } if (curl_errno($ch)) { - throw new \Exception(curl_error($ch)); + throw new \Exception(curl_error($ch), $responseStatus); } curl_close($ch); if ($responseStatus >= 400) { if (is_array($responseBody)) { - throw new \Exception(json_encode($responseBody)); + throw new \Exception(json_encode($responseBody), $responseStatus); } else { - throw new \Exception($responseStatus.': '.$responseBody); + throw new \Exception($responseStatus.': '.$responseBody, $responseStatus); } } @@ -225,5 +225,8 @@ protected function logError(Exception $e) Console::error('[Error] Message: '.$e->getMessage()); Console::error('[Error] File: '.$e->getFile()); Console::error('[Error] Line: '.$e->getLine()); + + Console::error('[Error] Trace: '); + Console::error($e->getTraceAsString()); } } diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php index 45081a3..2681038 100644 --- a/src/Analytics/Adapter/HubSpot.php +++ b/src/Analytics/Adapter/HubSpot.php @@ -2,7 +2,6 @@ namespace Utopia\Analytics\Adapter; -use Exception; use Utopia\Analytics\Adapter; use Utopia\Analytics\Event; @@ -25,7 +24,7 @@ public function __construct(string $token) */ public function send(Event $event): bool { - if (!$this->enabled) { + if (! $this->enabled) { return false; } @@ -35,14 +34,14 @@ public function send(Event $event): bool public function validate(Event $event): bool { - if (!$this->enabled) { + if (! $this->enabled) { return false; } // HubSpot event tracking isn't possible due to their chrome based extention system return true; } - + /** * Gets the name of the adapter. */ @@ -57,8 +56,10 @@ public function getName(): string public function contactExists(string $email): bool|int { try { - $result = $this->call('GET', '/crm/v3/objects/contacts/search', [], [ - 'filterGroups' => [ + $result = $this->call('POST', '/crm/v3/objects/contacts/search', [ + 'Content-Type' => 'application/json', + ], [ + 'filterGroups' => [[ 'filters' => [ [ 'value' => $email, @@ -66,7 +67,7 @@ public function contactExists(string $email): bool|int 'operator' => 'EQ', ], ], - ], + ], ], ]); $result = json_decode($result, true); @@ -88,7 +89,7 @@ public function contactExists(string $email): bool|int */ public function createContact(string $email, string $firstName = '', string $lastName = '', string $phone = ''): bool { - $body = ['contact' => [ + $body = ['properties' => [ 'email' => $email, 'firstname' => $firstName, 'lastname' => $lastName, @@ -113,12 +114,12 @@ public function createContact(string $email, string $firstName = '', string $las */ public function updateContact(string $contactId, string $email, string $firstName = '', string $lastName = '', string $phone = ''): bool { - $body = ['contact' => [ + $body = [ 'email' => $email, 'firstname' => $firstName, 'lastname' => $lastName, 'phone' => $phone, - ]]; + ]; try { $this->call('PATCH', '/crm/v3/objects/contacts/'.$contactId, [ @@ -127,6 +128,11 @@ public function updateContact(string $contactId, string $email, string $firstNam return true; } catch (\Exception $e) { + if ($e->getCode() == 400) { + // No changes to make + return true; + } + $this->logError($e); return false; @@ -163,8 +169,10 @@ public function deleteContact(string $email): bool public function accountExists(string $name): bool|int { try { - $result = $this->call('GET', '/crm/v3/objects/companies/search', [], [ - 'filterGroups' => [ + $result = $this->call('POST', '/crm/v3/objects/companies/search', [ + 'Content-Type' => 'application/json', + ], [ + 'filterGroups' => [[ 'filters' => [ [ 'value' => $name, @@ -172,7 +180,7 @@ public function accountExists(string $name): bool|int 'operator' => 'EQ', ], ], - ], + ]], ]); $result = json_decode($result, true); @@ -194,10 +202,10 @@ public function accountExists(string $name): bool|int */ public function createAccount(string $name, string $url = ''): bool { - $body = [ + $body = ['properties' => [ 'name' => $name, 'domain' => $url, - ]; + ]]; try { $this->call('POST', '/crm/v3/objects/companies', [ @@ -229,6 +237,11 @@ public function updateAccount(string $accountId, string $name, string $url = '', return true; } catch (\Exception $e) { + if ($e->getCode() == 400) { + // No changes to make + return true; + } + $this->logError($e); return false; @@ -277,37 +290,25 @@ public function syncAssociation(string $accountId, string $contactId, string $ro if (empty($associationId)) { // Create the association - $body = [ - 'from' => [ - 'id' => $contactId, - 'type' => 'CONTACT', - ], - 'to' => [ - 'id' => $accountId, - 'type' => 'COMPANY', - ], - 'category' => 'HUBSPOT_DEFINED', - 'definitionId' => 1, - ]; - - $this->call('PUT', '/crm/v4/objects/contact/'.$accountId.'/associations/company', [ + $this->call('PUT', '/crm/v4/objects/contact/'.$contactId.'/associations/default/company/'.$accountId, [ 'Content-Type' => 'application/json', - ], $body); + ]); } else { - // Update the association - $body = [ - 'category' => 'HUBSPOT_DEFINED', - 'definitionId' => 1, - ]; + // Delete and recreate the association + $this->call('DELETE', '/crm/v4/objects/contact/'.$contactId.'/associations/company/'.$accountId, [ + 'Content-Type' => 'application/json', + ]); - $this->call('PATCH', '/crm/v4/objects/contact/'.$accountId.'/associations/company/'.$associationId, [ + $this->call('PUT', '/crm/v4/objects/contact/'.$contactId.'/associations/default/company/'.$accountId, [ 'Content-Type' => 'application/json', - ], $body); + ]); } } catch (\Exception $e) { $this->logError($e); return false; } + + return true; } -} \ No newline at end of file +} diff --git a/tests/Analytics/AnalyticsTest.php b/tests/Analytics/AnalyticsTest.php index 000bce1..255729e 100644 --- a/tests/Analytics/AnalyticsTest.php +++ b/tests/Analytics/AnalyticsTest.php @@ -3,8 +3,8 @@ namespace Utopia\Tests; use PHPUnit\Framework\TestCase; -use Utopia\Analytics\Adapter\HubSpot; use Utopia\Analytics\Adapter\GoogleAnalytics; +use Utopia\Analytics\Adapter\HubSpot; use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Adapter\Orbit; use Utopia\Analytics\Adapter\Plausible; @@ -37,29 +37,9 @@ public function setUp(): void $this->hs = new HubSpot(App::getEnv('HS_APIKEY')); } - public function testGoogleAnalytics(): void - { - // Use Measurement Protocol Validation Server for testing. - $pageviewEvent = new Event(); - $pageviewEvent - ->setType('pageview') - ->setName('pageview') - ->setUrl('https://www.appwrite.io/docs/installation'); - - $normalEvent = new Event(); - $normalEvent->setType('testEvent') - ->setName('testEvent') - ->setUrl('https://www.appwrite.io/docs/installation') - ->setProps(['category' => 'testEvent']); - - $this->assertTrue($this->ga->validate($pageviewEvent)); - $this->assertTrue($this->ga->validate($normalEvent)); - - $this->ga->disable(); - $this->assertFalse($this->ga->validate($pageviewEvent)); - $this->assertFalse($this->ga->validate($normalEvent)); - } - + /** + * @group plausible + */ public function testPlausible() { $pageviewEvent = new Event(); @@ -78,33 +58,76 @@ public function testPlausible() $this->assertTrue($this->pa->validate($normalEvent)); } + /** + * @group hubspot + */ public function testHubSpotCreateContact() { $this->assertTrue($this->hs->createContact('analytics2@utopiaphp.com', 'Analytics', 'Utopia')); + + sleep(5); // Wait for HubSpot to index the new contact } + /** + * @group hubspot + * + * @depends testHubSpotCreateContact + */ public function testHubSpotGetContact() { - $contactID = $this->hs->contactExists('analytics2@utopiaphp.com'); - $this->assertIsNumeric($contactID); + // Sometimes it can take a few seconds for HubSpot to index the new contact + $tries = 0; + + while ($tries < 5) { + $contactID = $this->hs->contactExists('analytics2@utopiaphp.com'); + + if ($contactID) { + $this->assertIsNumeric($contactID); + break; + } + + sleep(5); + } return [ 'contactID' => $contactID, ]; } - public function testHubSpotCreateAccount() + /** + * @group hubspot + * + * @depends testHubSpotGetContact + */ + public function testHubSpotCreateAccount($data) { $this->assertTrue($this->hs->createAccount('Example Account 1', 'https://example.com', '1234567890')); + + sleep(10); // Wait for HubSpot to index the new account + + return $data; } /** - * @depends testHubSpotGetContact + * @group hubspot + * + * @depends testHubSpotCreateAccount */ public function testHubSpotGetAccount($data) { - $accountID = $this->hs->accountExists('Example Account 1'); - $this->assertIsNumeric($accountID); + + $tries = 0; + + while ($tries < 5) { + $accountID = $this->hs->accountExists('Example Account 1'); + + if ($accountID) { + $this->assertIsNumeric($accountID); + break; + } + + sleep(5); + } return array_merge([ 'accountID' => $accountID, @@ -113,28 +136,45 @@ public function testHubSpotGetAccount($data) /** * @depends testHubSpotGetAccount + * + * @group hubspot */ public function testHubSpotSyncAsociation($data) { $this->assertTrue($this->hs->syncAssociation($data['accountID'], $data['contactID'], 'Owner')); $this->assertTrue($this->hs->syncAssociation($data['accountID'], $data['contactID'], 'Software Developer')); + + return $data; } /** - * @depends testHubSpotGetContact + * @depends testHubSpotSyncAsociation + * + * @group hubspot */ public function testHubSpotUpdateContact($data) { $this->assertTrue($this->hs->updateContact($data['contactID'], 'analytics2@utopiaphp.com', '', '', '7223224241')); + + return $data; } - public function testHubSpotDeleteContact() + /** + * @depends testHubSpotUpdateContact + * + * @group hubspot + */ + public function testHubSpotDeleteContact($data) { $this->assertTrue($this->hs->deleteContact('analytics2@utopiaphp.com')); + + return $data; } /** - * @depends testHubSpotGetAccount + * @depends testHubSpotDeleteContact + * + * @group hubspot */ public function testHubSpotUpdateAccount($data) { @@ -142,32 +182,25 @@ public function testHubSpotUpdateAccount($data) $data['accountID'], 'Utopia', 'utopia.com', - 1)); + 1 + )); + + return $data; } /** - * @depends testHubSpotGetAccount + * @depends testHubSpotUpdateAccount + * + * @group hubspot */ public function testHubSpotDeleteAccount($data) { $this->assertTrue($this->hs->deleteAccount($data['accountID'])); } - public function testHubSpot() - { - $this->assertTrue($this->hs->createContact('analytics@utopiaphp.com', 'Analytics', 'Utopia')); - - $event = new Event(); - $event->setType('testEvent') - ->setName('testEvent'.chr(mt_rand(97, 122)).substr(md5(time()), 1, 5)) - ->setUrl('https://www.appwrite.io/docs/installation') - ->setProps(['category' => 'analytics:test', 'email' => 'analytics@utopiaphp.com', 'tags' => ['test', 'test2']]); - - $this->assertTrue($this->hs->send($event)); - sleep(10); - $this->assertTrue($this->hs->validate($event)); - } - + /** + * @group orbit + */ public function testOrbit(): void { $event = new Event(); @@ -180,6 +213,9 @@ public function testOrbit(): void $this->assertTrue($this->orbit->validate($event)); } + /** + * @group hubspot + */ public function testCleanup(): void { if ($this->hs->contactExists('analytics@utopiaphp.com')) { @@ -195,6 +231,9 @@ public function testCleanup(): void } } + /** + * @group mixpanel + */ public function testMixpanel() { /** Create a simple track event */ From 5475dc9de86d6bc4ffbba52f7bdd2eb8a39397d4 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 22:00:54 +0100 Subject: [PATCH 03/10] Update HubSpot.php --- src/Analytics/Adapter/HubSpot.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php index 2681038..197cb20 100644 --- a/src/Analytics/Adapter/HubSpot.php +++ b/src/Analytics/Adapter/HubSpot.php @@ -72,7 +72,7 @@ public function contactExists(string $email): bool|int $result = json_decode($result, true); - if ($result && $result['total'] > 0) { + if ($result && $result['total'] > 0 && array_sum($result['results']) > 0) { return $result['results'][0]['id']; } else { return false; @@ -185,7 +185,7 @@ public function accountExists(string $name): bool|int $result = json_decode($result, true); - if ($result && $result['total'] > 0) { + if ($result && $result['total'] > 0 && array_sum($result['results']) > 0) { return $result['results'][0]['id']; } else { return false; From 852c8679f2bae2f339db864a183ed20091fdd3dd Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 22:21:07 +0100 Subject: [PATCH 04/10] Fix Tests --- src/Analytics/Adapter/HubSpot.php | 4 ++-- tests/Analytics/AnalyticsTest.php | 29 ++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php index 197cb20..c8cd3c8 100644 --- a/src/Analytics/Adapter/HubSpot.php +++ b/src/Analytics/Adapter/HubSpot.php @@ -72,7 +72,7 @@ public function contactExists(string $email): bool|int $result = json_decode($result, true); - if ($result && $result['total'] > 0 && array_sum($result['results']) > 0) { + if ($result && $result['total'] > 0 && sizeof($result['results']) > 0) { return $result['results'][0]['id']; } else { return false; @@ -185,7 +185,7 @@ public function accountExists(string $name): bool|int $result = json_decode($result, true); - if ($result && $result['total'] > 0 && array_sum($result['results']) > 0) { + if ($result && $result['total'] > 0 && sizeof($result['results']) > 0) { return $result['results'][0]['id']; } else { return false; diff --git a/tests/Analytics/AnalyticsTest.php b/tests/Analytics/AnalyticsTest.php index 255729e..d0c2a57 100644 --- a/tests/Analytics/AnalyticsTest.php +++ b/tests/Analytics/AnalyticsTest.php @@ -30,10 +30,10 @@ class AnalyticsTest extends TestCase public function setUp(): void { - $this->ga = new GoogleAnalytics(App::getEnv('GA_TID'), App::getEnv('GA_CID')); - $this->pa = new Plausible(App::getEnv('PA_DOMAIN'), App::getEnv('PA_APIKEY'), 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', '192.168.0.1'); - $this->orbit = new Orbit(App::getEnv('OR_WORKSPACEID'), App::getEnv('OR_APIKEY'), 'Utopia Testing Suite'); - $this->mp = new Mixpanel(App::getEnv('MP_PROJECT_TOKEN')); + // $this->ga = new GoogleAnalytics(App::getEnv('GA_TID'), App::getEnv('GA_CID')); + // $this->pa = new Plausible(App::getEnv('PA_DOMAIN'), App::getEnv('PA_APIKEY'), 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', '192.168.0.1'); + // $this->orbit = new Orbit(App::getEnv('OR_WORKSPACEID'), App::getEnv('OR_APIKEY'), 'Utopia Testing Suite'); + // $this->mp = new Mixpanel(App::getEnv('MP_PROJECT_TOKEN')); $this->hs = new HubSpot(App::getEnv('HS_APIKEY')); } @@ -65,7 +65,7 @@ public function testHubSpotCreateContact() { $this->assertTrue($this->hs->createContact('analytics2@utopiaphp.com', 'Analytics', 'Utopia')); - sleep(5); // Wait for HubSpot to index the new contact + sleep(5); // Sometimes it can take a few seconds for HubSpot to index the new contact } /** @@ -75,7 +75,6 @@ public function testHubSpotCreateContact() */ public function testHubSpotGetContact() { - // Sometimes it can take a few seconds for HubSpot to index the new contact $tries = 0; while ($tries < 5) { @@ -86,7 +85,13 @@ public function testHubSpotGetContact() break; } + var_dump('Waiting for HubSpot to index the new contact... Attempt: '.$tries.'/5'); sleep(5); + $tries++; + + if ($tries === 5) { + $this->fail('HubSpot failed to index the new contact in a reasonable amount of time'); + } } return [ @@ -103,8 +108,7 @@ public function testHubSpotCreateAccount($data) { $this->assertTrue($this->hs->createAccount('Example Account 1', 'https://example.com', '1234567890')); - sleep(10); // Wait for HubSpot to index the new account - + sleep(5); // Sometimes it can take a few seconds for HubSpot to index the new account return $data; } @@ -115,7 +119,6 @@ public function testHubSpotCreateAccount($data) */ public function testHubSpotGetAccount($data) { - $tries = 0; while ($tries < 5) { @@ -126,7 +129,13 @@ public function testHubSpotGetAccount($data) break; } + var_dump('Waiting for HubSpot to index the new account... Attempt: '.$tries.'/5'); sleep(5); + $tries++; + + if ($tries === 5) { + $this->fail('HubSpot failed to index the new account'); + } } return array_merge([ @@ -229,6 +238,8 @@ public function testCleanup(): void if ($this->hs->accountExists('Example Account 1')) { $this->assertTrue($this->hs->deleteAccount($this->hs->accountExists('Example Account 1'))); } + + $this->assertTrue(true); } /** From be85efd9ffe77542be4660e6341e68d45e094761 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 22:25:44 +0100 Subject: [PATCH 05/10] Run Linter and fix tests --- src/Analytics/Adapter/HubSpot.php | 4 ++-- tests/Analytics/AnalyticsTest.php | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php index c8cd3c8..3fe3aac 100644 --- a/src/Analytics/Adapter/HubSpot.php +++ b/src/Analytics/Adapter/HubSpot.php @@ -72,7 +72,7 @@ public function contactExists(string $email): bool|int $result = json_decode($result, true); - if ($result && $result['total'] > 0 && sizeof($result['results']) > 0) { + if ($result && $result['total'] > 0 && count($result['results']) > 0) { return $result['results'][0]['id']; } else { return false; @@ -185,7 +185,7 @@ public function accountExists(string $name): bool|int $result = json_decode($result, true); - if ($result && $result['total'] > 0 && sizeof($result['results']) > 0) { + if ($result && $result['total'] > 0 && count($result['results']) > 0) { return $result['results'][0]['id']; } else { return false; diff --git a/tests/Analytics/AnalyticsTest.php b/tests/Analytics/AnalyticsTest.php index d0c2a57..5efd84c 100644 --- a/tests/Analytics/AnalyticsTest.php +++ b/tests/Analytics/AnalyticsTest.php @@ -30,10 +30,10 @@ class AnalyticsTest extends TestCase public function setUp(): void { - // $this->ga = new GoogleAnalytics(App::getEnv('GA_TID'), App::getEnv('GA_CID')); - // $this->pa = new Plausible(App::getEnv('PA_DOMAIN'), App::getEnv('PA_APIKEY'), 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', '192.168.0.1'); - // $this->orbit = new Orbit(App::getEnv('OR_WORKSPACEID'), App::getEnv('OR_APIKEY'), 'Utopia Testing Suite'); - // $this->mp = new Mixpanel(App::getEnv('MP_PROJECT_TOKEN')); + $this->ga = new GoogleAnalytics(App::getEnv('GA_TID'), App::getEnv('GA_CID')); + $this->pa = new Plausible(App::getEnv('PA_DOMAIN'), App::getEnv('PA_APIKEY'), 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', '192.168.0.1'); + $this->orbit = new Orbit(App::getEnv('OR_WORKSPACEID'), App::getEnv('OR_APIKEY'), 'Utopia Testing Suite'); + $this->mp = new Mixpanel(App::getEnv('MP_PROJECT_TOKEN')); $this->hs = new HubSpot(App::getEnv('HS_APIKEY')); } @@ -109,6 +109,7 @@ public function testHubSpotCreateAccount($data) $this->assertTrue($this->hs->createAccount('Example Account 1', 'https://example.com', '1234567890')); sleep(5); // Sometimes it can take a few seconds for HubSpot to index the new account + return $data; } From fcafe7e3872f04fa8b6e01847cde98b30c2adf33 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 23:28:50 +0100 Subject: [PATCH 06/10] Add AddToList method --- src/Analytics/Adapter/HubSpot.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php index 3fe3aac..651f1b1 100644 --- a/src/Analytics/Adapter/HubSpot.php +++ b/src/Analytics/Adapter/HubSpot.php @@ -311,4 +311,21 @@ public function syncAssociation(string $accountId, string $contactId, string $ro return true; } + + public function addToList(int $listId, int $contactId) + { + try { + $this->call('POST', '/contacts/v1/lists/'.$listId.'/add', [ + 'Content-Type' => 'application/json', + ], [ + 'vids' => [$contactId], + ]); + + return true; + } catch (\Exception $e) { + $this->logError($e); + + return false; + } + } } From 134ccb85943f8d4892b1bb0d91e145191b251987 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 23:41:25 +0100 Subject: [PATCH 07/10] Update src/Analytics/Adapter.php Co-authored-by: Jake Barnby --- src/Analytics/Adapter.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Analytics/Adapter.php b/src/Analytics/Adapter.php index 65c04ba..c1ef1ff 100644 --- a/src/Analytics/Adapter.php +++ b/src/Analytics/Adapter.php @@ -225,7 +225,6 @@ protected function logError(Exception $e) Console::error('[Error] Message: '.$e->getMessage()); Console::error('[Error] File: '.$e->getFile()); Console::error('[Error] Line: '.$e->getLine()); - Console::error('[Error] Trace: '); Console::error($e->getTraceAsString()); } From 59bf461eec0e6162ef7e57881d20820981ef8f2d Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 23:50:07 +0100 Subject: [PATCH 08/10] Address Jake's Comments --- src/Analytics/Adapter.php | 2 +- src/Analytics/Adapter/HubSpot.php | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Analytics/Adapter.php b/src/Analytics/Adapter.php index c1ef1ff..cbb3f79 100644 --- a/src/Analytics/Adapter.php +++ b/src/Analytics/Adapter.php @@ -124,7 +124,7 @@ public function call(string $method, string $path = '', array $headers = [], arr $responseType = ''; $responseBody = ''; - switch ($headers['Content-Type'] ?? '') { + switch ($headers['Content-Type']) { case 'application/json': $query = json_encode($params); break; diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php index 651f1b1..5266bdd 100644 --- a/src/Analytics/Adapter/HubSpot.php +++ b/src/Analytics/Adapter/HubSpot.php @@ -16,6 +16,7 @@ public function __construct(string $token) { $this->headers = [ 'Authorization' => 'Bearer '.$token, + 'Content-Type' => '' ]; } @@ -29,7 +30,7 @@ public function send(Event $event): bool } // HubSpot event tracking isn't possible due to their chrome based extention system - return true; + return false; } public function validate(Event $event): bool @@ -39,7 +40,7 @@ public function validate(Event $event): bool } // HubSpot event tracking isn't possible due to their chrome based extention system - return true; + return false; } /** @@ -312,7 +313,10 @@ public function syncAssociation(string $accountId, string $contactId, string $ro return true; } - public function addToList(int $listId, int $contactId) + /** + * Add a contact to a list + */ + public function addToList(int $listId, int $contactId): bool { try { $this->call('POST', '/contacts/v1/lists/'.$listId.'/add', [ From dcf85e1755fbf03f2af0bdc2f3e3f65eaa9d83f1 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Sep 2023 23:51:03 +0100 Subject: [PATCH 09/10] Run Linter --- src/Analytics/Adapter/HubSpot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analytics/Adapter/HubSpot.php b/src/Analytics/Adapter/HubSpot.php index 5266bdd..14c0212 100644 --- a/src/Analytics/Adapter/HubSpot.php +++ b/src/Analytics/Adapter/HubSpot.php @@ -16,7 +16,7 @@ public function __construct(string $token) { $this->headers = [ 'Authorization' => 'Bearer '.$token, - 'Content-Type' => '' + 'Content-Type' => '', ]; } From 2494ca576eec1b9345841f057ddf38d3e5d0831e Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Wed, 27 Sep 2023 00:21:12 +0100 Subject: [PATCH 10/10] Remove ActiveCampaign --- src/Analytics/Adapter/ActiveCampaign.php | 412 ----------------------- 1 file changed, 412 deletions(-) delete mode 100644 src/Analytics/Adapter/ActiveCampaign.php diff --git a/src/Analytics/Adapter/ActiveCampaign.php b/src/Analytics/Adapter/ActiveCampaign.php deleted file mode 100644 index d6df567..0000000 --- a/src/Analytics/Adapter/ActiveCampaign.php +++ /dev/null @@ -1,412 +0,0 @@ -call('GET', '/api/3/contacts', [], [ - 'email' => $email, - ]); - - $result = json_decode($result, true); - - if ($result && $result['meta']['total'] > 0) { - return $result['contacts'][0]['id']; - } else { - return false; - } - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Create a contact - */ - public function createContact(string $email, string $firstName = '', string $lastName = '', string $phone = ''): bool - { - $body = ['contact' => [ - 'email' => $email, - 'firstName' => $firstName, - 'lastName' => $lastName, - 'phone' => $phone, - ]]; - - try { - $this->call('POST', '/api/3/contacts', [ - 'Content-Type' => 'application/json', - ], $body); - - return true; - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Update contact - */ - public function updateContact(string $contactId, string $email, string $firstName = '', string $lastName = '', string $phone = ''): bool - { - $body = ['contact' => [ - 'email' => $email, - 'firstName' => $firstName, - 'lastName' => $lastName, - 'phone' => $phone, - ]]; - - try { - $this->call('PUT', '/api/3/contacts/'.$contactId, [ - 'Content-Type' => 'application/json', - ], $body); - - return true; - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Delete a contact - */ - public function deleteContact(string $email): bool - { - $contact = $this->contactExists($email); - - if (! $contact) { - return false; - } - - try { - $this->call('DELETE', '/api/3/contacts/'.$contact); - - return true; - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Account Exists - */ - public function accountExists(string $name): bool|int - { - try { - $result = $this->call('GET', '/api/3/accounts', [], [ - 'search' => $name, - ]); - - if (intval(json_decode($result, true)['meta']['total']) > 0) { - return intval((json_decode($result, true))['accounts'][0]['id']); - } else { - return false; - } - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Create an account - */ - public function createAccount(string $name, string $url = '', int $ownerId = 1, array $fields = []): bool - { - $body = ['account' => [ - 'name' => $name, - 'accountUrl' => $url, - 'owner' => $ownerId, - 'fields' => array_values(array_filter($fields, function ($value) { - return $value['fieldValue'] !== '' && $value['fieldValue'] !== null && $value['fieldValue'] !== false; - })), - ]]; - - try { - $this->call('POST', '/api/3/accounts', [ - 'Content-Type' => 'application/json', - ], $body); - - return true; - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Update an account - */ - public function updateAccount(string $accountId, string $name, string $url = '', int $ownerId = 1, array $fields = []): bool - { - $body = ['account' => [ - 'name' => $name, - 'accountUrl' => $url, - 'owner' => $ownerId, - 'fields' => array_values(array_filter($fields, function ($value) { - return $value['fieldValue'] !== '' && $value['fieldValue'] !== null && $value['fieldValue'] !== false; - })), - ]]; - - try { - $this->call('PUT', '/api/3/accounts/'.$accountId, [ - 'Content-Type' => 'application/json', - ], array_filter($body)); - - return true; - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Delete an account - */ - public function deleteAccount(string $accountId): bool - { - try { - $this->call('DELETE', '/api/3/accounts/'.$accountId); - - return true; - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - /** - * Sync an association - * - * Creates an association if it doesn't exist and updates it if it does - */ - public function syncAssociation(string $accountId, string $contactId, string $role = ''): bool - { - // See if the association already exists - try { - $result = $this->call('GET', '/api/3/accountContacts', [], [ - 'filters[account]' => $accountId, - 'filters[contact]' => $contactId, - ]); - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - - if (intval(json_decode($result, true)['meta']['total']) > 0) { - // Update the association - $associationId = intval((json_decode($result, true))['accountContacts'][0]['id']); - - try { - $result = $this->call('PUT', '/api/3/accountContacts/'.$associationId, [ - 'Content-Type' => 'application/json', - ], [ - 'accountContact' => [ - 'jobTitle' => $role, - ], - ]); - - return true; - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } else { - // Create the association - $result = $this->call('POST', '/api/3/accountContacts', [ - 'Content-Type' => 'application/json', - ], ['accountContact' => [ - 'account' => $accountId, - 'contact' => $contactId, - 'jobTitle' => $role, - ]]); - - return true; - } - } - - /** - * @return ActiveCampaign - */ - public function __construct(string $key, string $actId, string $apiKey, string $organisationId) - { - $this->key = $key; - $this->actId = $actId; - $this->apiKey = $apiKey; - $this->endpoint = 'https://'.$organisationId.'.api-us1.com/'; // ActiveCampaign API URL, Refer to https://developers.activecampaign.com/reference/url for more details. - $this->headers = [ - 'Api-Token' => $this->apiKey, - 'Content-Type' => null, - ]; - } - - /** - * Creates an Event on the remote analytics platform. - */ - public function send(Event $event): bool - { - if (! $this->enabled) { - return false; - } - - $query = [ - 'key' => $this->key, - 'event' => $event->getName(), - 'actid' => $this->actId, - 'eventdata' => json_encode($event->getProps()), - 'visit' => json_encode(['email' => $event->getProp('email')]), - ]; - - $query = array_filter($query, fn ($value) => ! is_null($value) && $value !== ''); - - $res = $this->call('POST', 'https://trackcmp.net/event', [], $query); // Active Campaign event URL, Refer to https://developers.activecampaign.com/reference/track-event/ for more details - if (json_decode($res, true)['success'] === 1) { - return true; - } else { - return false; - } - } - - /** - * Sets the client IP address. - * - * @param string $ip The IP address to use. - */ - public function setClientIP(string $clientIP): self - { - throw new \Exception('Not implemented'); - } - - /** - * Sets the client user agent. - * - * @param string $userAgent The user agent to use. - */ - public function setUserAgent(string $userAgent): self - { - throw new \Exception('Not implemented'); - } - - /** - * Set Tags - */ - public function setTags(string $contactId, array $tags): bool - { - foreach ($tags as $tag) { - try { - $this->call('POST', '/api/3/contactTags', [ - 'Content-Type' => 'application/json', - ], [ - 'contactTag' => [ - 'contact' => $contactId, - 'tag' => $tag, - ], - ]); - } catch (\Exception $e) { - $this->logError($e); - - return false; - } - } - - return true; - } - - public function validate(Event $event): bool - { - if (! $this->enabled) { - return false; - } - - $email = $event->getProp('email'); - - if (empty($email)) { - throw new \Exception('Email is required.'); - } - - if (! filter_var($email, FILTER_VALIDATE_EMAIL)) { - throw new \Exception('Invalid email address.'); - } - - $contactID = $this->contactExists($email); - $foundLog = false; - - // Get contact again, since AC doesn't refresh logs immediately - $response = $this->call('GET', '/api/3/activities', [], [ - 'contact' => $contactID, - 'orders[tstamp]' => 'DESC', - ]); - - $response = json_decode($response, true); - - if (empty($response['trackingLogs'])) { - throw new \Exception('Failed to find event on ActiveCampaign side.'); - } - - foreach ($response['trackingLogs'] as $log) { - if ($log['type'] === $event->getName()) { - $foundLog = true; - break; - } - } - - if (! $foundLog) { - throw new \Exception('Failed to find event on ActiveCampaign side.'); - } - - return true; - } -}