From 1216d07f3200f31e73d999ba32ad269c2ffaaaee Mon Sep 17 00:00:00 2001 From: Eric Gauzens Date: Fri, 17 Jan 2025 16:31:49 -0500 Subject: [PATCH 1/2] added get hubspot contact properties endpoint --- app/main.py | 16 ++++++++++++++++ tests/test_api.py | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/app/main.py b/app/main.py index f05d16d..b27d8ef 100644 --- a/app/main.py +++ b/app/main.py @@ -1405,6 +1405,22 @@ def create_wrike_task(): else: abort(400, description="Missing title or description") +@app.route("/hubspot_contact_properties/", methods=["GET"]) +def get_hubspot_contact_properties(email): + url = f"https://api.hubapi.com/crm/v3/objects/contacts/{email}?archived=false&idProperty=email&properties=firstname,lastname,email,newsletter,event_name" + headers = { + "Content-Type": "application/json", + 'Authorization': 'Bearer ' + Config.HUBSPOT_API_TOKEN + } + try: + response = requests.get(url, headers=headers) + if str(response.status_code).startswith('2'): + return response.json() + else: + return abort(response.status_code) + except Exception as ex: + return abort(500, description=f"Could not get contact with email address: {email} from Hubspot due to the following error: {ex}") + def get_contact_properties(object_id): client = hubspot.Client.create(access_token=Config.HUBSPOT_API_TOKEN) try: diff --git a/tests/test_api.py b/tests/test_api.py index 75a992d..e2f7a70 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -156,6 +156,10 @@ def test_create_wrike_task(client): ) assert resp.status_code == 200 +def test_get_hubspot_contact(client): + r = client.get(f"/hubspot_contact_properties/hubspot_webhook_test@test.com") + assert r.status_code == 200 + def test_hubspot_webhook(client): http_method = "POST" endpoint = "/hubspot_webhook" From fbb18a95dac8a9935219e568418ab0587a19f48c Mon Sep 17 00:00:00 2001 From: Eric Gauzens Date: Tue, 21 Jan 2025 14:04:44 -0500 Subject: [PATCH 2/2] Added subscribe to newsletter endpoint --- app/main.py | 92 ++++++++++++++++++++++++++++++++++++++++++++--- tests/test_api.py | 15 ++++++++ 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/app/main.py b/app/main.py index b27d8ef..6273711 100644 --- a/app/main.py +++ b/app/main.py @@ -1410,16 +1410,98 @@ def get_hubspot_contact_properties(email): url = f"https://api.hubapi.com/crm/v3/objects/contacts/{email}?archived=false&idProperty=email&properties=firstname,lastname,email,newsletter,event_name" headers = { "Content-Type": "application/json", - 'Authorization': 'Bearer ' + Config.HUBSPOT_API_TOKEN + "Authorization": "Bearer " + Config.HUBSPOT_API_TOKEN } try: response = requests.get(url, headers=headers) - if str(response.status_code).startswith('2'): - return response.json() + # Handle successful responses (2xx) + if response.status_code == 200: + return jsonify(response.json()) + # Handle not found (404) + elif response.status_code == 404: + return jsonify({ + "error": "Contact not found", + "message": f"No contact with the email '{email}' was found in HubSpot." + }), 404 + # Handle other non-success status codes else: - return abort(response.status_code) + return jsonify({ + "error": "Failed to fetch contact", + "message": f"HubSpot API responded with status code {response.status_code}.", + "details": response.json() if response.headers.get("Content-Type") == "application/json" else response.text + }), response.status_code + except requests.RequestException as ex: + # Handle exceptions raised by the requests library + return jsonify({ + "error": "RequestException", + "message": f"Could not get contact with email '{email}' due to a request error.", + "details": str(ex) + }), 500 except Exception as ex: - return abort(500, description=f"Could not get contact with email address: {email} from Hubspot due to the following error: {ex}") + # Handle other unexpected exceptions + return jsonify({ + "error": "Internal Server Error", + "message": f"An unexpected error occurred while fetching the contact with email '{email}'.", + "details": str(ex) + }), 500 + +@app.route("/subscribe_to_newsletter", methods=["POST"]) +def subscribe_to_newsletter(): + data = request.json + email = data.get('email_address') + first_name = data.get('first_name') + last_name = data.get('last_name') + + # Ensure the required `email` field is present + if not email: + return jsonify({'error': 'Email is required'}), 400 + + newsletter_property = '' + try: + contact_properties, status_code = get_hubspot_contact_properties(email) + if status_code == 200: + newsletter_property = contact_properties['properties'].get('newsletter', None) + else: + logging.error(f"Unexpected response from HubSpot: {contact_properties}") + raise Exception(f"Unexpected error: {contact_properties}") + except Exception as e: + logging.error(f"Error while retrieving contact properties for email {email}: {e}") + + if isinstance(newsletter_property, str): + current_newsletter_values = newsletter_property.split(';') + # remove possible empty string + current_newsletter_values = list(filter(None, current_newsletter_values)) + + # Append the Newsletter value if it's not already in the array + if 'Newsletter' not in current_newsletter_values: + current_newsletter_values.append('Newsletter') + payload = { + "inputs": [ + { + "properties": { + "email": email, + "firstname": first_name, + "lastname": last_name, + "newsletter": ';'.join(current_newsletter_values) + }, + "id": email, + "idProperty": "email" + } + ] + } + url = "https://api.hubapi.com/crm/v3/objects/contacts/batch/upsert" + headers = { + "Content-Type": "application/json", + 'Authorization': 'Bearer ' + Config.HUBSPOT_API_TOKEN + } + + # Send request to HubSpot API + try: + response = requests.post(url, json=payload, headers=headers) + response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx) + return jsonify(response.json()), 200 + except requests.exceptions.RequestException as e: + return jsonify({'error': str(e)}), 500 def get_contact_properties(object_id): client = hubspot.Client.create(access_token=Config.HUBSPOT_API_TOKEN) diff --git a/tests/test_api.py b/tests/test_api.py index e2f7a70..3b8437e 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -160,6 +160,21 @@ def test_get_hubspot_contact(client): r = client.get(f"/hubspot_contact_properties/hubspot_webhook_test@test.com") assert r.status_code == 200 +def test_subscribe_to_newsletter(client): + http_method = "POST" + endpoint = "/subscribe_to_newsletter" + base_url = "http://localhost" # Default for Flask test client + full_url = f"{base_url}{endpoint}" + mock_body = {"email_address":"hubspot_webhook_test@test.com","first_name":"Test Hubspot Webhook","last_name":"Do Not Delete"} + response = client.post( + endpoint, + json=mock_body, + headers={ + "Content-Type": "application/json" + } + ) + assert response.status_code == 200 + def test_hubspot_webhook(client): http_method = "POST" endpoint = "/hubspot_webhook"