Skip to content

Commit

Permalink
feat(cdp): upsert in the hubspot function (#25362)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra authored Oct 3, 2024
1 parent a1abaf5 commit 1f7f5d6
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 238 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,10 @@
from inline_snapshot import snapshot

from posthog.cdp.templates.google_cloud_storage.template_google_cloud_storage import TemplateGoogleCloudStorageMigrator
from posthog.cdp.templates.helpers import BaseHogFunctionTemplateTest
from posthog.cdp.templates.hubspot.template_hubspot import template as template_hubspot
from posthog.models import PluginConfig, PluginAttachment, Plugin, Integration
from posthog.test.base import BaseTest


class TestTemplateGoogleCloudStorage(BaseHogFunctionTemplateTest):
template = template_hubspot

def _inputs(self, **kwargs):
inputs = {
"oauth": {"access_token": "TOKEN"},
"email": "[email protected]",
"properties": {
"company": "PostHog",
},
}
inputs.update(kwargs)
return inputs

def test_function_works(self):
self.mock_fetch_response = lambda *args: {"status": 200, "body": {"status": "success"}} # type: ignore

res = self.run_function(inputs=self._inputs())

assert res.result is None

assert self.get_mock_fetch_calls() == [
(
"https://api.hubapi.com/crm/v3/objects/contacts",
{
"method": "POST",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)
]
assert self.get_mock_print_calls() == [("Contact created successfully!",)]

def test_exits_if_no_email(self):
for email in [None, ""]:
self.mock_print.reset_mock()
res = self.run_function(inputs=self._inputs(email=email))

assert res.result is None
assert self.get_mock_fetch_calls() == []
assert self.get_mock_print_calls() == [("`email` input is empty. Not creating a contact.",)]

def test_handles_updates(self):
call_count = 0

# First call respond with 409, second one 200 and increment call_count
def mock_fetch(*args):
nonlocal call_count
call_count += 1
return (
{"status": 409, "body": {"message": "Contact already exists. Existing ID: 12345"}}
if call_count == 1
else {"status": 200, "body": {"status": "success"}}
)

self.mock_fetch_response = mock_fetch # type: ignore

res = self.run_function(inputs=self._inputs())

assert res.result is None

assert len(self.get_mock_fetch_calls()) == 2

assert self.get_mock_fetch_calls()[0] == (
"https://api.hubapi.com/crm/v3/objects/contacts",
{
"method": "POST",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)

assert self.get_mock_fetch_calls()[1] == (
"https://api.hubapi.com/crm/v3/objects/contacts/12345",
{
"method": "PATCH",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)


class TestTemplateMigration(BaseTest):
def get_plugin_config(self, config: dict):
_config = {
Expand Down
84 changes: 0 additions & 84 deletions posthog/cdp/templates/google_pubsub/test_template_google_pubsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,10 @@
from inline_snapshot import snapshot

from posthog.cdp.templates.google_pubsub.template_google_pubsub import TemplateGooglePubSubMigrator
from posthog.cdp.templates.helpers import BaseHogFunctionTemplateTest
from posthog.cdp.templates.hubspot.template_hubspot import template as template_hubspot
from posthog.models import PluginConfig, PluginAttachment, Plugin, Integration
from posthog.test.base import BaseTest


class TestTemplateGooglePubSub(BaseHogFunctionTemplateTest):
template = template_hubspot

def _inputs(self, **kwargs):
inputs = {
"oauth": {"access_token": "TOKEN"},
"email": "[email protected]",
"properties": {
"company": "PostHog",
},
}
inputs.update(kwargs)
return inputs

def test_function_works(self):
self.mock_fetch_response = lambda *args: {"status": 200, "body": {"status": "success"}} # type: ignore

res = self.run_function(inputs=self._inputs())

assert res.result is None

assert self.get_mock_fetch_calls() == [
(
"https://api.hubapi.com/crm/v3/objects/contacts",
{
"method": "POST",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)
]
assert self.get_mock_print_calls() == [("Contact created successfully!",)]

def test_exits_if_no_email(self):
for email in [None, ""]:
self.mock_print.reset_mock()
res = self.run_function(inputs=self._inputs(email=email))

assert res.result is None
assert self.get_mock_fetch_calls() == []
assert self.get_mock_print_calls() == [("`email` input is empty. Not creating a contact.",)]

def test_handles_updates(self):
call_count = 0

# First call respond with 409, second one 200 and increment call_count
def mock_fetch(*args):
nonlocal call_count
call_count += 1
return (
{"status": 409, "body": {"message": "Contact already exists. Existing ID: 12345"}}
if call_count == 1
else {"status": 200, "body": {"status": "success"}}
)

self.mock_fetch_response = mock_fetch # type: ignore

res = self.run_function(inputs=self._inputs())

assert res.result is None

assert len(self.get_mock_fetch_calls()) == 2

assert self.get_mock_fetch_calls()[0] == (
"https://api.hubapi.com/crm/v3/objects/contacts",
{
"method": "POST",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)

assert self.get_mock_fetch_calls()[1] == (
"https://api.hubapi.com/crm/v3/objects/contacts/12345",
{
"method": "PATCH",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)


class TestTemplateMigration(BaseTest):
def get_plugin_config(self, config: dict):
_config = {
Expand Down
54 changes: 26 additions & 28 deletions posthog/cdp/templates/hubspot/template_hubspot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@
icon_url="/static/services/hubspot.png",
category=["CRM", "Customer Success"],
hog="""
let properties := inputs.properties
properties.email := inputs.email
let properties := {
'email': inputs.email
}
for (let key, value in inputs.properties) {
if (typeof(value) in ('object', 'array', 'tuple')) {
properties[key] := jsonStringify(value)
} else {
properties[key] := value
}
}
if (empty(properties.email)) {
print('`email` input is empty. Not creating a contact.')
Expand All @@ -24,36 +32,26 @@
'Authorization': f'Bearer {inputs.oauth.access_token}',
'Content-Type': 'application/json'
}
let body := {
'inputs': [
{
'properties': properties,
'id': properties.email,
'idProperty': 'email'
}
]
}
let res := fetch('https://api.hubapi.com/crm/v3/objects/contacts', {
'method': 'POST',
'headers': headers,
'body': {
'properties': properties
}
let res := fetch('https://api.hubapi.com/crm/v3/objects/contacts/batch/upsert', {
'method': 'POST',
'headers': headers,
'body': body
})
if (res.status == 409) {
let existingId := replaceOne(res.body.message, 'Contact already exists. Existing ID: ', '')
let updateRes := fetch(f'https://api.hubapi.com/crm/v3/objects/contacts/{existingId}', {
'method': 'PATCH',
'headers': headers,
'body': {
'properties': properties
}
})
if (updateRes.status != 200 or updateRes.body.status == 'error') {
print('Error updating contact:', updateRes.body)
return
}
print('Contact updated successfully!')
return
} else if (res.status >= 300 or res.body.status == 'error') {
print('Error creating contact:', res.body)
return
if (res.status == 200) {
print(f'Contact {properties.email} updated successfully!')
} else {
print('Contact created successfully!')
throw Error(f'Error updating contact {properties.email} (status {res.status}): {res.body}')
}
""".strip(),
inputs_schema=[
Expand Down
53 changes: 11 additions & 42 deletions posthog/cdp/templates/hubspot/test_template_hubspot.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,23 @@ def test_function_works(self):

assert self.get_mock_fetch_calls() == [
(
"https://api.hubapi.com/crm/v3/objects/contacts",
"https://api.hubapi.com/crm/v3/objects/contacts/batch/upsert",
{
"method": "POST",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
"body": {
"inputs": [
{
"properties": {"company": "PostHog", "email": "[email protected]"},
"id": "[email protected]",
"idProperty": "email",
}
]
},
},
)
]
assert self.get_mock_print_calls() == [("Contact created successfully!",)]
assert self.get_mock_print_calls() == [("Contact [email protected] updated successfully!",)]

def test_exits_if_no_email(self):
for email in [None, ""]:
Expand All @@ -48,45 +56,6 @@ def test_exits_if_no_email(self):
assert self.get_mock_fetch_calls() == []
assert self.get_mock_print_calls() == [("`email` input is empty. Not creating a contact.",)]

def test_handles_updates(self):
call_count = 0

# First call respond with 409, second one 200 and increment call_count
def mock_fetch(*args):
nonlocal call_count
call_count += 1
return (
{"status": 409, "body": {"message": "Contact already exists. Existing ID: 12345"}}
if call_count == 1
else {"status": 200, "body": {"status": "success"}}
)

self.mock_fetch_response = mock_fetch # type: ignore

res = self.run_function(inputs=self._inputs())

assert res.result is None

assert len(self.get_mock_fetch_calls()) == 2

assert self.get_mock_fetch_calls()[0] == (
"https://api.hubapi.com/crm/v3/objects/contacts",
{
"method": "POST",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)

assert self.get_mock_fetch_calls()[1] == (
"https://api.hubapi.com/crm/v3/objects/contacts/12345",
{
"method": "PATCH",
"headers": {"Authorization": "Bearer TOKEN", "Content-Type": "application/json"},
"body": {"properties": {"company": "PostHog", "email": "[email protected]"}},
},
)


class TestTemplateMigration(BaseTest):
def get_plugin_config(self, config: dict):
Expand Down

0 comments on commit 1f7f5d6

Please sign in to comment.