From c4a87c7def46f4240fc8b005b3d5ce5909f18287 Mon Sep 17 00:00:00 2001 From: Danilo Campos Date: Thu, 17 Oct 2024 14:04:55 -0400 Subject: [PATCH] feat: Airtable CDP destination, creating records per-event (#25656) Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- frontend/public/services/airtable.png | Bin 0 -> 2783 bytes posthog/cdp/templates/__init__.py | 3 +- .../templates/airtable/template_airtable.py | 82 ++++++++++++++++++ .../airtable/test_template_airtable.py | 66 ++++++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 frontend/public/services/airtable.png create mode 100644 posthog/cdp/templates/airtable/template_airtable.py create mode 100644 posthog/cdp/templates/airtable/test_template_airtable.py diff --git a/frontend/public/services/airtable.png b/frontend/public/services/airtable.png new file mode 100644 index 0000000000000000000000000000000000000000..4d496006b1cc2c7e19f2598b8b96f706212fecb9 GIT binary patch literal 2783 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 l$S`WnI73BfcCY^nFaTDGE(^cs7y= 400) { + throw Error(f'Error from api.airtable.com (status {res.status}): {res.body}') +} +""".strip(), + inputs_schema=[ + { + "key": "access_token", + "type": "string", + "label": "Airtable access token", + "secret": True, + "required": True, + "description": "Create this at https://airtable.com/create/tokens", + }, + { + "key": "base_id", + "type": "string", + "label": "Airtable base ID", + "secret": False, + "required": True, + "description": "Find this at https://airtable.com/developers/web/api/introduction", + }, + { + "key": "table_name", + "type": "string", + "label": "Table name", + "secret": False, + "required": True, + }, + { + "key": "fields", + "type": "json", + "label": "Fields", + "default": {"Timestamp": "{event.timestamp}", "Person Name": "{person.name}"}, + "secret": False, + "required": True, + "description": "Map field names from Airtable to properties from events and person records.", + }, + { + "key": "debug", + "type": "boolean", + "label": "Log responses", + "description": "Logs the response of http calls for debugging.", + "secret": False, + "required": False, + "default": False, + }, + ], +) diff --git a/posthog/cdp/templates/airtable/test_template_airtable.py b/posthog/cdp/templates/airtable/test_template_airtable.py new file mode 100644 index 0000000000000..fb4549c7d3c35 --- /dev/null +++ b/posthog/cdp/templates/airtable/test_template_airtable.py @@ -0,0 +1,66 @@ +from inline_snapshot import snapshot +from posthog.cdp.templates.helpers import BaseHogFunctionTemplateTest +from posthog.cdp.templates.airtable.template_airtable import template as template_airtable + + +class TestTemplateAirtable(BaseHogFunctionTemplateTest): + template = template_airtable + + def test_function_works(self): + self.run_function( + inputs={ + "access_token": "test_token", + "base_id": "test_base_id", + "table_name": "test_table", + "fields": {"Name": "John Doe", "Email": "john@example.com"}, + "debug": False, + } + ) + + assert self.get_mock_fetch_calls()[0] == snapshot( + ( + "https://api.airtable.com/v0/test_base_id/test_table", + { + "headers": {"Content-Type": "application/json", "Authorization": "Bearer test_token"}, + "body": {"fields": {"Name": "John Doe", "Email": "john@example.com"}, "typecast": True}, + "method": "POST", + }, + ) + ) + assert self.get_mock_print_calls() == snapshot([]) + + def test_prints_when_debugging(self): + self.run_function( + inputs={ + "access_token": "test_token", + "base_id": "test_base_id", + "table_name": "test_table", + "fields": {"Name": "John Doe", "Email": "john@example.com"}, + "debug": True, + } + ) + + assert self.get_mock_fetch_calls()[0] == snapshot( + ( + "https://api.airtable.com/v0/test_base_id/test_table", + { + "headers": {"Content-Type": "application/json", "Authorization": "Bearer test_token"}, + "body": {"fields": {"Name": "John Doe", "Email": "john@example.com"}, "typecast": True}, + "method": "POST", + }, + ) + ) + assert self.get_mock_print_calls() == snapshot( + [ + ( + "Request", + "https://api.airtable.com/v0/test_base_id/test_table", + { + "headers": {"Content-Type": "application/json", "Authorization": "Bearer test_token"}, + "body": {"fields": {"Name": "John Doe", "Email": "john@example.com"}, "typecast": True}, + "method": "POST", + }, + ), + ("Response", 200, {}), + ] + )