posthog-node
Advanced tools
Comparing version 4.0.0 to 4.0.1
@@ -0,1 +1,6 @@ | ||
# 4.0.1 - 2024-04-25 | ||
1. Prevent double JSON parsing of feature flag payloads, which would convert the payload [1] into 1. | ||
# 4.0.0 - 2024-03-18 | ||
@@ -2,0 +7,0 @@ |
@@ -40,3 +40,3 @@ /// <reference types="node" /> | ||
sessionExpirationTimeSeconds?: number; | ||
/** Whether to post events to PostHog in JSON or compressed format */ | ||
/** Whether to post events to PostHog in JSON or compressed format. Defaults to 'form' */ | ||
captureMode?: 'json' | 'form'; | ||
@@ -43,0 +43,0 @@ disableGeoip?: boolean; |
@@ -40,3 +40,3 @@ /// <reference types="node" /> | ||
sessionExpirationTimeSeconds?: number; | ||
/** Whether to post events to PostHog in JSON or compressed format */ | ||
/** Whether to post events to PostHog in JSON or compressed format. Defaults to 'form' */ | ||
captureMode?: 'json' | 'form'; | ||
@@ -43,0 +43,0 @@ disableGeoip?: boolean; |
@@ -50,3 +50,3 @@ import { JsonType } from '../../posthog-core/src'; | ||
}; | ||
payloads?: Record<string, JsonType>; | ||
payloads?: Record<string, string>; | ||
}; | ||
@@ -53,0 +53,0 @@ deleted: boolean; |
{ | ||
"name": "posthog-node", | ||
"version": "4.0.0", | ||
"version": "4.0.1", | ||
"description": "PostHog Node.js integration", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -151,7 +151,11 @@ import { createHash } from 'rusha' | ||
// Undefined means a loading or missing data issue. Null means evaluation happened and there was no match | ||
if (response === undefined) { | ||
if (response === undefined || response === null) { | ||
return null | ||
} | ||
return response | ||
try { | ||
return JSON.parse(response) | ||
} catch { | ||
return response | ||
} | ||
} | ||
@@ -312,3 +316,3 @@ | ||
if (propertyType === 'cohort') { | ||
matches = matchCohort(prop, properties, this.cohorts) | ||
matches = matchCohort(prop, properties, this.cohorts, this.debugMode) | ||
} else { | ||
@@ -563,3 +567,4 @@ matches = matchProperty(prop, properties) | ||
propertyValues: Record<string, any>, | ||
cohortProperties: FeatureFlagsPoller['cohorts'] | ||
cohortProperties: FeatureFlagsPoller['cohorts'], | ||
debugMode: boolean = false | ||
): boolean { | ||
@@ -572,3 +577,3 @@ const cohortId = String(property.value) | ||
const propertyGroup = cohortProperties[cohortId] | ||
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties) | ||
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode) | ||
} | ||
@@ -579,3 +584,4 @@ | ||
propertyValues: Record<string, any>, | ||
cohortProperties: FeatureFlagsPoller['cohorts'] | ||
cohortProperties: FeatureFlagsPoller['cohorts'], | ||
debugMode: boolean = false | ||
): boolean { | ||
@@ -600,3 +606,3 @@ if (!propertyGroup) { | ||
try { | ||
const matches = matchPropertyGroup(prop, propertyValues, cohortProperties) | ||
const matches = matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode) | ||
if (propertyGroupType === 'AND') { | ||
@@ -614,3 +620,5 @@ if (!matches) { | ||
if (err instanceof InconclusiveMatchError) { | ||
console.debug(`Failed to compute property ${prop} locally: ${err}`) | ||
if (debugMode) { | ||
console.debug(`Failed to compute property ${prop} locally: ${err}`) | ||
} | ||
errorMatchingLocally = true | ||
@@ -633,3 +641,3 @@ } else { | ||
if (prop.type === 'cohort') { | ||
matches = matchCohort(prop, propertyValues, cohortProperties) | ||
matches = matchCohort(prop, propertyValues, cohortProperties, debugMode) | ||
} else { | ||
@@ -660,3 +668,5 @@ matches = matchProperty(prop, propertyValues) | ||
if (err instanceof InconclusiveMatchError) { | ||
console.debug(`Failed to compute property ${prop} locally: ${err}`) | ||
if (debugMode) { | ||
console.debug(`Failed to compute property ${prop} locally: ${err}`) | ||
} | ||
errorMatchingLocally = true | ||
@@ -663,0 +673,0 @@ } else { |
@@ -343,8 +343,3 @@ import { version } from '../package.json' | ||
} | ||
try { | ||
return JSON.parse(response as any) | ||
} catch { | ||
return response | ||
} | ||
return response | ||
} | ||
@@ -351,0 +346,0 @@ |
@@ -57,3 +57,3 @@ import { JsonType } from '../../posthog-core/src' | ||
} | ||
payloads?: Record<string, JsonType> | ||
payloads?: Record<string, string> | ||
} | ||
@@ -60,0 +60,0 @@ deleted: boolean |
@@ -469,7 +469,10 @@ import { PostHog as PostHog } from '../src/posthog-node' | ||
'disabled-flag': false, | ||
'feature-array': true, | ||
} | ||
// these are stringified in apiImplementation | ||
const mockFeatureFlagPayloads = { | ||
'feature-1': { color: 'blue' }, | ||
'feature-variant': 2, | ||
'feature-array': [1], | ||
} | ||
@@ -501,3 +504,3 @@ | ||
}, | ||
payloads: { 'first-variant': 'some-payload', 'third-variant': { a: 'json' } }, | ||
payloads: { 'first-variant': 'some-payload', 'third-variant': JSON.stringify({ a: 'json' }) }, | ||
}, | ||
@@ -525,3 +528,3 @@ } | ||
], | ||
payloads: { true: 300 }, | ||
payloads: { true: '300' }, | ||
}, | ||
@@ -542,6 +545,22 @@ } | ||
], | ||
payloads: { true: 300 }, | ||
payloads: { true: '300' }, | ||
}, | ||
} | ||
const arrayFlag = { | ||
id: 5, | ||
name: 'Beta Feature', | ||
key: 'feature-array', | ||
active: true, | ||
filters: { | ||
groups: [ | ||
{ | ||
properties: [], | ||
rollout_percentage: 100, | ||
}, | ||
], | ||
payloads: { true: JSON.stringify([1]) }, | ||
}, | ||
} | ||
mockedFetch.mockImplementation( | ||
@@ -551,3 +570,3 @@ apiImplementation({ | ||
decideFlagPayloads: mockFeatureFlagPayloads, | ||
localFlags: { flags: [multivariateFlag, basicFlag, falseFlag] }, | ||
localFlags: { flags: [multivariateFlag, basicFlag, falseFlag, arrayFlag] }, | ||
}) | ||
@@ -611,5 +630,6 @@ ) | ||
properties: expect.objectContaining({ | ||
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant'], | ||
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant', 'feature-array'], | ||
'$feature/feature-1': true, | ||
'$feature/feature-2': true, | ||
'$feature/feature-array': true, | ||
'$feature/feature-variant': 'variant', | ||
@@ -668,4 +688,5 @@ $lib: 'posthog-node', | ||
properties: expect.objectContaining({ | ||
$active_feature_flags: ['beta-feature-local'], | ||
$active_feature_flags: ['beta-feature-local', 'feature-array'], | ||
'$feature/beta-feature-local': 'third-variant', | ||
'$feature/feature-array': true, | ||
'$feature/false-flag': false, | ||
@@ -766,5 +787,6 @@ $lib: 'posthog-node', | ||
expect(getLastBatchEvents()?.[0].properties).toEqual({ | ||
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant'], | ||
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant', 'feature-array'], | ||
'$feature/feature-1': true, | ||
'$feature/feature-2': true, | ||
'$feature/feature-array': true, | ||
'$feature/disabled-flag': false, | ||
@@ -1019,2 +1041,42 @@ '$feature/feature-variant': 'variant', | ||
it('should not double parse json with getFeatureFlagPayloads and local eval', async () => { | ||
expect(mockedFetch).toHaveBeenCalledTimes(0) | ||
posthog = new PostHog('TEST_API_KEY', { | ||
host: 'http://example.com', | ||
flushAt: 1, | ||
fetchRetryCount: 0, | ||
personalApiKey: 'TEST_PERSONAL_API_KEY', | ||
}) | ||
mockedFetch.mockClear() | ||
expect(mockedFetch).toHaveBeenCalledTimes(0) | ||
await expect( | ||
posthog.getFeatureFlagPayload('feature-array', '123', true, { onlyEvaluateLocally: true }) | ||
).resolves.toEqual([1]) | ||
expect(mockedFetch).toHaveBeenCalledTimes(1) | ||
expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall) | ||
mockedFetch.mockClear() | ||
await expect(posthog.getFeatureFlagPayload('feature-array', '123')).resolves.toEqual([1]) | ||
expect(mockedFetch).toHaveBeenCalledTimes(0) | ||
await expect(posthog.getFeatureFlagPayload('false-flag', '123', true)).resolves.toEqual(300) | ||
expect(mockedFetch).toHaveBeenCalledTimes(0) | ||
}) | ||
it('should not double parse json with getFeatureFlagPayloads and server eval', async () => { | ||
expect(mockedFetch).toHaveBeenCalledTimes(0) | ||
await expect( | ||
posthog.getFeatureFlagPayload('feature-array', '123', undefined, { groups: { org: '123' } }) | ||
).resolves.toEqual([1]) | ||
expect(mockedFetch).toHaveBeenCalledTimes(1) | ||
expect(mockedFetch).toHaveBeenCalledWith( | ||
'http://example.com/decide/?v=3', | ||
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') }) | ||
) | ||
}) | ||
it('should do getFeatureFlagPayloads without matchValue', async () => { | ||
@@ -1021,0 +1083,0 @@ expect(mockedFetch).toHaveBeenCalledTimes(0) |
@@ -23,3 +23,5 @@ export const apiImplementation = ({ | ||
featureFlags: decideFlags, | ||
featureFlagPayloads: decideFlagPayloads, | ||
featureFlagPayloads: Object.fromEntries( | ||
Object.entries(decideFlagPayloads || {}).map(([k, v]) => [k, JSON.stringify(v)]) | ||
), | ||
}) | ||
@@ -26,0 +28,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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
879043
12899