diff --git a/posthog-core/src/index.ts b/posthog-core/src/index.ts index 39ff6f12..bf4471e1 100644 --- a/posthog-core/src/index.ts +++ b/posthog-core/src/index.ts @@ -724,48 +724,52 @@ export abstract class PostHogCore extends PostHogCoreStateless { } protected setupBootstrap(options?: Partial): void { + const bootstrap = options?.bootstrap + if (!bootstrap) { + return + } + // bootstrap options are only set if no persisted values are found // this is to prevent overwriting existing values - if (options?.bootstrap?.distinctId) { - if (options?.bootstrap?.isIdentifiedId) { + if (bootstrap.distinctId) { + if (bootstrap.isIdentifiedId) { const distinctId = this.getPersistedProperty(PostHogPersistedProperty.DistinctId) if (!distinctId) { - this.setPersistedProperty(PostHogPersistedProperty.DistinctId, options.bootstrap.distinctId) + this.setPersistedProperty(PostHogPersistedProperty.DistinctId, bootstrap.distinctId) } } else { const anonymousId = this.getPersistedProperty(PostHogPersistedProperty.AnonymousId) if (!anonymousId) { - this.setPersistedProperty(PostHogPersistedProperty.AnonymousId, options.bootstrap.distinctId) + this.setPersistedProperty(PostHogPersistedProperty.AnonymousId, bootstrap.distinctId) } } } - if (options?.bootstrap?.featureFlags) { - const bootstrapFlags = Object.keys(options.bootstrap?.featureFlags || {}) - .filter((flag) => !!options.bootstrap?.featureFlags?.[flag]) + const bootstrapfeatureFlags = bootstrap.featureFlags + if (bootstrapfeatureFlags && Object.keys(bootstrapfeatureFlags).length) { + const bootstrapFlags = Object.keys(bootstrapfeatureFlags) + .filter((flag) => !!bootstrapfeatureFlags[flag]) .reduce( - (res: Record, key) => ( - (res[key] = options.bootstrap?.featureFlags?.[key] || false), res - ), + (res: Record, key) => ((res[key] = bootstrapfeatureFlags[key] || false), res), {} ) - if (bootstrapFlags.length) { + if (Object.keys(bootstrapFlags).length) { const currentFlags = this.getPersistedProperty(PostHogPersistedProperty.FeatureFlags) || {} const newFeatureFlags = { ...bootstrapFlags, ...currentFlags } this.setKnownFeatureFlags(newFeatureFlags) } - const bootstrapFlagPayloads = options?.bootstrap?.featureFlagPayloads - if (bootstrapFlagPayloads && bootstrapFlagPayloads?.length) { + const bootstrapFlagPayloads = bootstrap.featureFlagPayloads + if (bootstrapFlagPayloads && Object.keys(bootstrapFlagPayloads).length) { const currentFlagPayloads = this.getPersistedProperty( PostHogPersistedProperty.FeatureFlagPayloads ) || {} - const newFeatureFlagPayloads = { bootstrapFlagPayloads, ...currentFlagPayloads } + const newFeatureFlagPayloads = { ...bootstrapFlagPayloads, ...currentFlagPayloads } this.setKnownFeatureFlagPayloads(newFeatureFlagPayloads) } } diff --git a/posthog-core/test/posthog.featureflags.spec.ts b/posthog-core/test/posthog.featureflags.spec.ts index f8a15d22..194644c7 100644 --- a/posthog-core/test/posthog.featureflags.spec.ts +++ b/posthog-core/test/posthog.featureflags.spec.ts @@ -586,4 +586,68 @@ describe('PostHog Core', () => { }) }) }) + + describe('bootstapped do not overwrite values', () => { + beforeEach(() => { + ;[posthog, mocks] = createTestClient( + 'TEST_API_KEY', + { + flushAt: 1, + bootstrap: { + distinctId: 'tomato', + featureFlags: { 'bootstrap-1': 'variant-1', enabled: true, disabled: false }, + featureFlagPayloads: { + 'bootstrap-1': { + some: 'key', + }, + enabled: 200, + }, + }, + }, + (_mocks) => { + _mocks.fetch.mockImplementation((url) => { + if (url.includes('/decide/')) { + return Promise.resolve({ + status: 200, + text: () => Promise.resolve('ok'), + json: () => + Promise.resolve({ + featureFlags: createMockFeatureFlags(), + featureFlagPayloads: createMockFeatureFlagPayloads(), + }), + }) + } + + return Promise.resolve({ + status: 200, + text: () => Promise.resolve('ok'), + json: () => + Promise.resolve({ + status: 'ok', + }), + }) + }) + }, + { + distinct_id: '123', + feature_flags: { 'bootstrap-1': 'variant-2' }, + feature_flag_payloads: { 'bootstrap-1': { some: 'other-key' } }, + } + ) + }) + + it('distinct id should not be overwritten if already there', () => { + expect(posthog.getDistinctId()).toEqual('123') + }) + + it('flags should not be overwritten if already there', () => { + expect(posthog.getFeatureFlag('bootstrap-1')).toEqual('variant-2') + }) + + it('flag payloads should not be overwritten if already there', () => { + expect(posthog.getFeatureFlagPayload('bootstrap-1')).toEqual({ + some: 'other-key', + }) + }) + }) }) diff --git a/posthog-core/test/test-utils/PostHogCoreTestClient.ts b/posthog-core/test/test-utils/PostHogCoreTestClient.ts index 0a4451a6..5039e650 100644 --- a/posthog-core/test/test-utils/PostHogCoreTestClient.ts +++ b/posthog-core/test/test-utils/PostHogCoreTestClient.ts @@ -1,4 +1,4 @@ -import { PostHogCore, PostHogCoreOptions, PostHogFetchOptions, PostHogFetchResponse } from '../../src' +import { JsonType, PostHogCore, PostHogCoreOptions, PostHogFetchOptions, PostHogFetchResponse } from '../../src' const version = '2.0.0-alpha' @@ -42,9 +42,9 @@ export class PostHogCoreTestClient extends PostHogCore { export const createTestClient = ( apiKey: string, options?: PostHogCoreOptions, - setupMocks?: (mocks: PostHogCoreTestClientMocks) => void + setupMocks?: (mocks: PostHogCoreTestClientMocks) => void, + storageCache: { [key: string]: string | JsonType } = {} ): [PostHogCoreTestClient, PostHogCoreTestClientMocks] => { - const storageCache: { [key: string]: string | undefined } = {} const mocks = { fetch: jest.fn, [string, PostHogFetchOptions]>(), storage: {