posthog-node
Advanced tools
Comparing version 4.3.0 to 4.3.1
# Next | ||
# 4.3.1 - 2024-11-26 | ||
1. Fix bug where this SDK incorrectly sent `$feature_flag_called` events with null values when using `getFeatureFlagPayload`. | ||
# 4.3.0 - 2024-11-25 | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "posthog-node", | ||
"version": "4.3.0", | ||
"version": "4.3.1", | ||
"description": "PostHog Node.js integration", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -297,6 +297,5 @@ import { version } from '../package.json' | ||
): Promise<JsonType | undefined> { | ||
const { groups, disableGeoip } = options || {} | ||
let { onlyEvaluateLocally, sendFeatureFlagEvents, personProperties, groupProperties } = options || {} | ||
const { groups, disableGeoip, onlyEvaluateLocally = false, personProperties, groupProperties } = options || {} | ||
const adjustedProperties = this.addLocalPersonAndGroupProperties( | ||
const { allPersonProperties, allGroupProperties } = this.addLocalPersonAndGroupProperties( | ||
distinctId, | ||
@@ -308,45 +307,67 @@ groups, | ||
personProperties = adjustedProperties.allPersonProperties | ||
groupProperties = adjustedProperties.allGroupProperties | ||
let response = undefined | ||
// Try to get match value locally if not provided | ||
if (!matchValue) { | ||
if (matchValue === undefined) { | ||
matchValue = await this.getFeatureFlag(key, distinctId, { | ||
...options, | ||
onlyEvaluateLocally: true, | ||
sendFeatureFlagEvents: false, | ||
}) | ||
} | ||
let response: string | boolean | undefined | ||
let payload: JsonType | undefined | ||
if (matchValue) { | ||
response = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue) | ||
response = matchValue | ||
payload = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue) | ||
} else { | ||
response = undefined | ||
payload = undefined | ||
} | ||
// set defaults | ||
if (onlyEvaluateLocally == undefined) { | ||
onlyEvaluateLocally = false | ||
} | ||
if (sendFeatureFlagEvents == undefined) { | ||
sendFeatureFlagEvents = true | ||
} | ||
// Determine if the payload was evaluated locally | ||
const payloadWasLocallyEvaluated = payload !== undefined | ||
// set defaults | ||
if (onlyEvaluateLocally == undefined) { | ||
onlyEvaluateLocally = false | ||
} | ||
// Fetch final flags and payloads either locally or from the remote server | ||
let fetchedOrLocalFlags: Record<string, string | boolean> | undefined | ||
let fetchedOrLocalPayloads: Record<string, JsonType | undefined> | undefined | ||
const payloadWasLocallyEvaluated = response !== undefined | ||
if (!payloadWasLocallyEvaluated && !onlyEvaluateLocally) { | ||
response = await super.getFeatureFlagPayloadStateless( | ||
key, | ||
if (payloadWasLocallyEvaluated || onlyEvaluateLocally) { | ||
if (response !== undefined) { | ||
fetchedOrLocalFlags = { [key]: response } | ||
fetchedOrLocalPayloads = { [key]: payload } | ||
} else { | ||
fetchedOrLocalFlags = {} | ||
fetchedOrLocalPayloads = {} | ||
} | ||
} else { | ||
const fetchedData = await super.getFeatureFlagsAndPayloadsStateless( | ||
distinctId, | ||
groups, | ||
personProperties, | ||
groupProperties, | ||
allPersonProperties, | ||
allGroupProperties, | ||
disableGeoip | ||
) | ||
fetchedOrLocalFlags = fetchedData.flags || {} | ||
fetchedOrLocalPayloads = fetchedData.payloads || {} | ||
} | ||
return response | ||
const finalResponse = fetchedOrLocalFlags[key] | ||
const finalPayload = fetchedOrLocalPayloads[key] | ||
const finalLocallyEvaluated = payloadWasLocallyEvaluated | ||
this.capture({ | ||
distinctId, | ||
event: '$feature_flag_called', | ||
properties: { | ||
$feature_flag: key, | ||
$feature_flag_response: finalResponse, | ||
$feature_flag_payload: finalPayload, | ||
locally_evaluated: finalLocallyEvaluated, | ||
[`$feature/${key}`]: finalResponse, | ||
}, | ||
groups, | ||
disableGeoip, | ||
}) | ||
return finalPayload | ||
} | ||
@@ -353,0 +374,0 @@ |
@@ -900,2 +900,3 @@ import { PostHog as PostHog } from '../src/posthog-node' | ||
], | ||
payloads: { true: { variant: 'A' } }, | ||
}, | ||
@@ -907,3 +908,7 @@ }, | ||
mockedFetch.mockImplementation( | ||
apiImplementation({ localFlags: flags, decideFlags: { 'decide-flag': 'decide-value' } }) | ||
apiImplementation({ | ||
localFlags: flags, | ||
decideFlags: { 'decide-flag': 'decide-value' }, | ||
decideFlagPayloads: { 'beta-feature': { variant: 'A' } }, | ||
}) | ||
) | ||
@@ -950,2 +955,30 @@ | ||
expect( | ||
await posthog.getFeatureFlagPayload('beta-feature', 'some-distinct-id', undefined, { | ||
personProperties: { region: 'USA', name: 'Aloha' }, | ||
}) | ||
).toEqual({ variant: 'A' }) | ||
// TRICKY: There's now an extra step before events are queued, so need to wait for that to resolve | ||
jest.runOnlyPendingTimers() | ||
await waitForPromises() | ||
await posthog.flush() | ||
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object)) | ||
expect(getLastBatchEvents()?.[0]).toEqual( | ||
expect.objectContaining({ | ||
distinct_id: 'some-distinct-id', | ||
event: '$feature_flag_called', | ||
properties: expect.objectContaining({ | ||
$feature_flag: 'beta-feature', | ||
$feature_flag_response: true, | ||
$feature_flag_payload: { variant: 'A' }, | ||
locally_evaluated: true, | ||
[`$feature/${'beta-feature'}`]: true, | ||
}), | ||
}) | ||
) | ||
mockedFetch.mockClear() | ||
// # called again for same user, shouldn't call capture again | ||
@@ -1087,3 +1120,3 @@ expect( | ||
await expect(posthog.getFeatureFlagPayload('false-flag', '123', true)).resolves.toEqual(300) | ||
expect(mockedFetch).toHaveBeenCalledTimes(0) | ||
expect(mockedFetch).toHaveBeenCalledTimes(1) // this now calls the server, because in this case the flag is not locally evaluated but we have a payload that we need to calculate | ||
}) | ||
@@ -1090,0 +1123,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
920101
13445