Skip to content
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

Add HubSpot adapter #47

Merged
merged 10 commits into from
Sep 26, 2023
13 changes: 8 additions & 5 deletions src/Analytics/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public function call(string $method, string $path = '', array $headers = [], arr
$responseType = '';
$responseBody = '';

switch ($headers['Content-Type']) {
switch ($headers['Content-Type'] ?? '') {
PineappleIOnic marked this conversation as resolved.
Show resolved Hide resolved
case 'application/json':
$query = json_encode($params);
break;
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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());

PineappleIOnic marked this conversation as resolved.
Show resolved Hide resolved
Console::error('[Error] Trace: ');
Console::error($e->getTraceAsString());
}
}
1 change: 0 additions & 1 deletion src/Analytics/Adapter/ActiveCampaign.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
314 changes: 314 additions & 0 deletions src/Analytics/Adapter/HubSpot.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
<?php

namespace Utopia\Analytics\Adapter;

use Utopia\Analytics\Adapter;
use Utopia\Analytics\Event;

class HubSpot extends Adapter
{
/**
* Endpoint for MixPanel Events
*/
public string $endpoint = 'https://api.hubapi.com';

public function __construct(string $token)
{
$this->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;
PineappleIOnic marked this conversation as resolved.
Show resolved Hide resolved
}

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;
PineappleIOnic marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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('POST', '/crm/v3/objects/contacts/search', [
'Content-Type' => 'application/json',
], [
'filterGroups' => [[
'filters' => [
[
'value' => $email,
'propertyName' => 'email',
'operator' => 'EQ',
],
],
], ],
]);

$result = json_decode($result, true);

if ($result && $result['total'] > 0 && count($result['results']) > 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 = ['properties' => [
'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 = [
'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) {
if ($e->getCode() == 400) {
// No changes to make
return true;
}

$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('POST', '/crm/v3/objects/companies/search', [
'Content-Type' => 'application/json',
], [
'filterGroups' => [[
'filters' => [
[
'value' => $name,
'propertyName' => 'name',
'operator' => 'EQ',
],
],
]],
]);

$result = json_decode($result, true);

if ($result && $result['total'] > 0 && count($result['results']) > 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 = ['properties' => [
'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) {
if ($e->getCode() == 400) {
// No changes to make
return true;
}

$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
$this->call('PUT', '/crm/v4/objects/contact/'.$contactId.'/associations/default/company/'.$accountId, [
'Content-Type' => 'application/json',
]);
} else {
// Delete and recreate the association
$this->call('DELETE', '/crm/v4/objects/contact/'.$contactId.'/associations/company/'.$accountId, [
'Content-Type' => 'application/json',
]);

$this->call('PUT', '/crm/v4/objects/contact/'.$contactId.'/associations/default/company/'.$accountId, [
'Content-Type' => 'application/json',
]);
}
} catch (\Exception $e) {
$this->logError($e);

return false;
}

return true;
}
}
Loading