[Cloud] Fix sessions stitching across domains (#103964) (#104028)

Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2021-06-30 20:21:14 -04:00 committed by GitHub
parent 42effd0c2c
commit 7fff4d77b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 33 deletions

View file

@ -12,7 +12,7 @@ export interface FullStoryDeps {
basePath: IBasePath;
orgId: string;
packageInfo: PackageInfo;
userIdPromise: Promise<string | undefined>;
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
);
});
};

View file

@ -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 () => {

View file

@ -166,16 +166,21 @@ export class CloudPlugin implements Plugin<CloudSetup> {
}
// 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<string | undefined> = 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,
});
}
}