-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cdp): add mailchimp template (#25781)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
9c47f83
commit 2e13099
Showing
4 changed files
with
229 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
from posthog.cdp.templates.hog_function_template import HogFunctionTemplate | ||
|
||
template: HogFunctionTemplate = HogFunctionTemplate( | ||
status="beta", | ||
id="template-mailchimp", | ||
name="Mailchimp", | ||
description="Updates a contact in Mailchimp and subscribes new ones.", | ||
icon_url="/static/services/mailchimp.png", | ||
category=["Email Marketing"], | ||
hog=""" | ||
if (empty(inputs.email)) { | ||
print('No email set. Skipping...') | ||
return | ||
} | ||
let properties := {} | ||
for (let key, value in inputs.properties) { | ||
if (not empty(value)) { | ||
properties[key] := value | ||
} | ||
} | ||
if (inputs.include_all_properties) { | ||
for (let key, value in event.properties) { | ||
if (not empty(value) and not key like '$%') { | ||
properties[key] := value | ||
} | ||
} | ||
} | ||
let userStatus := fetch(f'https://{inputs.dataCenterId}.api.mailchimp.com/3.0/lists/{inputs.audienceId}/members/{md5Hex(inputs.email)}', { | ||
'method': 'GET', | ||
'headers': { | ||
'Authorization': f'Bearer {inputs.apiKey}', | ||
'Content-Type': 'application/json' | ||
} | ||
}) | ||
if (userStatus.status == 404 or userStatus.status == 200) { | ||
let res := fetch(f'https://{inputs.dataCenterId}.api.mailchimp.com/3.0/lists/{inputs.audienceId}/members/{md5Hex(inputs.email)}', { | ||
'method': 'PUT', | ||
'headers': { | ||
'Authorization': f'Bearer {inputs.apiKey}', | ||
'Content-Type': 'application/json' | ||
}, | ||
'body': { | ||
'email_address': inputs.email, | ||
'status_if_new': inputs.doubleOptIn ? 'pending' : 'subscribed', | ||
'merge_fields': properties | ||
} | ||
}) | ||
if (res.status >= 400) { | ||
throw Error(f'Error from api.mailchimp.com (status {userStatus.status}): {userStatus.body}') | ||
} | ||
} else if (userStatus.status >= 400) { | ||
throw Error(f'Error from api.mailchimp.com (status {userStatus.status}): {userStatus.body}') | ||
} | ||
""".strip(), | ||
inputs_schema=[ | ||
{ | ||
"key": "apiKey", | ||
"type": "string", | ||
"label": "Mailchimp API Key", | ||
"description": "See the docs here: https://mailchimp.com/help/about-api-keys/", | ||
"secret": True, | ||
"required": True, | ||
}, | ||
{ | ||
"key": "audienceId", | ||
"type": "string", | ||
"label": "Mailchimp audience ID", | ||
"description": "See the docs here: https://mailchimp.com/help/find-audience-id/", | ||
"secret": False, | ||
"required": True, | ||
}, | ||
{ | ||
"key": "dataCenterId", | ||
"type": "string", | ||
"label": "Mailchimp data center ID", | ||
"description": "You can find your Datacenter ID in the Mailchimp url in your browser when you're logged in. It's the 'us1' in 'https://us1.admin.mailchimp.com/lists/'", | ||
"secret": False, | ||
"required": True, | ||
}, | ||
{ | ||
"key": "email", | ||
"type": "string", | ||
"label": "Email of the user", | ||
"description": "Where to find the email for the contact to be created. You can use the filters section to filter out unwanted emails or internal users.", | ||
"default": "{person.properties.email}", | ||
"secret": False, | ||
"required": True, | ||
}, | ||
{ | ||
"key": "doubleOptIn", | ||
"type": "boolean", | ||
"label": "Enable double opt-in", | ||
"description": "If enabled, Mailchimp sends a confirmation email to that user, and that email is tagged with a pending subscriber status. The subscriber status automatically changes to subscribed once the user confirms the email.", | ||
"default": False, | ||
"secret": False, | ||
"required": True, | ||
}, | ||
{ | ||
"key": "include_all_properties", | ||
"type": "boolean", | ||
"label": "Include all event properties", | ||
"description": "If set, all person properties will be included. Individual properties can be overridden below.", | ||
"default": False, | ||
"secret": False, | ||
"required": True, | ||
}, | ||
{ | ||
"key": "properties", | ||
"type": "dictionary", | ||
"label": "Merge field", | ||
"description": "Map of Mailchimp merge fields and their values. You can use the filters section to filter out unwanted events. Check out this page for more details: https://mailchimp.com/developer/marketing/docs/merge-fields/#add-merge-data-to-contacts", | ||
"default": { | ||
"FNAME": "{person.properties.firstname}", | ||
"LNAME": "{person.properties.lastname}", | ||
"COMPANY": "{person.properties.company}", | ||
}, | ||
"secret": False, | ||
"required": False, | ||
}, | ||
], | ||
filters={ | ||
"events": [ | ||
{"id": "$identify", "name": "$identify", "type": "events", "order": 0}, | ||
{"id": "$set", "name": "$set", "type": "events", "order": 1}, | ||
], | ||
"actions": [], | ||
"filter_test_accounts": True, | ||
}, | ||
) |
92 changes: 92 additions & 0 deletions
92
posthog/cdp/templates/mailchimp/test_template_mailchimp.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from inline_snapshot import snapshot | ||
from posthog.cdp.templates.helpers import BaseHogFunctionTemplateTest | ||
from posthog.cdp.templates.mailchimp.template_mailchimp import ( | ||
template as template_mailchimp, | ||
) | ||
|
||
|
||
def create_inputs(**kwargs): | ||
inputs = { | ||
"apiKey": "abcdef", | ||
"audienceId": "a1b2c3", | ||
"dataCenterId": "us1", | ||
"email": "[email protected]", | ||
"include_all_properties": False, | ||
"doubleOptIn": False, | ||
"properties": {"FNAME": "Max", "LNAME": "AI", "COMPANY": "PostHog"}, | ||
} | ||
inputs.update(kwargs) | ||
|
||
return inputs | ||
|
||
|
||
class TestTemplateMailchimp(BaseHogFunctionTemplateTest): | ||
template = template_mailchimp | ||
|
||
def test_function_works(self): | ||
self.run_function( | ||
inputs=create_inputs(), | ||
globals={ | ||
"event": {"event": "$identify"}, | ||
}, | ||
) | ||
|
||
assert self.get_mock_fetch_calls()[0] == snapshot( | ||
( | ||
"https://us1.api.mailchimp.com/3.0/lists/a1b2c3/members/12d91149f17f7ac265e833ea05ef6249", | ||
{ | ||
"method": "GET", | ||
"headers": { | ||
"Authorization": "Bearer abcdef", | ||
"Content-Type": "application/json", | ||
}, | ||
}, | ||
) | ||
) | ||
|
||
def test_body_includes_all_properties_if_set(self): | ||
self.run_function( | ||
inputs=create_inputs(include_all_properties=False), | ||
globals={ | ||
"event": {"properties": {"PHONE": "+1415000000"}}, | ||
}, | ||
) | ||
|
||
assert self.get_mock_fetch_calls()[1][1]["body"]["merge_fields"] == snapshot( | ||
{"FNAME": "Max", "LNAME": "AI", "COMPANY": "PostHog"} | ||
) | ||
|
||
self.run_function( | ||
inputs=create_inputs(include_all_properties=True), | ||
globals={ | ||
"event": {"properties": {"PHONE": "+1415000000"}}, | ||
}, | ||
) | ||
|
||
assert self.get_mock_fetch_calls()[1][1]["body"]["merge_fields"] == snapshot( | ||
{ | ||
"FNAME": "Max", | ||
"LNAME": "AI", | ||
"COMPANY": "PostHog", | ||
"PHONE": "+1415000000", | ||
} | ||
) | ||
|
||
def test_double_opt_in(self): | ||
self.run_function( | ||
inputs=create_inputs(doubleOptIn=False), | ||
) | ||
|
||
assert self.get_mock_fetch_calls()[1][1]["body"]["status_if_new"] == snapshot("subscribed") | ||
|
||
self.run_function( | ||
inputs=create_inputs(doubleOptIn=True), | ||
) | ||
|
||
assert self.get_mock_fetch_calls()[1][1]["body"]["status_if_new"] == snapshot("pending") | ||
|
||
def test_function_requires_identifier(self): | ||
self.run_function(inputs=create_inputs(email="")) | ||
|
||
assert not self.get_mock_fetch_calls() | ||
assert self.get_mock_print_calls() == snapshot([("No email set. Skipping...",)]) |