Skip to content

Commit

Permalink
chore: identify set once (#313)
Browse files Browse the repository at this point in the history
  • Loading branch information
marandaneto authored Nov 22, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 6412334 commit 450f6a6
Showing 11 changed files with 143 additions and 13 deletions.
13 changes: 10 additions & 3 deletions posthog-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -213,7 +213,7 @@ export abstract class PostHogCoreStateless {
): void {
this.wrap(() => {
// The properties passed to identifyStateless are event properties.
// To add person properties, pass in all person properties to the `$set` key.
// To add person properties, pass in all person properties to the `$set` and `$set_once` keys.

const payload = {
...this.buildPayload({
@@ -953,10 +953,17 @@ export abstract class PostHogCore extends PostHogCoreStateless {
this.groups(properties.$groups)
}

// promote $set and $set_once to top level
const userPropsOnce = properties?.$set_once
delete properties?.$set_once

// if no $set is provided we assume all properties are $set
const userProps = properties?.$set || properties

const allProperties = this.enrichProperties({
...properties,
$anon_distinct_id: this.getAnonymousId(),
$set: properties,
$set: userProps,
$set_once: userPropsOnce,
})

if (distinctId !== previousDistinctId) {
81 changes: 79 additions & 2 deletions posthog-core/test/posthog.identify.spec.ts
Original file line number Diff line number Diff line change
@@ -30,14 +30,91 @@ describe('PostHog Core', () => {
properties: {
$lib: 'posthog-core-tests',
$lib_version: '2.0.0-alpha',
foo: 'bar',
$anon_distinct_id: expect.any(String),
$session_id: expect.any(String),
$set: {
foo: 'bar',
},
},
timestamp: '2022-01-01T00:00:00.000Z',
timestamp: expect.any(String),
uuid: expect.any(String),
type: 'identify',
},
],
sent_at: expect.any(String),
})
})

it('should send an $identify with $set and $set_once event', async () => {
posthog.identify('id-1', {
$set: {
foo: 'bar',
},
$set_once: {
vip: true,
},
})
await waitForPromises()
expect(mocks.fetch).toHaveBeenCalledTimes(2)
expect(parseBody(mocks.fetch.mock.calls[0])).toEqual({
api_key: 'TEST_API_KEY',
batch: [
{
event: '$identify',
distinct_id: posthog.getDistinctId(),
library: 'posthog-core-tests',
library_version: '2.0.0-alpha',
properties: {
$lib: 'posthog-core-tests',
$lib_version: '2.0.0-alpha',
$anon_distinct_id: expect.any(String),
$session_id: expect.any(String),
$set: {
foo: 'bar',
},
$set_once: {
vip: true,
},
},
timestamp: expect.any(String),
uuid: expect.any(String),
type: 'identify',
},
],
sent_at: expect.any(String),
})
})

it('should send an $identify with $set_once event', async () => {
posthog.identify('id-1', {
foo: 'bar',
$set_once: {
vip: true,
},
})
await waitForPromises()
expect(mocks.fetch).toHaveBeenCalledTimes(2)
expect(parseBody(mocks.fetch.mock.calls[0])).toEqual({
api_key: 'TEST_API_KEY',
batch: [
{
event: '$identify',
distinct_id: posthog.getDistinctId(),
library: 'posthog-core-tests',
library_version: '2.0.0-alpha',
properties: {
$lib: 'posthog-core-tests',
$lib_version: '2.0.0-alpha',
$anon_distinct_id: expect.any(String),
$session_id: expect.any(String),
$set: {
foo: 'bar',
},
$set_once: {
vip: true,
},
},
timestamp: expect.any(String),
uuid: expect.any(String),
type: 'identify',
},
4 changes: 4 additions & 0 deletions posthog-node/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Next

# 4.2.3 - 2024-11-21

1. fix: identify method allows passing a $set_once object

# 4.2.2 - 2024-11-18

1. fix: Shutdown will now respect the timeout and forcefully return rather than returning after the next fetch.
2 changes: 1 addition & 1 deletion posthog-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "posthog-node",
"version": "4.2.2",
"version": "4.2.3",
"description": "PostHog Node.js integration",
"repository": {
"type": "git",
11 changes: 9 additions & 2 deletions posthog-node/src/posthog-node.ts
Original file line number Diff line number Diff line change
@@ -174,12 +174,19 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {

identify({ distinctId, properties, disableGeoip }: IdentifyMessage): void {
// Catch properties passed as $set and move them to the top level
const personProperties = properties?.$set || properties

// promote $set and $set_once to top level
const userPropsOnce = properties?.$set_once
delete properties?.$set_once

// if no $set is provided we assume all properties are $set
const userProps = properties?.$set || properties

super.identifyStateless(
distinctId,
{
$set: personProperties,
$set: userProps,
$set_once: userPropsOnce,
},
{ disableGeoip }
)
32 changes: 29 additions & 3 deletions posthog-node/test/posthog-node.spec.ts
Original file line number Diff line number Diff line change
@@ -154,9 +154,9 @@ describe('PostHog Node.js', () => {
])
})

it('should handle identify mistakenly using $set', async () => {
it('should handle identify using $set and $set_once', async () => {
expect(mockedFetch).toHaveBeenCalledTimes(0)
posthog.identify({ distinctId: '123', properties: { foo: 'bar', $set: { foo: 'other' } } })
posthog.identify({ distinctId: '123', properties: { $set: { foo: 'bar' }, $set_once: { vip: true } } })
jest.runOnlyPendingTimers()
await waitForPromises()
const batchEvents = getLastBatchEvents()
@@ -166,7 +166,33 @@ describe('PostHog Node.js', () => {
event: '$identify',
properties: {
$set: {
foo: 'other',
foo: 'bar',
},
$set_once: {
vip: true,
},
$geoip_disable: true,
},
},
])
})

it('should handle identify using $set_once', async () => {
expect(mockedFetch).toHaveBeenCalledTimes(0)
posthog.identify({ distinctId: '123', properties: { foo: 'bar', $set_once: { vip: true } } })
jest.runOnlyPendingTimers()
await waitForPromises()
const batchEvents = getLastBatchEvents()
expect(batchEvents).toMatchObject([
{
distinct_id: '123',
event: '$identify',
properties: {
$set: {
foo: 'bar',
},
$set_once: {
vip: true,
},
$geoip_disable: true,
},
4 changes: 4 additions & 0 deletions posthog-react-native/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Next

# 3.3.14 - 2024-11-21

1. fix: identify method allows passing a $set_once object

# 3.3.13 - 2024-11-19

1. fix: session replay respects the flushAt flag
2 changes: 1 addition & 1 deletion posthog-react-native/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "posthog-react-native",
"version": "3.3.13",
"version": "3.3.14",
"main": "lib/posthog-react-native/index.js",
"files": [
"lib/"
3 changes: 3 additions & 0 deletions posthog-web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Next

# 3.1.0 - 2024-11-21

## Changed

1. chore: default `captureMode` changed to `json`.
1. To keep using the `form` mode, just set the `captureMode` option to `form` when initializing the PostHog client.
2. fix: identify method allows passing a $set_once object

# 3.0.2 - 2024-06-15

2 changes: 2 additions & 0 deletions posthog-web/README.md
Original file line number Diff line number Diff line change
@@ -29,6 +29,8 @@ posthog.capture('my-event', { myProperty: 'foo' })

// Identify a user (e.g. on login)
posthog.identify('my-unique-user-id', { email: '[email protected]', name: 'Jane Doe' })
// ...or with Set Once additional properties
posthog.identify('my-unique-user-id', { $set: { email: '[email protected]', name: 'Jane Doe' }, $set_once: { vip: true } })

// Reset a user (e.g. on logout)
posthog.reset()
2 changes: 1 addition & 1 deletion posthog-web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "posthog-js-lite",
"version": "3.0.2",
"version": "3.1.0",
"main": "lib/index.cjs.js",
"module": "lib/index.esm.js",
"types": "lib/index.d.ts",

0 comments on commit 450f6a6

Please sign in to comment.