Skip to content

Commit

Permalink
fix(cdp): webhook message migration (#25197)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra authored Sep 25, 2024
1 parent de87079 commit 77e93e8
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 32 deletions.
23 changes: 14 additions & 9 deletions plugin-server/src/cdp/hog-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,32 @@ export function execHog(bytecode: any, options?: ExecOptions): ExecResult {
})
}

export const formatInput = (bytecode: any, globals: HogFunctionInvocation['globals']): any => {
export const formatInput = (bytecode: any, globals: HogFunctionInvocation['globals'], key?: string): any => {
// Similar to how we generate the bytecode by iterating over the values,
// here we iterate over the object and replace the bytecode with the actual values
// bytecode is indicated as an array beginning with ["_H"] (versions 1+) or ["_h"] (version 0)

if (Array.isArray(bytecode) && (bytecode[0] === '_h' || bytecode[0] === '_H')) {
const res = execHog(bytecode, { globals })
if (!res.finished) {
// NOT ALLOWED
throw new Error('Input fields must be simple sync values')
}
if (res.error) {
throw res.error
}
if (!res.finished) {
// NOT ALLOWED
throw new Error(`Could not execute bytecode for input field: ${key}`)
}
return convertHogToJS(res.result)
}

if (Array.isArray(bytecode)) {
return bytecode.map((item) => formatInput(item, globals))
return bytecode.map((item) => formatInput(item, globals, key))
} else if (typeof bytecode === 'object') {
return Object.fromEntries(Object.entries(bytecode).map(([key, value]) => [key, formatInput(value, globals)]))
return Object.fromEntries(
Object.entries(bytecode).map(([key2, value]) => [
key2,
formatInput(value, globals, key ? `${key}.${key2}` : key2),
])
)
} else {
return bytecode
}
Expand Down Expand Up @@ -463,7 +468,7 @@ export class HogExecutor {

if (item.bytecode) {
// Use the bytecode to compile the field
builtInputs[key] = formatInput(item.bytecode, invocation.globals)
builtInputs[key] = formatInput(item.bytecode, invocation.globals, key)
}
})

Expand All @@ -472,7 +477,7 @@ export class HogExecutor {

if (item.bytecode) {
// Use the bytecode to compile the field
builtInputs[key] = formatInput(item.bytecode, invocation.globals)
builtInputs[key] = formatInput(item.bytecode, invocation.globals, key)
}
})

Expand Down
28 changes: 14 additions & 14 deletions posthog/management/commands/migrate_action_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@

# Maps to a string or a tuple of name and url
mappings: dict[str, str | list[str]] = {
"[event]": ["{event.name}", "{event.url}"],
"[event]": ["{event.event}", "{event.url}"],
"[event.link]": "{event.url}",
"[event.event]": "{event.name}",
"[event.event]": "{event.event}",
"[event.name]": "{event.event}",
"[event.uuid]": "{event.uuid}",
"[person]": ["{person.name}", "{person.url}"],
"[person.link]": "{person.url}",
"[person.uuid]": "{person.id}",
"[user]": ["{person.name}", "{person.url}"],
"[user.name]": "{person.name}",
"[user.pathname]": "{event.properties.$pathname}",
"[user.email]": "{person.properties.email}",
Expand Down Expand Up @@ -46,10 +49,10 @@ def convert_link(text: str, url: str, is_slack: bool) -> str:

def convert_slack_message_format_to_hog(action: Action, is_slack: bool) -> tuple[str, str]:
message_format = action.slack_message_format or "[action.name] triggered by [person]"
message_format = message_format.replace("{", "\\{")
matches = re.findall(r"(\[[^\]]+\])", message_format)
markdown = message_format
text = message_format

# Iterate over each match replacing it with the appropriate hog format
for match in matches:
content = match[1:-1]
Expand All @@ -60,8 +63,8 @@ def convert_slack_message_format_to_hog(action: Action, is_slack: bool) -> tuple
# For text we just replace it with the name
text = text.replace(match, mappings[match][0])
else:
markdown = markdown.replace(match, f"{{{content}}}")
text = text.replace(match, f"{{{content}}}")
markdown = markdown.replace(match, str(mappings[match]))
text = text.replace(match, str(mappings[match]))
elif match.startswith("[action."):
# Action data is no longer available as it is just a filter hence we need to replace it with static values
action_property = content.split(".")[1]
Expand All @@ -84,15 +87,17 @@ def convert_slack_message_format_to_hog(action: Action, is_slack: bool) -> tuple
text = text.replace(match, f"{{{content}}}")
elif match.startswith("[user."):
parts = content.split(".")
string = ".".join(["person", "properties", "$" + parts[1]])
for part in parts[2:]:
string += f".{part}"
string = ".".join(["person", "properties", "$" + parts[1], *parts[2:]])
markdown = markdown.replace(match, f"{{{string}}}")
text = text.replace(match, f"{{{string}}}")
elif match.startswith("[properties."):
parts = content.split(".")
string = ".".join(["event", *parts])
markdown = markdown.replace(match, f"{{{string}}}")
text = text.replace(match, f"{{{string}}}")
else:
markdown = markdown.replace(match, f"{{{content}}}")
text = text.replace(match, f"{{{content}}}")

print( # noqa: T201
f"[Action {action.id}] Converted message format:",
{
Expand All @@ -106,12 +111,9 @@ def convert_slack_message_format_to_hog(action: Action, is_slack: bool) -> tuple

def convert_to_hog_function(action: Action, inert=False) -> Optional[HogFunction]:
webhook_url = action.team.slack_incoming_webhook

if not webhook_url:
return None

message_markdown, message_text = convert_slack_message_format_to_hog(action, is_slack="slack" in webhook_url)

if "slack" in webhook_url:
body = {
"text": message_text,
Expand All @@ -121,12 +123,10 @@ def convert_to_hog_function(action: Action, inert=False) -> Optional[HogFunction
body = {
"text": message_markdown,
}

hog_code = inert_fetch_print if inert else webhook_template.hog
hog_name = f"Webhook for action {action.id} ({action.name})"
if inert:
hog_name = f"[CDP-TEST-HIDDEN] {hog_name}"

hog_function = HogFunction(
name=hog_name,
description="Automatically migrated from legacy action webhooks",
Expand Down
18 changes: 9 additions & 9 deletions posthog/management/commands/test/test_migrate_action_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ def test_migrates_message_format(self):
assert hog_function.inputs["method"]["value"] == "POST"
assert hog_function.inputs["body"]["value"] == snapshot(
{
"text": "{event.name} triggered by {person.name}",
"text": "{event.event} triggered by {person.name}",
"blocks": [
{
"text": {
"text": "<{event.url}|{event.name}> triggered by <{person.url}|{person.name}>",
"text": "<{event.url}|{event.event}> triggered by <{person.url}|{person.name}>",
"type": "mrkdwn",
},
"type": "section",
Expand All @@ -114,7 +114,7 @@ def test_migrates_message_format_not_slack(self):

assert hog_function.inputs["url"]["value"] == "https://webhooks.other.com/123"
assert hog_function.inputs["body"]["value"] == snapshot(
{"text": "[{event.name}]({event.url}) triggered by [{person.name}]({person.url})"}
{"text": "[{event.event}]({event.url}) triggered by [{person.name}]({person.url})"}
)

def test_migrates_advanced_message_format(self):
Expand All @@ -125,17 +125,17 @@ def test_migrates_advanced_message_format(self):

assert (
hog_function.inputs["body"]["value"]["text"]
== """Event: {event.name} {event.event} {event.link} {event.uuid}
Person: {person.name} {person.link} {person.properties.foo.bar}
== """Event: {event.event} {event.event} {event.url} {event.uuid}
Person: {person.name} {person.url} {person.properties.foo.bar}
Groups: {groups.organization.url} {groups.organization.properties.foo.bar}
Action: Test Action {project.url}/data-management/actions/1""".replace("1", str(self.action.id))
)

assert hog_function.inputs["body"]["value"]["blocks"] == [
{
"text": {
"text": """Event: <{event.url}|{event.name}> {event.event} {event.link} {event.uuid}
Person: <{person.url}|{person.name}> {person.link} {person.properties.foo.bar}
"text": """Event: <{event.url}|{event.event}> {event.event} {event.url} {event.uuid}
Person: <{person.url}|{person.name}> {person.url} {person.properties.foo.bar}
Groups: {groups.organization.url} {groups.organization.properties.foo.bar}
Action: <{project.url}/data-management/actions/1|Test Action> {project.url}/data-management/actions/1""".replace(
"1", str(self.action.id)
Expand All @@ -156,8 +156,8 @@ def test_migrates_advanced_message_format_not_slack(self):

assert hog_function.inputs["body"]["value"] == {
"text": """\
Event: [{event.name}]({event.url}) {event.event} {event.link} {event.uuid}
Person: [{person.name}]({person.url}) {person.link} {person.properties.foo.bar}
Event: [{event.event}]({event.url}) {event.event} {event.url} {event.uuid}
Person: [{person.name}]({person.url}) {person.url} {person.properties.foo.bar}
Groups: {groups.organization.url} {groups.organization.properties.foo.bar}
Action: [Test Action]({project.url}/data-management/actions/1) {project.url}/data-management/actions/1\
""".replace("1", str(self.action.id))
Expand Down

0 comments on commit 77e93e8

Please sign in to comment.