From 84adef40790f992140bc0d5e024e03ae778c31f4 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 4 Feb 2025 16:30:18 +0000 Subject: [PATCH 01/19] Add WIP reddit pixel --- .../mapping/HogFunctionMappings.tsx | 12 +- posthog/cdp/templates/__init__.py | 2 + .../reddit/template_reddit_conversions_api.py | 1 + .../templates/reddit/template_reddit_pixel.py | 228 ++++++++++++++++++ 4 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 posthog/cdp/templates/reddit/template_reddit_conversions_api.py create mode 100644 posthog/cdp/templates/reddit/template_reddit_pixel.py diff --git a/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx b/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx index ee7a8d1953c33..c3d024ad8929b 100644 --- a/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx +++ b/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx @@ -25,11 +25,13 @@ import { HogFunctionInputs } from '../HogFunctionInputs' const humanize = (value: string): string => { // Simple replacement from something like MY_STRING-here to My string here - return value - .toLowerCase() - .replace(/_/g, ' ') - .replace(/-/g, ' ') - .replace(/\b\w/g, (char) => char.toUpperCase()) + return ( + value + .toLowerCase() + .replace(/_/g, ' ') + .replace(/-/g, ' ') + .replace(/\b\w/g, (char) => char.toUpperCase()) + 'humanized' + ) } const MappingSummary = memo(function MappingSummary({ diff --git a/posthog/cdp/templates/__init__.py b/posthog/cdp/templates/__init__.py index 7d92420dfb242..be31db894f814 100644 --- a/posthog/cdp/templates/__init__.py +++ b/posthog/cdp/templates/__init__.py @@ -53,6 +53,7 @@ from ._internal.template_blank import blank_site_destination, blank_site_app from .snapchat_ads.template_snapchat_ads import template as snapchat_ads from .snapchat_ads.template_pixel import template_snapchat_pixel as snapchat_pixel +from .reddit.template_reddit_pixel import template_reddit_pixel as reddit_pixel HOG_FUNCTION_TEMPLATES = [ @@ -97,6 +98,7 @@ meta_ads, microsoft_teams, posthog, + reddit_pixel, rudderstack, salesforce_create, salesforce_update, diff --git a/posthog/cdp/templates/reddit/template_reddit_conversions_api.py b/posthog/cdp/templates/reddit/template_reddit_conversions_api.py new file mode 100644 index 0000000000000..70396b486bf66 --- /dev/null +++ b/posthog/cdp/templates/reddit/template_reddit_conversions_api.py @@ -0,0 +1 @@ +# https://ads-api.reddit.com/docs/v2/#tag/Conversions diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py new file mode 100644 index 0000000000000..49c8df4f3ac88 --- /dev/null +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -0,0 +1,228 @@ +from posthog.cdp.templates.hog_function_template import HogFunctionMappingTemplate, HogFunctionTemplate + +common_inputs = [ + { + "key": "eventProperties", + "type": "dictionary", + "description": "Map of Reddit event attributes and their values. Check out these pages for more details: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel and https://business.reddithelp.com/s/article/about-event-metadata", + "label": "Event parameters", + "default": { + "conversion_id": "{event.uuid}", + "products": "{event.properties.products ? arrayMap(product -> ({'id': product.product_id, 'category': product.category, 'name': product.name}), event.properties.product) : [{id: event.properties.product_id, category: event.properties.category, name: event.properties.name}]}", + "value": "{toFloat(event.properties.value ?? event.properties.revenue)}", + "currency": "{event.properties.currency}", + }, + "secret": False, + "required": False, + }, +] + +template_reddit_pixel: HogFunctionTemplate = HogFunctionTemplate( + free=True, + status="alpha", + type="site_destination", + id="template-reddit-pixel", + name="Reddit Pixel", + description="Track how many Reddit users interact with your website.", + icon_url="/static/services/reddit.png", + category=["Advertisement"], + hog=""" +// Adds window.rdt and lazily loads the Reddit Pixel script +function initSnippet() { + !function(w,d){if(!w.rdt){var p=w.rdt=function(){p.sendEvent?p.sendEvent.apply(p,arguments):p.callQueue.push(arguments)};p.callQueue=[];var t=d.createElement("script");t.src="https://www.redditstatic.com/ads/pixel.js",t.async=!0;var s=d.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}}(window,document); +} + +export function onLoad({ inputs }) { + initSnippet(); + let userProperties = {}; + for (const [key, value] of Object.entries(inputs.userProperties)) { + if (value) { + userProperties[key] = value; + } + }; + rdt('init', inputs.pixelId, userProperties); +} +export function onEvent({ inputs }) { + let eventProperties = {}; + for (const [key, value] of Object.entries(inputs.eventProperties)) { + if (value) { + eventProperties[key] = value; + } + }; + rdt('track', inputs.eventType, eventProperties); +} +""".strip(), + inputs_schema=[ + { + "key": "pixelId", + "type": "string", + "label": "Pixel ID", + "description": "You must obtain a Pixel ID to use the Reddit Pixel. If you've already set up a Pixel for your website, we recommend that you use the same Pixel ID for your browser and server events.", + "default": "", + "secret": False, + "required": True, + }, + { + "key": "userProperties", + "type": "dictionary", + "description": "Map of Reddit user parameters and their values. Check out this page for more details: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "label": "User parameters", + "default": { + "email": "{person.properties.email}", + }, + "secret": False, + "required": False, + }, + ], + # See our event specification here: + # https://posthog.com/docs/data/event-spec/ecommerce-events + # And reddit's here: + # https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel + mapping_templates=[ + HogFunctionMappingTemplate( + name="Page Visit", + include_by_default=True, + filters={"events": [{"id": "$pageview", "name": "Pageview", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "PageVisit", + "required": True, + }, + *common_inputs, + ], + ), + HogFunctionMappingTemplate( + name="Search", + include_by_default=True, + filters={"events": [{"id": "Products Searched", "name": "Products Searched", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "Search", + "required": True, + }, + *common_inputs, + ], + ), + HogFunctionMappingTemplate( + name="Add To Cart", + include_by_default=True, + filters={"events": [{"id": "Product Added", "name": "Add To Cart", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "AddToCart", + "required": True, + }, + *common_inputs, + ], + ), + HogFunctionMappingTemplate( + name="Add To Wishlist", + include_by_default=True, + filters={"events": [{"id": "Product Added to Wishlist", "name": "Add To Wishlist", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "AddToWishlist", + "required": True, + }, + *common_inputs, + ], + ), + HogFunctionMappingTemplate( + name="Purchase", + include_by_default=True, + filters={"events": [{"id": "Purchase", "name": "Purchase", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "Purchase", + "required": True, + }, + *common_inputs, + ], + ), + # Some events not in our spec: + HogFunctionMappingTemplate( + name="View Content", + include_by_default=True, + filters={"events": [{"id": "View Content", "name": "ViewContent", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "ViewContent", + "required": True, + }, + *common_inputs, + ], + ), + HogFunctionMappingTemplate( + name="Lead", + include_by_default=True, + filters={"events": [{"id": "Lead", "name": "Lead", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "Lead", + "required": True, + }, + *common_inputs, + ], + ), + HogFunctionMappingTemplate( + name="Sign Up", + include_by_default=True, + filters={"events": [{"id": "Sign Up", "name": "Sign Up", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "SignUp", + "required": True, + }, + *common_inputs, + ], + ), + HogFunctionMappingTemplate( + name="name", + include_by_default=True, + filters={"events": [{"id": "e.id", "name": "e.name", "type": "events"}]}, + inputs_schema=[ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + "default": "default", + "required": True, + }, + *common_inputs, + ], + ), + ], +) From 4dd6a7cb247b72844ba9e9290452323c7be02688 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Thu, 6 Feb 2025 14:03:09 +0000 Subject: [PATCH 02/19] Remove stupid test code --- .../hogfunctions/mapping/HogFunctionMappings.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx b/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx index c3d024ad8929b..ee7a8d1953c33 100644 --- a/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx +++ b/frontend/src/scenes/pipeline/hogfunctions/mapping/HogFunctionMappings.tsx @@ -25,13 +25,11 @@ import { HogFunctionInputs } from '../HogFunctionInputs' const humanize = (value: string): string => { // Simple replacement from something like MY_STRING-here to My string here - return ( - value - .toLowerCase() - .replace(/_/g, ' ') - .replace(/-/g, ' ') - .replace(/\b\w/g, (char) => char.toUpperCase()) + 'humanized' - ) + return value + .toLowerCase() + .replace(/_/g, ' ') + .replace(/-/g, ' ') + .replace(/\b\w/g, (char) => char.toUpperCase()) } const MappingSummary = memo(function MappingSummary({ From ab08ab06afd647a8817326b221463c65db4283e1 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Mon, 10 Feb 2025 12:59:48 +0000 Subject: [PATCH 03/19] Fix event name --- .../templates/reddit/template_reddit_pixel.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index 49c8df4f3ac88..b5e9b3d2901c9 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -146,7 +146,7 @@ HogFunctionMappingTemplate( name="Purchase", include_by_default=True, - filters={"events": [{"id": "Purchase", "name": "Purchase", "type": "events"}]}, + filters={"events": [{"id": "Order Completed", "name": "Purchase", "type": "events"}]}, inputs_schema=[ { "key": "eventType", @@ -208,21 +208,21 @@ *common_inputs, ], ), - HogFunctionMappingTemplate( - name="name", - include_by_default=True, - filters={"events": [{"id": "e.id", "name": "e.name", "type": "events"}]}, - inputs_schema=[ - { - "key": "eventType", - "type": "string", - "label": "Event Type", - "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", - "default": "default", - "required": True, - }, - *common_inputs, - ], - ), + # HogFunctionMappingTemplate( + # name="name", + # include_by_default=True, + # filters={"events": [{"id": "e.id", "name": "e.name", "type": "events"}]}, + # inputs_schema=[ + # { + # "key": "eventType", + # "type": "string", + # "label": "Event Type", + # "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", + # "default": "default", + # "required": True, + # }, + # *common_inputs, + # ], + # ), ], ) From 920bd9c6e509b9982ebe7e157f178131aa7ec057 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Mon, 10 Feb 2025 21:14:22 +0000 Subject: [PATCH 04/19] Add support for custom events --- .../templates/reddit/template_reddit_pixel.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index b5e9b3d2901c9..f636d6ad74e5d 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -43,13 +43,30 @@ rdt('init', inputs.pixelId, userProperties); } export function onEvent({ inputs }) { + const builtInEvents = [ + 'PageVisit', + 'Search', + 'AddToCart', + 'AddToWishlist', + 'Purchase', + 'ViewContent', + 'Lead', + 'SignUp', + ]; let eventProperties = {}; for (const [key, value] of Object.entries(inputs.eventProperties)) { if (value) { eventProperties[key] = value; } }; - rdt('track', inputs.eventType, eventProperties); + if (builtInEvents.includes(inputs.eventType)) { + rdt('track', inputs.eventType, eventProperties); + } else { + rdt('track', 'Custom', { + customEventName: inputs.eventType, + ...eventProperties, + }); + } } """.strip(), inputs_schema=[ From fbb1853e529c61630129549c8bf7cee34131192a Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 13:12:25 +0000 Subject: [PATCH 05/19] Working tests for site destinations --- posthog/cdp/templates/helpers.py | 99 ++++++++++++++++++- .../reddit/template_reddit_conversions_api.py | 1 - .../templates/reddit/template_reddit_pixel.py | 2 +- .../reddit/test_template_reddit_pixel.py | 23 +++++ requirements-dev.in | 3 +- requirements-dev.txt | 2 + 6 files changed, 126 insertions(+), 4 deletions(-) delete mode 100644 posthog/cdp/templates/reddit/template_reddit_conversions_api.py create mode 100644 posthog/cdp/templates/reddit/test_template_reddit_pixel.py diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index 2cc4721d1119d..7c5a1c93a8b67 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -1,9 +1,16 @@ +import json from typing import Any, Optional, cast from unittest.mock import MagicMock + +from posthog.api.hog_function_template import HogFunctionTemplates +from posthog.cdp.site_functions import get_transpiled_function from posthog.cdp.templates.hog_function_template import HogFunctionTemplate from posthog.cdp.validation import compile_hog -from posthog.test.base import BaseTest +from posthog.models import HogFunction +from posthog.models.utils import uuid7 +from posthog.test.base import BaseTest, APIBaseTest from common.hogvm.python.execute import execute_bytecode +from common.hogvm.python.stl import now class BaseHogFunctionTemplateTest(BaseTest): @@ -89,3 +96,93 @@ def run_function(self, inputs: dict, globals=None, functions: Optional[dict] = N globals, functions=final_functions, ) + + +class BaseSiteDestinationFunctionTest(APIBaseTest): + template: HogFunctionTemplate + window_fn: str + inputs: dict + + def _transpiled(self): + HogFunctionTemplates._load_templates() + # use the API to create a HogFunction based on the template + payload = { + "description": self.template.description, + "enabled": True, + "filters": self.template.filters, + "icon_url": self.template.icon_url, + "inputs": self.inputs, + "mappings": [ + { + "filters": m.filters, + "inputs": {i["key"]: {"value": i["default"]} for i in m.inputs_schema}, + "inputs_schema": m.inputs_schema, + "name": m.name, + } + for m in (self.template.mapping_templates or []) + ], + "masking": self.template.masking, + "name": self.template.name, + "template_id": self.template.id, + "type": self.template.type, + } + response = self.client.post( + f"/api/projects/{self.team.id}/hog_functions/", + data=payload, + ) + function_id = response.json()["id"] + + # load from the DB based on the created ID + hog_function = HogFunction.objects.get(id=function_id) + + transpiled = get_transpiled_function(hog_function) + + return transpiled + + def _process_event( + self, event_name: str, event_properties: Optional[dict] = None, person_properties: Optional[dict] = None + ): + event_id = str(uuid7()) + js_globals = { + "event": {"uuid": event_id, "event": event_name, "properties": event_properties or {}, "timestamp": now()}, + "person": {"properties": person_properties or {}}, + "groups": {}, + } + transpiled = self._transpiled() + js = f""" + {JS_STDLIB} + + const calls = []; + const {self.window_fn} = (...args) => calls.push(args); + window.{self.window_fn} = {self.window_fn}; + + const globals = {json.dumps(js_globals)}; + const posthog = {{ + get_property: (key) => key === '$stored_person_properties' ? globals.person.properties : null, + }}; + + const initFn = {transpiled}().init; + + const processEvent = initFn({{ posthog, callback: console.log }}).processEvent; + + processEvent(globals, posthog);; + """ + import STPyV8 + + with STPyV8.JSContext() as ctxt: + ctxt.eval(js) + calls_json = ctxt.eval("JSON.stringify(calls)") + calls = json.loads(calls_json) + assert isinstance(calls, list) + return (event_id, calls) + + +JS_STDLIB = """ +const document = {}; +const window = {}; + +// easy but hacky impl, if we need a correct one, see +// https://github.com/zloirock/core-js/blob/4b7201bb18a66481d8aa7ca28782c151bc99f152/packages/core-js/modules/web.structured-clone.js#L109 +const structuredClone = (obj) => JSON.parse(JSON.stringify(obj)); +window.structuredClone = structuredClone; +""" diff --git a/posthog/cdp/templates/reddit/template_reddit_conversions_api.py b/posthog/cdp/templates/reddit/template_reddit_conversions_api.py deleted file mode 100644 index 70396b486bf66..0000000000000 --- a/posthog/cdp/templates/reddit/template_reddit_conversions_api.py +++ /dev/null @@ -1 +0,0 @@ -# https://ads-api.reddit.com/docs/v2/#tag/Conversions diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index f636d6ad74e5d..9f98b6e47f9f5 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -8,7 +8,7 @@ "label": "Event parameters", "default": { "conversion_id": "{event.uuid}", - "products": "{event.properties.products ? arrayMap(product -> ({'id': product.product_id, 'category': product.category, 'name': product.name}), event.properties.product) : [{id: event.properties.product_id, category: event.properties.category, name: event.properties.name}]}", + "products": "{event.properties.products ? arrayMap(product -> ({'id': product.product_id, 'category': product.category, 'name': product.name}), event.properties.products) : event.properties.product_id ? [{id: event.properties.product_id, category: event.properties.category, name: event.properties.name}] : undefined}", "value": "{toFloat(event.properties.value ?? event.properties.revenue)}", "currency": "{event.properties.currency}", }, diff --git a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py new file mode 100644 index 0000000000000..fd66c1d0d1788 --- /dev/null +++ b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py @@ -0,0 +1,23 @@ +from posthog.cdp.templates.helpers import BaseSiteDestinationFunctionTest +from posthog.cdp.templates.reddit.template_reddit_pixel import template_reddit_pixel + + +class TestTemplateRedditAds(BaseSiteDestinationFunctionTest): + template = template_reddit_pixel + inputs = { + "pixelId": { + "value": "pixel12345", + }, + "userProperties": { + "value": {"email": "{person.properties.email}"}, + }, + } + window_fn = "rdt" + + def test_pageview(self): + email = "test@example.com" + event_id, calls = self._process_event("$pageview", {}, {"email": email}) + + assert len(calls) == 2 + assert calls[0] == ["init", "pixel12345", {"email": email}] + assert calls[1] == ["track", "PageVisit", {"conversion_id": event_id}] diff --git a/requirements-dev.in b/requirements-dev.in index 13d50b9a4428d..294f5a49fb01d 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -60,4 +60,5 @@ prance==23.06.21.0 openapi-spec-validator==0.7.1 # Needed for prance as a validation backend deepeval==1.5.5 tiktoken==0.8.* -tach~=0.20.0 \ No newline at end of file +tach~=0.20.0 +stpyv8==13.1.201.22 \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 25eacc83a9460..509908df12cde 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -615,6 +615,8 @@ sqlparse==0.4.4 # via # -c requirements.txt # django +stpyv8==13.1.201.22 + # via -r requirements-dev.in syrupy==4.6.4 # via -r requirements-dev.in tabulate==0.9.0 From d9dd697d1785de0ef116664666407e3174ace1af Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 14:16:09 +0000 Subject: [PATCH 06/19] Add more tests --- posthog/cdp/templates/helpers.py | 20 ++--- .../reddit/test_template_reddit_pixel.py | 77 ++++++++++++++++++- 2 files changed, 83 insertions(+), 14 deletions(-) diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index 7c5a1c93a8b67..3649db1b75794 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -100,10 +100,13 @@ def run_function(self, inputs: dict, globals=None, functions: Optional[dict] = N class BaseSiteDestinationFunctionTest(APIBaseTest): template: HogFunctionTemplate - window_fn: str + track_fn: str inputs: dict + _transpiled: str + + def setUp(self): + super().setUp() - def _transpiled(self): HogFunctionTemplates._load_templates() # use the API to create a HogFunction based on the template payload = { @@ -135,9 +138,7 @@ def _transpiled(self): # load from the DB based on the created ID hog_function = HogFunction.objects.get(id=function_id) - transpiled = get_transpiled_function(hog_function) - - return transpiled + self._transpiled = get_transpiled_function(hog_function) def _process_event( self, event_name: str, event_properties: Optional[dict] = None, person_properties: Optional[dict] = None @@ -148,20 +149,19 @@ def _process_event( "person": {"properties": person_properties or {}}, "groups": {}, } - transpiled = self._transpiled() js = f""" {JS_STDLIB} const calls = []; - const {self.window_fn} = (...args) => calls.push(args); - window.{self.window_fn} = {self.window_fn}; + const {self.track_fn} = (...args) => calls.push(args); + window.{self.track_fn} = {self.track_fn}; const globals = {json.dumps(js_globals)}; const posthog = {{ get_property: (key) => key === '$stored_person_properties' ? globals.person.properties : null, }}; - const initFn = {transpiled}().init; + const initFn = {self._transpiled}().init; const processEvent = initFn({{ posthog, callback: console.log }}).processEvent; @@ -174,7 +174,7 @@ def _process_event( calls_json = ctxt.eval("JSON.stringify(calls)") calls = json.loads(calls_json) assert isinstance(calls, list) - return (event_id, calls) + return event_id, calls JS_STDLIB = """ diff --git a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py index fd66c1d0d1788..b6d55e266eac0 100644 --- a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py @@ -1,6 +1,9 @@ from posthog.cdp.templates.helpers import BaseSiteDestinationFunctionTest from posthog.cdp.templates.reddit.template_reddit_pixel import template_reddit_pixel +TEST_EMAIL = "test@example.com" +TEST_PRODUCT_ID = "product12345" + class TestTemplateRedditAds(BaseSiteDestinationFunctionTest): template = template_reddit_pixel @@ -12,12 +15,78 @@ class TestTemplateRedditAds(BaseSiteDestinationFunctionTest): "value": {"email": "{person.properties.email}"}, }, } - window_fn = "rdt" + track_fn = "rdt" def test_pageview(self): - email = "test@example.com" - event_id, calls = self._process_event("$pageview", {}, {"email": email}) + event_id, calls = self._process_event("$pageview", {}, {"email": TEST_EMAIL}) assert len(calls) == 2 - assert calls[0] == ["init", "pixel12345", {"email": email}] + assert calls[0] == ["init", "pixel12345", {"email": TEST_EMAIL}] assert calls[1] == ["track", "PageVisit", {"conversion_id": event_id}] + + def test_products_searched(self): + event_id, calls = self._process_event( + "Products Searched", + { + "products": [{"product_id": TEST_PRODUCT_ID}], + }, + {"email": TEST_EMAIL}, + ) + + assert calls[1] == ["track", "Search", {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}]}] + + def test_product_added(self): + event_id, calls = self._process_event( + "Product Added", + { + "products": [{"product_id": TEST_PRODUCT_ID}], + "value": 42, + "currency": "USD", + }, + {"email": TEST_EMAIL}, + ) + + assert calls[1] == [ + "track", + "AddToCart", + {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}], "currency": "USD", "value": 42}, + ] + + def test_product_added_to_wishlist(self): + event_id, calls = self._process_event( + "Product Added to Wishlist", + { + "products": [{"product_id": TEST_PRODUCT_ID}], + "value": 42, + "currency": "USD", + }, + {"email": TEST_EMAIL}, + ) + + assert calls[1] == [ + "track", + "AddToWishlist", + {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}], "currency": "USD", "value": 42}, + ] + + def test_purchase(self): + event_id, calls = self._process_event( + "Order Completed", + { + "products": [{"product_id": TEST_PRODUCT_ID}], + "value": 42, + "currency": "USD", + }, + {"email": TEST_EMAIL}, + ) + + assert calls[1] == [ + "track", + "Purchase", + {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}], "currency": "USD", "value": 42}, + ] + + def test_event_not_in_spec(self): + event_id, calls = self._process_event("Event Not In Spec", {}, {"email": TEST_EMAIL}) + + assert len(calls) == 1 # Only init call From fe2e0ccc00418a1dec18650c411c6c88260bda29 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 14:27:01 +0000 Subject: [PATCH 07/19] Remove commented-out --- .../templates/reddit/template_reddit_pixel.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index 9f98b6e47f9f5..d0f91d50b6b16 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -225,21 +225,5 @@ *common_inputs, ], ), - # HogFunctionMappingTemplate( - # name="name", - # include_by_default=True, - # filters={"events": [{"id": "e.id", "name": "e.name", "type": "events"}]}, - # inputs_schema=[ - # { - # "key": "eventType", - # "type": "string", - # "label": "Event Type", - # "description": "Check out this page for possible event types: https://business.reddithelp.com/s/article/manual-conversion-events-with-the-reddit-pixel", - # "default": "default", - # "required": True, - # }, - # *common_inputs, - # ], - # ), ], ) From dd065266930517395112082a78cd6609ca93bf65 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 14:32:40 +0000 Subject: [PATCH 08/19] Add comments --- posthog/cdp/templates/helpers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index 3649db1b75794..4077ed26ed691 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -149,6 +149,9 @@ def _process_event( "person": {"properties": person_properties or {}}, "groups": {}, } + # This makes a couple of assumptions, which hold in the example I've seen but might not hold forever: + # - the template's init code adds a track function to the window object / global scope + # - if the track function already exists, the template's init code (which typically adds an html script element) will not be run js = f""" {JS_STDLIB} @@ -171,12 +174,15 @@ def _process_event( with STPyV8.JSContext() as ctxt: ctxt.eval(js) - calls_json = ctxt.eval("JSON.stringify(calls)") + calls_json = ctxt.eval( + "JSON.stringify(calls)" + ) # send a string type over the bridge as complex types can cause crashes calls = json.loads(calls_json) assert isinstance(calls, list) return event_id, calls +# STPyV8 doesn't provide a window or document object, so set these up with a minimal implementation JS_STDLIB = """ const document = {}; const window = {}; From 685203cf530217efa6f9efaaec815d151d168fc3 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 14:38:15 +0000 Subject: [PATCH 09/19] Move pixel id to a constant --- .../reddit/test_template_reddit_pixel.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py index b6d55e266eac0..11ba143af6b6c 100644 --- a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py @@ -3,13 +3,14 @@ TEST_EMAIL = "test@example.com" TEST_PRODUCT_ID = "product12345" +TEST_PIXEL_ID = "pixel12345" class TestTemplateRedditAds(BaseSiteDestinationFunctionTest): template = template_reddit_pixel inputs = { "pixelId": { - "value": "pixel12345", + "value": TEST_PIXEL_ID, }, "userProperties": { "value": {"email": "{person.properties.email}"}, @@ -21,7 +22,7 @@ def test_pageview(self): event_id, calls = self._process_event("$pageview", {}, {"email": TEST_EMAIL}) assert len(calls) == 2 - assert calls[0] == ["init", "pixel12345", {"email": TEST_EMAIL}] + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] assert calls[1] == ["track", "PageVisit", {"conversion_id": event_id}] def test_products_searched(self): @@ -32,7 +33,8 @@ def test_products_searched(self): }, {"email": TEST_EMAIL}, ) - + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] assert calls[1] == ["track", "Search", {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}]}] def test_product_added(self): @@ -45,7 +47,8 @@ def test_product_added(self): }, {"email": TEST_EMAIL}, ) - + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] assert calls[1] == [ "track", "AddToCart", @@ -62,7 +65,8 @@ def test_product_added_to_wishlist(self): }, {"email": TEST_EMAIL}, ) - + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] assert calls[1] == [ "track", "AddToWishlist", @@ -79,7 +83,8 @@ def test_purchase(self): }, {"email": TEST_EMAIL}, ) - + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] assert calls[1] == [ "track", "Purchase", @@ -90,3 +95,4 @@ def test_event_not_in_spec(self): event_id, calls = self._process_event("Event Not In Spec", {}, {"email": TEST_EMAIL}) assert len(calls) == 1 # Only init call + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] From 9ee606a8b50ed4720f2ac3e7eab2864c2e7d7592 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 14:59:07 +0000 Subject: [PATCH 10/19] Handle events in saas spec --- posthog/cdp/templates/helpers.py | 10 ++-- .../templates/reddit/template_reddit_pixel.py | 13 +++--- .../reddit/test_template_reddit_pixel.py | 46 +++++++++++++++++++ 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index 4077ed26ed691..14b4e0e29d4f1 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -2,7 +2,6 @@ from typing import Any, Optional, cast from unittest.mock import MagicMock -from posthog.api.hog_function_template import HogFunctionTemplates from posthog.cdp.site_functions import get_transpiled_function from posthog.cdp.templates.hog_function_template import HogFunctionTemplate from posthog.cdp.validation import compile_hog @@ -107,8 +106,7 @@ class BaseSiteDestinationFunctionTest(APIBaseTest): def setUp(self): super().setUp() - HogFunctionTemplates._load_templates() - # use the API to create a HogFunction based on the template + # TODO do this without calling the API. There's a lot of logic in the endpoint which would need to be extracted payload = { "description": self.template.description, "enabled": True, @@ -149,9 +147,9 @@ def _process_event( "person": {"properties": person_properties or {}}, "groups": {}, } - # This makes a couple of assumptions, which hold in the example I've seen but might not hold forever: - # - the template's init code adds a track function to the window object / global scope - # - if the track function already exists, the template's init code (which typically adds an html script element) will not be run + # We rely on the fact that most tracking scripts have idempotent init functions. + # This means that we can add our own tracking function first, and the regular init code (which typically adds an HTML script element) won't run. + # This lets us run the processEvent code in a minimal JS environment, and capture the outputs for given inputs. js = f""" {JS_STDLIB} diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index d0f91d50b6b16..d714425c8313f 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -176,11 +176,10 @@ *common_inputs, ], ), - # Some events not in our spec: HogFunctionMappingTemplate( - name="View Content", + name="Product Viewed", include_by_default=True, - filters={"events": [{"id": "View Content", "name": "ViewContent", "type": "events"}]}, + filters={"events": [{"id": "Product Viewed", "name": "Product Viewed", "type": "events"}]}, inputs_schema=[ { "key": "eventType", @@ -194,9 +193,9 @@ ], ), HogFunctionMappingTemplate( - name="Lead", + name="Lead Generated", include_by_default=True, - filters={"events": [{"id": "Lead", "name": "Lead", "type": "events"}]}, + filters={"events": [{"id": "Lead Generated", "name": "Lead Generated", "type": "events"}]}, inputs_schema=[ { "key": "eventType", @@ -210,9 +209,9 @@ ], ), HogFunctionMappingTemplate( - name="Sign Up", + name="Signed Up", include_by_default=True, - filters={"events": [{"id": "Sign Up", "name": "Sign Up", "type": "events"}]}, + filters={"events": [{"id": "Signed Up", "name": "Signed Up", "type": "events"}]}, inputs_schema=[ { "key": "eventType", diff --git a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py index 11ba143af6b6c..6cbd0d3542b76 100644 --- a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py @@ -73,6 +73,24 @@ def test_product_added_to_wishlist(self): {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}], "currency": "USD", "value": 42}, ] + def test_product_viewed(self): + event_id, calls = self._process_event( + "Product Viewed", + { + "products": [{"product_id": TEST_PRODUCT_ID}], + "value": 42, + "currency": "USD", + }, + {"email": TEST_EMAIL}, + ) + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] + assert calls[1] == [ + "track", + "ViewContent", + {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}], "currency": "USD", "value": 42}, + ] + def test_purchase(self): event_id, calls = self._process_event( "Order Completed", @@ -91,6 +109,34 @@ def test_purchase(self): {"conversion_id": event_id, "products": [{"id": TEST_PRODUCT_ID}], "currency": "USD", "value": 42}, ] + def test_lead_generated(self): + event_id, calls = self._process_event( + "Lead Generated", + {}, + {"email": TEST_EMAIL}, + ) + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] + assert calls[1] == [ + "track", + "Lead", + {"conversion_id": event_id}, + ] + + def test_signed_up(self): + event_id, calls = self._process_event( + "Signed Up", + {}, + {"email": TEST_EMAIL}, + ) + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] + assert calls[1] == [ + "track", + "SignUp", + {"conversion_id": event_id}, + ] + def test_event_not_in_spec(self): event_id, calls = self._process_event("Event Not In Spec", {}, {"email": TEST_EMAIL}) From adab33a95f95d8faf14d944ed5a26c1990cd8158 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 15:00:37 +0000 Subject: [PATCH 11/19] Move import --- posthog/cdp/templates/helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index 14b4e0e29d4f1..180a84dd3f6bb 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -11,6 +11,8 @@ from common.hogvm.python.execute import execute_bytecode from common.hogvm.python.stl import now +import STPyV8 + class BaseHogFunctionTemplateTest(BaseTest): template: HogFunctionTemplate @@ -168,7 +170,6 @@ def _process_event( processEvent(globals, posthog);; """ - import STPyV8 with STPyV8.JSContext() as ctxt: ctxt.eval(js) From c600399182624cd2b66015ce389f1a5922879b5a Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 15:02:03 +0000 Subject: [PATCH 12/19] mypy --- posthog/cdp/templates/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index 180a84dd3f6bb..d639c7da45dfe 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -118,7 +118,7 @@ def setUp(self): "mappings": [ { "filters": m.filters, - "inputs": {i["key"]: {"value": i["default"]} for i in m.inputs_schema}, + "inputs": {i["key"]: {"value": i["default"]} for i in (m.inputs_schema or [])}, "inputs_schema": m.inputs_schema, "name": m.name, } From da4a19fc2d8129c49f145711445eba310e8c580a Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Tue, 11 Feb 2025 20:59:09 +0000 Subject: [PATCH 13/19] Add debugging --- posthog/cdp/templates/helpers.py | 3 +++ .../templates/reddit/template_reddit_pixel.py | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index d639c7da45dfe..640eda37ee543 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -162,6 +162,9 @@ def _process_event( const globals = {json.dumps(js_globals)}; const posthog = {{ get_property: (key) => key === '$stored_person_properties' ? globals.person.properties : null, + config: {{ + debug: true, + }} }}; const initFn = {self._transpiled}().init; diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index d714425c8313f..710e8747c638d 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -40,9 +40,12 @@ userProperties[key] = value; } }; + if (posthog.config.debug) { + console.log('[PostHog.js] rdt init', inputs.pixelId, userProperties); + } rdt('init', inputs.pixelId, userProperties); } -export function onEvent({ inputs }) { +export function onEvent({ inputs, posthog }) { const builtInEvents = [ 'PageVisit', 'Search', @@ -59,14 +62,17 @@ eventProperties[key] = value; } }; + let eventName; if (builtInEvents.includes(inputs.eventType)) { - rdt('track', inputs.eventType, eventProperties); + eventName = inputs.eventType; } else { - rdt('track', 'Custom', { - customEventName: inputs.eventType, - ...eventProperties, - }); + eventName = 'Custom'; + eventProperties.customEventName = inputs.eventType; + } + if (posthog.config.debug) { + console.log('[PostHog.js] rdt track', eventName, eventProperties); } + rdt('track', eventName, eventProperties); } """.strip(), inputs_schema=[ From c6b88fc26e51f73e643d0b690adfe7a1225017f9 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Wed, 12 Feb 2025 09:17:12 +0000 Subject: [PATCH 14/19] Add a test for product from top-level event properties --- .../templates/reddit/template_reddit_pixel.py | 4 +-- .../reddit/test_template_reddit_pixel.py | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index 710e8747c638d..02366e59bf13b 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -8,8 +8,8 @@ "label": "Event parameters", "default": { "conversion_id": "{event.uuid}", - "products": "{event.properties.products ? arrayMap(product -> ({'id': product.product_id, 'category': product.category, 'name': product.name}), event.properties.products) : event.properties.product_id ? [{id: event.properties.product_id, category: event.properties.category, name: event.properties.name}] : undefined}", - "value": "{toFloat(event.properties.value ?? event.properties.revenue)}", + "products": "{event.properties.products ? arrayMap(product -> ({'id': product.product_id, 'category': product.category, 'name': product.name}), event.properties.products) : event.properties.product_id ? [{'id': event.properties.product_id, 'category': event.properties.category, 'name': event.properties.name}] : undefined}", + "value": "{toFloat(event.properties.value ?? event.properties.revenue ?? event.properties.price)}", "currency": "{event.properties.currency}", }, "secret": False, diff --git a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py index 6cbd0d3542b76..62ae01fe7ffaa 100644 --- a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py @@ -142,3 +142,28 @@ def test_event_not_in_spec(self): assert len(calls) == 1 # Only init call assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] + + def test_product_from_top_level_properties(self): + event_id, calls = self._process_event( + "Product Added", + { + "product_id": TEST_PRODUCT_ID, + "name": "Product Name", + "category": "Product Category", + "price": 42, + "currency": "USD", + }, + {"email": TEST_EMAIL}, + ) + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] + assert calls[1] == [ + "track", + "AddToCart", + { + "conversion_id": event_id, + "products": [{"id": TEST_PRODUCT_ID, "name": "Product Name", "category": "Product Category"}], + "currency": "USD", + "value": 42, + }, + ] From 493dc16a65ee7b6a12a0c5a221fe6ad2bb46ceb1 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Wed, 12 Feb 2025 09:27:52 +0000 Subject: [PATCH 15/19] Improve mapping names --- .../cdp/templates/reddit/template_reddit_pixel.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index 02366e59bf13b..321b24b2a68cc 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -135,9 +135,9 @@ ], ), HogFunctionMappingTemplate( - name="Add To Cart", + name="Product Added", include_by_default=True, - filters={"events": [{"id": "Product Added", "name": "Add To Cart", "type": "events"}]}, + filters={"events": [{"id": "Product Added", "name": "Product Added", "type": "events"}]}, inputs_schema=[ { "key": "eventType", @@ -151,9 +151,11 @@ ], ), HogFunctionMappingTemplate( - name="Add To Wishlist", + name="Product Added to Wishlist", include_by_default=True, - filters={"events": [{"id": "Product Added to Wishlist", "name": "Add To Wishlist", "type": "events"}]}, + filters={ + "events": [{"id": "Product Added to Wishlist", "name": "Product Added to Wishlist", "type": "events"}] + }, inputs_schema=[ { "key": "eventType", @@ -167,9 +169,9 @@ ], ), HogFunctionMappingTemplate( - name="Purchase", + name="Order Completed", include_by_default=True, - filters={"events": [{"id": "Order Completed", "name": "Purchase", "type": "events"}]}, + filters={"events": [{"id": "Order Completed", "name": "Order Completed", "type": "events"}]}, inputs_schema=[ { "key": "eventType", From 8ec07e1d48d92e39e57f2de498128ce0f1bf0dd8 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Wed, 12 Feb 2025 11:15:40 +0000 Subject: [PATCH 16/19] Use posthog --- posthog/cdp/templates/reddit/template_reddit_pixel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index 321b24b2a68cc..8870bb65da20e 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -32,7 +32,7 @@ !function(w,d){if(!w.rdt){var p=w.rdt=function(){p.sendEvent?p.sendEvent.apply(p,arguments):p.callQueue.push(arguments)};p.callQueue=[];var t=d.createElement("script");t.src="https://www.redditstatic.com/ads/pixel.js",t.async=!0;var s=d.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}}(window,document); } -export function onLoad({ inputs }) { +export function onLoad({ inputs, posthog }) { initSnippet(); let userProperties = {}; for (const [key, value] of Object.entries(inputs.userProperties)) { @@ -41,7 +41,7 @@ } }; if (posthog.config.debug) { - console.log('[PostHog.js] rdt init', inputs.pixelId, userProperties); + console.log('[PostHog] rdt init', inputs.pixelId, userProperties); } rdt('init', inputs.pixelId, userProperties); } @@ -70,7 +70,7 @@ eventProperties.customEventName = inputs.eventType; } if (posthog.config.debug) { - console.log('[PostHog.js] rdt track', eventName, eventProperties); + console.log('[PostHog] rdt track', eventName, eventProperties); } rdt('track', eventName, eventProperties); } From 1da603a7ce13b9eb1f612975ff6d9dd185041023 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Wed, 12 Feb 2025 11:19:44 +0000 Subject: [PATCH 17/19] Add reddit png --- frontend/public/services/reddit.png | Bin 0 -> 20227 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 frontend/public/services/reddit.png diff --git a/frontend/public/services/reddit.png b/frontend/public/services/reddit.png new file mode 100644 index 0000000000000000000000000000000000000000..1a716f7d7b2871ce9e690bb5eb079583b77d9297 GIT binary patch literal 20227 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 literal 0 HcmV?d00001 From 2e2f36fc563a0db0c0f68f001d8cda9f9f2ddfbe Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Wed, 12 Feb 2025 11:53:08 +0000 Subject: [PATCH 18/19] Make built-in event names clearer --- .../templates/reddit/template_reddit_pixel.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/posthog/cdp/templates/reddit/template_reddit_pixel.py b/posthog/cdp/templates/reddit/template_reddit_pixel.py index 8870bb65da20e..2bfbce47948eb 100644 --- a/posthog/cdp/templates/reddit/template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/template_reddit_pixel.py @@ -32,6 +32,20 @@ !function(w,d){if(!w.rdt){var p=w.rdt=function(){p.sendEvent?p.sendEvent.apply(p,arguments):p.callQueue.push(arguments)};p.callQueue=[];var t=d.createElement("script");t.src="https://www.redditstatic.com/ads/pixel.js",t.async=!0;var s=d.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}}(window,document); } +// These are the event names which we are allowed to call rdt with. If we want to send a different event name, we will +// need to use the 'Custom' event name, and pass original event name as 'customEventName' in event properties. +const RDT_ALLOWED_EVENT_NAMES = [ + 'PageVisit', + 'Search', + 'AddToCart', + 'AddToWishlist', + 'Purchase', + 'ViewContent', + 'Lead', + 'SignUp', + 'Custom', +]; + export function onLoad({ inputs, posthog }) { initSnippet(); let userProperties = {}; @@ -46,16 +60,7 @@ rdt('init', inputs.pixelId, userProperties); } export function onEvent({ inputs, posthog }) { - const builtInEvents = [ - 'PageVisit', - 'Search', - 'AddToCart', - 'AddToWishlist', - 'Purchase', - 'ViewContent', - 'Lead', - 'SignUp', - ]; + let eventProperties = {}; for (const [key, value] of Object.entries(inputs.eventProperties)) { if (value) { @@ -63,7 +68,7 @@ } }; let eventName; - if (builtInEvents.includes(inputs.eventType)) { + if (RDT_ALLOWED_EVENT_NAMES.includes(inputs.eventType)) { eventName = inputs.eventType; } else { eventName = 'Custom'; From 47277362ec84e8978640741e5130c54a66e26044 Mon Sep 17 00:00:00 2001 From: Robbie Coomber Date: Wed, 12 Feb 2025 12:37:41 +0000 Subject: [PATCH 19/19] Add test for custom event --- posthog/cdp/templates/helpers.py | 29 +++++---- .../reddit/test_template_reddit_pixel.py | 63 +++++++++++++++++++ 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/posthog/cdp/templates/helpers.py b/posthog/cdp/templates/helpers.py index 640eda37ee543..26d0742ba9983 100644 --- a/posthog/cdp/templates/helpers.py +++ b/posthog/cdp/templates/helpers.py @@ -1,17 +1,19 @@ +import functools import json +from collections.abc import Callable from typing import Any, Optional, cast from unittest.mock import MagicMock +import STPyV8 + +from common.hogvm.python.execute import execute_bytecode +from common.hogvm.python.stl import now from posthog.cdp.site_functions import get_transpiled_function from posthog.cdp.templates.hog_function_template import HogFunctionTemplate from posthog.cdp.validation import compile_hog from posthog.models import HogFunction from posthog.models.utils import uuid7 from posthog.test.base import BaseTest, APIBaseTest -from common.hogvm.python.execute import execute_bytecode -from common.hogvm.python.stl import now - -import STPyV8 class BaseHogFunctionTemplateTest(BaseTest): @@ -103,11 +105,9 @@ class BaseSiteDestinationFunctionTest(APIBaseTest): template: HogFunctionTemplate track_fn: str inputs: dict - _transpiled: str - - def setUp(self): - super().setUp() + @functools.lru_cache + def _get_transpiled(self, edit_payload: Optional[Callable[[dict], dict]] = None): # TODO do this without calling the API. There's a lot of logic in the endpoint which would need to be extracted payload = { "description": self.template.description, @@ -129,19 +129,26 @@ def setUp(self): "template_id": self.template.id, "type": self.template.type, } + if edit_payload: + payload = edit_payload(payload) response = self.client.post( f"/api/projects/{self.team.id}/hog_functions/", data=payload, ) + assert response.status_code in (200, 201) function_id = response.json()["id"] # load from the DB based on the created ID hog_function = HogFunction.objects.get(id=function_id) - self._transpiled = get_transpiled_function(hog_function) + return get_transpiled_function(hog_function) def _process_event( - self, event_name: str, event_properties: Optional[dict] = None, person_properties: Optional[dict] = None + self, + event_name: str, + event_properties: Optional[dict] = None, + person_properties: Optional[dict] = None, + edit_payload: Optional[Callable[[dict], dict]] = None, ): event_id = str(uuid7()) js_globals = { @@ -167,7 +174,7 @@ def _process_event( }} }}; - const initFn = {self._transpiled}().init; + const initFn = {self._get_transpiled(edit_payload)}().init; const processEvent = initFn({{ posthog, callback: console.log }}).processEvent; diff --git a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py index 62ae01fe7ffaa..1adc51ef81fa6 100644 --- a/posthog/cdp/templates/reddit/test_template_reddit_pixel.py +++ b/posthog/cdp/templates/reddit/test_template_reddit_pixel.py @@ -167,3 +167,66 @@ def test_product_from_top_level_properties(self): "value": 42, }, ] + + def test_custom_event(self): + def add_custom_mapping(payload): + payload["mappings"].append( + { + "filters": {"events": [{"id": "Event Not In Spec", "name": "Event Not In Spec", "type": "events"}]}, + "inputs": { + "eventType": {"value": "Mapped Event Not In Spec"}, + "eventProperties": { + "value": { + "conversion_id": "{event.uuid}", + "products": "{event.properties.products ? arrayMap(product -> ({'id': product.product_id, 'category': product.category, 'name': product.name}), event.properties.products) : event.properties.product_id ? [{'id': event.properties.product_id, 'category': event.properties.category, 'name': event.properties.name}] : undefined}", + "value": "{toFloat(event.properties.value ?? event.properties.revenue ?? event.properties.price)}", + "currency": "{event.properties.currency}", + } + }, + }, + "inputs_schema": [ + { + "key": "eventType", + "type": "string", + "label": "Event Type", + "description": "description", + "default": "Mapped Event Not In Spec", + "required": True, + }, + { + "key": "eventProperties", + "type": "dictionary", + "description": "description", + "label": "Event parameters", + "default": { + "conversion_id": "{event.uuid}", + "products": "{event.properties.products ? arrayMap(product -> ({'id': product.product_id, 'category': product.category, 'name': product.name}), event.properties.products) : event.properties.product_id ? [{'id': event.properties.product_id, 'category': event.properties.category, 'name': event.properties.name}] : undefined}", + "value": "{toFloat(event.properties.value ?? event.properties.revenue ?? event.properties.price)}", + "currency": "{event.properties.currency}", + }, + "secret": False, + "required": False, + }, + ], + "name": "Event Not In Spec", + }, + ) + return payload + + event_id, calls = self._process_event( + "Event Not In Spec", + {}, + {"email": TEST_EMAIL}, + edit_payload=add_custom_mapping, + ) + + assert len(calls) == 2 + assert calls[0] == ["init", TEST_PIXEL_ID, {"email": TEST_EMAIL}] + assert calls[1] == [ + "track", + "Custom", + { + "conversion_id": event_id, + "customEventName": "Mapped Event Not In Spec", + }, + ]