Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT_POSTHOG #1960

Merged
merged 19 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5d8a3ff
added posthog features, updated posthog version in telemetry, updated…
jwalsh-bdt Jan 17, 2025
87d79e4
Removed pageview action and added support for page view, screen view …
jwalsh-bdt Jan 18, 2025
4674522
Merge branch 'baptisteArno:main' into FEAT_POSTHOG
jwalsh-bdt Jan 21, 2025
9c152b0
updated calls to posthog to use 'await'. Removed console.log statements
jwalsh-bdt Jan 21, 2025
de60f48
Merge branch 'main' into FEAT_POSTHOG
baptisteArno Jan 24, 2025
c6637be
improve auth UI/UX
baptisteArno Jan 24, 2025
a90fc05
update SVG logo design and dimensions
baptisteArno Jan 24, 2025
9f1ff12
Merge remote-tracking branch 'upstream/main' into FEAT_POSTHOG
jwalsh-bdt Jan 25, 2025
c3b68d2
Updated to add more tags for block, removed dropdown from Capture act…
jwalsh-bdt Jan 25, 2025
4b1a8dd
Merge remote-tracking branch 'upstream/main' into FEAT_POSTHOG
jwalsh-bdt Jan 26, 2025
7b7c791
Merge branch 'baptisteArno:main' into FEAT_POSTHOG
jwalsh-bdt Jan 27, 2025
7d30188
Merge remote-tracking branch 'upstream/main' into FEAT_POSTHOG
jwalsh-bdt Jan 28, 2025
42f28ba
update host default value
baptisteArno Jan 29, 2025
d34d6ad
add parseBlockNodeLabel to action definitions for dynamic labeling
baptisteArno Jan 29, 2025
2ee05db
simplify and improve Capture UX
baptisteArno Jan 29, 2025
fd382de
remove get flag and identify
baptisteArno Jan 29, 2025
0d11fcc
add personProperites field in capture and improve identifyGroup UX
baptisteArno Jan 29, 2025
a9dab33
refactor parseGroups to enforce non-empty keys and types
baptisteArno Jan 29, 2025
9596e66
update PostHog documentation and enhance event capture details
baptisteArno Jan 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions apps/docs/editor/blocks/integrations/posthog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: Posthog
---

With the Posthog block, you can send events to Posthog and trigger your Posthog workflows. It uses the Posthog Node.js library to send server side events to Posthog, so that it works on non web browser devices.

## How to find my `Project API Key` and `API Host`?

To find your `Project API Key`, you need to go to your Posthog Settings page and click on the `General` link. Then scroll down to the Web Snippet and copy the api key and host info from the snippet;

<Frame>
<img
src="/images/blocks/integrations/posthog/posthog-keys.png"
alt="Posthog Keys"
/>
</Frame>

## Identify User

This action allows you to identify a user in Posthog. It requires the `User ID` and can optionally take a set of `Properties` that will be added to the user profile.

<Frame>
<img
src="/images/blocks/integrations/posthog/identify.png"
alt="Identify User"
/>
</Frame>

## Identify Group

This action allows you to identify a group in Posthog. It requires the `Group Type` and `Group Key`, and can optionally take `User ID` and a set of `Properties` that will be added to the group profile.

<Frame>
<img
src="/images/blocks/integrations/posthog/group-identify.png"
alt="Identify Group"
/>
</Frame>

## Capture

This action allows you to capture a custom event, a page view event, a screen view event or a survey sent event in Posthog. If you select `Custom`, it requires the `Event Name` to be set. It can optionally take a `User ID` set of `Properties`, as well as Group information.

<Frame>
<img
src="/images/blocks/integrations/posthog/capture.png"
alt="Capture Event"
/>
</Frame>


## Get Flag

This action allows you to get a flag from Posthog. It requires the `Flag Name` and `User ID`. You can choose between `Boolean` and `Multivariate` flags. If the flag is enabled for the user, the action will retrieve the flag payload and store it in the `Flag Payload` variable that you configure.

<Frame>
<img
src="/images/blocks/integrations/posthog/flags.png"
alt="Get Flag"
/>
</Frame>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
"editor/blocks/integrations/anthropic",
"editor/blocks/integrations/dify-ai",
"editor/blocks/integrations/nocodb",
"editor/blocks/integrations/posthog",
"editor/blocks/integrations/segment",
"editor/blocks/integrations/zendesk"
]
Expand Down
Binary file modified bun.lockb
Binary file not shown.
17 changes: 17 additions & 0 deletions packages/forge/blocks/posthog/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@typebot.io/posthog-block",
"version": "1.0.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./schemas": "./src/schemas.ts"
},
"devDependencies": {
"@typebot.io/forge": "workspace:*",
"@typebot.io/tsconfig": "workspace:*"
},
"dependencies": {
"posthog-node": "4.3.2"
}
}
146 changes: 146 additions & 0 deletions packages/forge/blocks/posthog/src/actions/capture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { createAction, option } from "@typebot.io/forge";
import { auth } from "../auth";
import { createClient } from "../helpers/createClient";
import { createProperties } from "../helpers/createProperties";

interface EventPayload {
distinctId: string;
event: string;
properties: {};
groups?: {};
}

export const capture = createAction({
auth,
name: "Capture",
options: option.object({
type: option
.enum(["custom", "page view", "screen view", "survey sent"])
.layout({
label: "Event Type",
defaultValue: "custom",
}),
name: option.string.layout({
label: "Event Name",
placeholder: "Required for custom events...",
isRequired: false,
}),
userId: option.string.layout({
label: "User ID",
isRequired: false,
}),
anonymous: option.boolean.layout({
label: "Anonymous User",
isRequired: false,
defaultValue: false,
}),
properties: option
.array(
option.object({
key: option.string.layout({
label: "Key",
isRequired: true,
}),
value: option.string.layout({
label: "Value",
isRequired: true,
}),
}),
)
.layout({
itemLabel: "property",
accordion: "Event properties",
}),
groupKey: option.string.layout({
label: "Group Key",
isRequired: false,
accordion: "Group settings",
}),
groupType: option.string.layout({
label: "Group Type",
isRequired: false,
accordion: "Group settings",
}),
}),
run: {
server: async ({
credentials: { apiKey, host },
options: {
type,
name,
userId,
anonymous,
groupKey,
groupType,
properties,
},
}) => {
switch (type) {
case "page view":
name = "$pageview";
break;
case "screen view":
name = "$screen";
break;
case "survey sent":
name = "survey sent";
break;
}

console.log("name", name);
console.log("type", type);
console.log("userId", userId);
console.log("apiKey", apiKey);
console.log("host", host);

if (
name === undefined ||
name.length === 0 ||
type === undefined ||
type.length === 0 ||
!userId ||
userId.length === 0 ||
apiKey === undefined ||
host === undefined
)
return;

const posthog = createClient(apiKey, host);

let eventPayload: EventPayload = {
distinctId: userId,
event: name,
properties: {},
};

if (properties !== undefined && properties.length > 0) {
eventPayload.properties = createProperties(properties);
}

if (
groupType !== undefined &&
groupType.length > 0 &&
groupKey !== undefined &&
groupKey.length > 0
) {
eventPayload = {
...eventPayload,
groups: { [`${groupType}`]: groupKey },
};
}

if (anonymous) {
eventPayload.properties = {
...eventPayload.properties,
$process_person_profile: false,
};
}

console.log(JSON.stringify(eventPayload, null, 2));

posthog.capture(eventPayload);

await posthog.shutdown();
jwalsh-bdt marked this conversation as resolved.
Show resolved Hide resolved
},
},
});
114 changes: 114 additions & 0 deletions packages/forge/blocks/posthog/src/actions/getFlag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { createAction, option } from "@typebot.io/forge";
import { isDefined, isEmpty } from "@typebot.io/lib/utils";
import { auth } from "../auth";
import { createClient } from "../helpers/createClient";
import { createProperties } from "../helpers/createProperties";

export const getFlag = createAction({
auth,
name: "Get Flag",
options: option.object({
userId: option.string.layout({
label: "User ID",
isRequired: true,
}),
flagKey: option.string.layout({
label: "Flag Key",
isRequired: true,
}),
responseMapping: option
.saveResponseArray(["Flag Payload"] as const)
.layout({
accordion: "Save payload",
}),
multivariate: option.boolean.layout({
label: "Multivariate",
isRequired: false,
defaultValue: false,
}),
properties: option
.array(
option.object({
key: option.string.layout({
label: "Key",
isRequired: true,
}),
value: option.string.layout({
label: "Value",
isRequired: true,
}),
}),
)
.layout({
itemLabel: "property",
}),
}),
getSetVariableIds: ({ responseMapping }) =>
responseMapping?.map((res) => res.variableId).filter(isDefined) ?? [],
run: {
server: async ({
credentials: { apiKey, host },
options: { userId, flagKey, responseMapping, multivariate, properties },
variables,
}) => {
if (
!userId ||
userId.length === 0 ||
!flagKey ||
flagKey.length === 0 ||
apiKey === undefined ||
host === undefined
)
return;

const posthog = createClient(apiKey, host);

let isFeatureFlagEnabled = false;
let matchedFlagPayload = null;

if (!multivariate) {
isFeatureFlagEnabled =
(await posthog.isFeatureEnabled(flagKey, userId)) ?? false;
if (isFeatureFlagEnabled) {
matchedFlagPayload = await posthog.getFeatureFlagPayload(
flagKey,
userId,
isFeatureFlagEnabled,
);
}
jwalsh-bdt marked this conversation as resolved.
Show resolved Hide resolved
} else {
let enabledVariant = null;

if (properties) {
const personProperties = createProperties(properties);
enabledVariant = await posthog.getFeatureFlag(
flagKey,
userId,
personProperties,
);
} else {
enabledVariant =
(await posthog.getFeatureFlag(flagKey, userId)) ?? false;
}
matchedFlagPayload = await posthog.getFeatureFlagPayload(
flagKey,
userId,
enabledVariant,
);
}

if (responseMapping) {
responseMapping?.forEach((mapping) => {
if (!mapping.variableId) return;
if (!mapping.item || mapping.item === "Flag Payload") {
variables.set([
{ id: mapping.variableId, value: matchedFlagPayload },
]);
}
});
}

await posthog.shutdown();
jwalsh-bdt marked this conversation as resolved.
Show resolved Hide resolved
},
},
});
Loading