diff --git a/x-pack/plugins/cloud/public/fullstory.ts b/x-pack/plugins/cloud/public/fullstory.ts index 31e5ec128b9a..b118688f31ae 100644 --- a/x-pack/plugins/cloud/public/fullstory.ts +++ b/x-pack/plugins/cloud/public/fullstory.ts @@ -12,7 +12,7 @@ export interface FullStoryDeps { basePath: IBasePath; orgId: string; packageInfo: PackageInfo; - userIdPromise: Promise; + userId?: string; } interface FullStoryApi { @@ -24,7 +24,7 @@ export const initializeFullStory = async ({ basePath, orgId, packageInfo, - userIdPromise, + userId, }: FullStoryDeps) => { // @ts-expect-error window._fs_debug = false; @@ -73,28 +73,23 @@ export const initializeFullStory = async ({ /* eslint-enable */ // @ts-expect-error - const fullstory: FullStoryApi = window.FSKibana; + const fullStory: FullStoryApi = window.FSKibana; + + try { + // This needs to be called syncronously to be sure that we populate the user ID soon enough to make sessions merging + // across domains work + if (!userId) return; + // Do the hashing here to keep it at clear as possible in our source code that we do not send literal user IDs + const hashedId = sha256(userId.toString()); + fullStory.identify(hashedId); + } catch (e) { + // eslint-disable-next-line no-console + console.error(`[cloud.full_story] Could not call FS.identify due to error: ${e.toString()}`, e); + } // Record an event that Kibana was opened so we can easily search for sessions that use Kibana - // @ts-expect-error - window.FSKibana.event('Loaded Kibana', { + fullStory.event('Loaded Kibana', { + // `str` suffix is required, see docs: https://help.fullstory.com/hc/en-us/articles/360020623234 kibana_version_str: packageInfo.version, }); - - // Use a promise here so we don't have to wait to retrieve the user to start recording the session - userIdPromise - .then((userId) => { - if (!userId) return; - // Do the hashing here to keep it at clear as possible in our source code that we do not send literal user IDs - const hashedId = sha256(userId.toString()); - // @ts-expect-error - window.FSKibana.identify(hashedId); - }) - .catch((e) => { - // eslint-disable-next-line no-console - console.error( - `[cloud.full_story] Could not call FS.identify due to error: ${e.toString()}`, - e - ); - }); }; diff --git a/x-pack/plugins/cloud/public/plugin.test.ts b/x-pack/plugins/cloud/public/plugin.test.ts index af4d3c4c9005..264ae61c050e 100644 --- a/x-pack/plugins/cloud/public/plugin.test.ts +++ b/x-pack/plugins/cloud/public/plugin.test.ts @@ -63,16 +63,11 @@ describe('Cloud Plugin', () => { }); expect(initializeFullStoryMock).toHaveBeenCalled(); - const { - basePath, - orgId, - packageInfo, - userIdPromise, - } = initializeFullStoryMock.mock.calls[0][0]; + const { basePath, orgId, packageInfo, userId } = initializeFullStoryMock.mock.calls[0][0]; expect(basePath.prepend).toBeDefined(); expect(orgId).toEqual('foo'); expect(packageInfo).toEqual(initContext.env.packageInfo); - expect(await userIdPromise).toEqual('1234'); + expect(userId).toEqual('1234'); }); it('passes undefined user ID when security is not available', async () => { @@ -82,9 +77,9 @@ describe('Cloud Plugin', () => { }); expect(initializeFullStoryMock).toHaveBeenCalled(); - const { orgId, userIdPromise } = initializeFullStoryMock.mock.calls[0][0]; + const { orgId, userId } = initializeFullStoryMock.mock.calls[0][0]; expect(orgId).toEqual('foo'); - expect(await userIdPromise).toEqual(undefined); + expect(userId).toEqual(undefined); }); it('does not call initializeFullStory when enabled=false', async () => { diff --git a/x-pack/plugins/cloud/public/plugin.ts b/x-pack/plugins/cloud/public/plugin.ts index 68dece1bc5d3..98017d09ef80 100644 --- a/x-pack/plugins/cloud/public/plugin.ts +++ b/x-pack/plugins/cloud/public/plugin.ts @@ -166,16 +166,21 @@ export class CloudPlugin implements Plugin { } // Keep this import async so that we do not load any FullStory code into the browser when it is disabled. - const { initializeFullStory } = await import('./fullstory'); + const fullStoryChunkPromise = import('./fullstory'); const userIdPromise: Promise = security ? loadFullStoryUserId({ getCurrentUser: security.authc.getCurrentUser }) : Promise.resolve(undefined); + const [{ initializeFullStory }, userId] = await Promise.all([ + fullStoryChunkPromise, + userIdPromise, + ]); + initializeFullStory({ basePath, orgId, packageInfo: this.initializerContext.env.packageInfo, - userIdPromise, + userId, }); } }