Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 4 additions & 36 deletions api/integrations/lead_tracking/hubspot/lead_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,11 @@

logger = logging.getLogger(__name__)

try:
import re2 as re # type: ignore[import-untyped]

logger.info("Using re2 library for regex.")
except ImportError:
logger.warning("Unable to import re2. Falling back to re.")
import re


class HubspotLeadTracker(LeadTracker):
@staticmethod
def should_track(user: FFAdminUser) -> bool:
if not settings.ENABLE_HUBSPOT_LEAD_TRACKING:
return False

domain = user.email_domain

if settings.HUBSPOT_IGNORE_DOMAINS_REGEX and re.match(
settings.HUBSPOT_IGNORE_DOMAINS_REGEX, domain
):
return False

if (
settings.HUBSPOT_IGNORE_DOMAINS
and domain in settings.HUBSPOT_IGNORE_DOMAINS
):
return False

return True
return settings.ENABLE_HUBSPOT_LEAD_TRACKING

def update_company_active_subscription(
self, subscription: Subscription
Expand Down Expand Up @@ -96,17 +72,9 @@ def create_user_hubspot_contact(self, user: FFAdminUser) -> str | None:
return hubspot_contact_id

def create_lead(self, user: FFAdminUser, organisation: Organisation) -> None:
hubspot_contact_id = self._get_or_create_user_hubspot_id(user)
if not hubspot_contact_id:
return
hubspot_org_id = self._get_organisation_hubspot_id(user, organisation)
if not hubspot_org_id:
return

self.client.associate_contact_to_company(
contact_id=hubspot_contact_id,
company_id=hubspot_org_id,
)
# Only create the contact. HubSpot handles company creation and
# association automatically from the contact's email domain.
self._get_or_create_user_hubspot_id(user)

def _get_new_contact_with_retry(
self, user: FFAdminUser, max_retries: int = 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,6 @@ def test_track_hubspot_lead_v2__new_user_added_to_org__creates_associations(
"organisations.models.track_hubspot_lead_v2"
)

mock_client_existing_contact.get_company_by_domain.return_value = {
"id": HUBSPOT_COMPANY_ID,
"properties": {"name": domain},
}
mock_client_existing_contact.update_company.return_value = {
"id": HUBSPOT_COMPANY_ID,
"properties": {"name": organisation.name},
}
assert getattr(organisation, "hubspot_organisation", None) is None
# When
user.add_organisation(organisation, role=OrganisationRole.ADMIN)
Expand All @@ -165,16 +157,10 @@ def test_track_hubspot_lead_v2__new_user_added_to_org__creates_associations(

# Triggering it manually to void the delay
track_hubspot_lead_v2(user.id, organisation.id)
organisation.refresh_from_db()
assert organisation.hubspot_organisation is not None
assert organisation.hubspot_organisation.hubspot_id == HUBSPOT_COMPANY_ID

# create_lead only creates the contact, not the company association
mock_client_existing_contact.create_company.assert_not_called()

mock_client_existing_contact.associate_contact_to_company.assert_called_once_with(
contact_id=HUBSPOT_USER_ID,
company_id=HUBSPOT_COMPANY_ID,
)
mock_client_existing_contact.associate_contact_to_company.assert_not_called()
mock_client_existing_contact.create_lead_form.assert_not_called()
mock_client_existing_contact.get_contact.assert_called_once_with(user)

Expand Down Expand Up @@ -253,38 +239,7 @@ def test_create_lead__existing_hubspot_org__creates_contact_and_associates(
mock_client.create_lead_form.assert_called_once_with(
user=user, form_id=HUBSPOT_FORM_ID_SAAS
)
mock_client.associate_contact_to_company.assert_called_once_with(
contact_id=HUBSPOT_USER_ID,
company_id=HUBSPOT_COMPANY_ID,
)


def test_create_lead__filtered_domain__skips_company_creation(
organisation: Organisation,
settings: SettingsWrapper,
mock_client_existing_contact: MagicMock,
enable_hubspot: None,
mocker: MockerFixture,
) -> None:
# Given
settings.HUBSPOT_IGNORE_ORGANISATION_DOMAINS = ["example.com"]

user = FFAdminUser.objects.create(
email="new.user@example.com",
first_name="Frank",
last_name="Louis",
marketing_consent_given=True,
)

# When
tracker = HubspotLeadTracker()
tracker.create_lead(user=user, organisation=organisation)

# Then
assert HubspotLead.objects.filter(user=user, hubspot_id=HUBSPOT_USER_ID).exists()
mock_client_existing_contact.get_contact.assert_called_once_with(user)
mock_client_existing_contact.create_company.assert_not_called()
mock_client_existing_contact.associate_contact_to_company.assert_not_called()
mock_client.associate_contact_to_company.assert_not_called()


def test_update_company_active_subscription__valid_subscription__calls_update_company(
Expand Down Expand Up @@ -390,38 +345,6 @@ def test_create_user_hubspot_contact__get_contact_retries__returns_expected_id(
assert mock_client.get_contact.call_count == expected_call_count


@pytest.mark.parametrize(
"hubspot_contact_id, hubspot_org_id",
[
(None, "org_123"),
("contact_123", None),
],
)
def test_create_lead__missing_contact_or_org_id__skips_association(
mocker: MockerFixture,
hubspot_contact_id: str | None,
hubspot_org_id: str | None,
staff_user: FFAdminUser,
organisation: Organisation,
) -> None:
# Given
mock_client = mocker.MagicMock()
tracker = HubspotLeadTracker()

mocker.patch.object(
tracker, "_get_or_create_user_hubspot_id", return_value=hubspot_contact_id
)
mocker.patch.object(
tracker, "_get_organisation_hubspot_id", return_value=hubspot_org_id
)

# When
tracker.create_lead(staff_user, organisation)

# Then
mock_client.associate_contact_to_company.assert_not_called()


def test_register_hubspot_tracker_and_track_user__no_explicit_user__falls_back_to_request_user(
mocker: MockerFixture, staff_user: FFAdminUser, settings: SettingsWrapper
) -> None:
Expand Down
Loading