diff --git a/frontend/public/services/braze.png b/frontend/public/services/braze.png new file mode 100644 index 00000000000..f354ab9c013 Binary files /dev/null and b/frontend/public/services/braze.png differ diff --git a/posthog/cdp/templates/__init__.py b/posthog/cdp/templates/__init__.py index aa26040e42d..085937e5f54 100644 --- a/posthog/cdp/templates/__init__.py +++ b/posthog/cdp/templates/__init__.py @@ -1,6 +1,7 @@ from .webhook.template_webhook import template as webhook from .slack.template_slack import template as slack from .hubspot.template_hubspot import template as hubspot +from .braze.template_braze import template as braze from .customerio.template_customerio import template as customerio, TemplateCustomerioMigrator from .intercom.template_intercom import template as intercom, TemplateIntercomMigrator from .sendgrid.template_sendgrid import template as sendgrid, TemplateSendGridMigrator @@ -42,6 +43,7 @@ HOG_FUNCTION_TEMPLATES = [ avo, gleap, google_pubsub, + braze, ] diff --git a/posthog/cdp/templates/braze/template_braze.py b/posthog/cdp/templates/braze/template_braze.py new file mode 100644 index 00000000000..a337357b6c4 --- /dev/null +++ b/posthog/cdp/templates/braze/template_braze.py @@ -0,0 +1,83 @@ +from posthog.cdp.templates.hog_function_template import HogFunctionTemplate + + +template: HogFunctionTemplate = HogFunctionTemplate( + status="beta", + id="template-braze", + name="Send events to Braze", + description="Send events to Braze", + icon_url="/static/services/braze.png", + hog=""" +let getPayload := () -> [{ + 'attributes': inputs.attributes, + 'events': [inputs.event] +}] + +let res := fetch(f'{inputs.brazeEndpoint}/users/track', { + 'method': 'POST', + 'headers': { + 'Authorization': f'Bearer {inputs.apiKey}', + 'Content-Type': 'application/json' + }, + 'body': getPayload() +}) + +if (res.status >= 200 and res.status < 300) { + print('Event sent successfully!') +} else { + throw Error(f'Error sending event: {res.status} {res.body}') +} +""".strip(), + inputs_schema=[ + { + "key": "brazeEndpoint", + "type": "choice", + "label": "Braze REST Endpoint", + "description": "The endpoint identifier where your Braze instance is located, see the docs here: https://www.braze.com/docs/api/basics", + "choices": [ + {"label": "US-01", "value": "https://rest.iad-01.braze.com"}, + {"label": "US-02", "value": "https://rest.iad-02.braze.com"}, + {"label": "US-03", "value": "https://rest.iad-03.braze.com"}, + {"label": "US-04", "value": "https://rest.iad-04.braze.com"}, + {"label": "US-05", "value": "https://rest.iad-05.braze.com"}, + {"label": "US-06", "value": "https://rest.iad-06.braze.com"}, + {"label": "US-08", "value": "https://rest.iad-08.braze.com"}, + {"label": "EU-01", "value": "https://rest.fra-01.braze.eu"}, + {"label": "EU-02", "value": "https://rest.fra-02.braze.eu"}, + ], + "default": "", + "secret": False, + "required": True, + }, + { + "key": "apiKey", + "type": "string", + "label": "Your Braze API Key", + "description": "See the docs here: https://www.braze.com/docs/api/api_key/", + "default": "", + "secret": True, + "required": True, + }, + { + "key": "attributes", + "type": "json", + "label": "Attributes to set", + "default": {"email": "{person.properties.email}"}, + "secret": False, + "required": True, + }, + { + "key": "event", + "type": "json", + "label": "Event payload", + "default": { + "properties": "{event.properties}", + "external_id": "{event.distinct_id}", + "name": "{event.name}", + "time": "{event.timestamp}", + }, + "secret": False, + "required": True, + }, + ], +) diff --git a/posthog/cdp/templates/braze/test_template_braze.py b/posthog/cdp/templates/braze/test_template_braze.py new file mode 100644 index 00000000000..1aa52d78269 --- /dev/null +++ b/posthog/cdp/templates/braze/test_template_braze.py @@ -0,0 +1,48 @@ +from freezegun import freeze_time +from posthog.cdp.templates.helpers import BaseHogFunctionTemplateTest +from posthog.cdp.templates.braze.template_braze import template as template_braze + + +class TestTemplateBraze(BaseHogFunctionTemplateTest): + template = template_braze + + @freeze_time("2024-04-16T12:34:51Z") + def test_function_works(self): + res = self.run_function( + inputs={ + "brazeEndpoint": "https://rest.fra-01.braze.eu", + "apiKey": "my_secret_key", + "attributes": {"email": "{person.properties.email}"}, + "event": { + "name": "{event.name}", + "time": "{event.timestamp}", + "properties": "{event.properties}", + "external_id": "{event.distinct_id}", + }, + } + ) + + assert res.result is None + assert self.get_mock_fetch_calls()[0] == ( + "https://rest.fra-01.braze.eu/users/track", + { + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer my_secret_key", + }, + "body": [ + { + "attributes": {"email": "{person.properties.email}"}, + "events": [ + { + "external_id": "{event.distinct_id}", + "name": "{event.name}", + "properties": "{event.properties}", + "time": "{event.timestamp}", + }, + ], + } + ], + "method": "POST", + }, + )