Skip to content

Commit 86164bb

Browse files
authored
fix: save existing anon tokens for multiple init calls (#562)
### Context In InstantSearch, in order for user tokens to be set in time for the first query on page load, anonymous user tokens are generated and set as both the InstantSearch token and Insights token. This flow has one issue though. When `useCookies` flag needs to be changed later (such as with a cookie consent banner), a second `init` call to insights with the flag set to true does not save the existing token to a cookie. A short repro is as follows with the very latest version of InstantSearch where this behaviour was added (PR: algolia/instantsearch#6377): ```js const searchClient = algoliasearch( // ... ); window.aa('init', { appId: 'id', apiKey: 'key', }); const search = instantsearch({ indexName: 'index', searchClient, insights: true, }); search.addWidgets([ // ... ]); // This does not save the cookie atm setTimeout(() => { window.aa('init', { partial: true, useCookie: true, }); }, 5000); ```
1 parent e9a4c10 commit 86164bb

File tree

4 files changed

+55
-2
lines changed

4 files changed

+55
-2
lines changed

lib/__tests__/init.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { getCookie, MONTH } from "../_tokenUtils";
22
import AlgoliaAnalytics from "../insights";
33
import * as utils from "../utils";
4+
import { createUUID } from "../utils/uuid";
45

56
jest.mock("../utils", () => ({
67
__esModule: true,
@@ -240,6 +241,23 @@ describe("init", () => {
240241

241242
setUserToken.mockRestore();
242243
});
244+
it("should save anonymous userToken as cookie when useCookie is set to true later", () => {
245+
analyticsInstance.init({
246+
apiKey: "***",
247+
appId: "XXX"
248+
});
249+
250+
analyticsInstance.setUserToken(`anonymous-${createUUID()}`);
251+
252+
analyticsInstance.init({
253+
partial: true,
254+
useCookie: true
255+
});
256+
257+
expect(document.cookie).toEqual(
258+
expect.stringMatching(/^_ALGOLIA=anonymous-/)
259+
);
260+
});
243261
it("should replace existing options when called again", () => {
244262
analyticsInstance.init({
245263
apiKey: "apiKey1",

lib/_tokenUtils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,24 @@ export const getCookie = (name: string): string => {
3131
return "";
3232
};
3333

34+
export function checkIfAnonymousToken(token: number | string): boolean {
35+
if (typeof token === "number") {
36+
return false;
37+
}
38+
39+
return token.indexOf("anonymous-") === 0;
40+
}
41+
42+
export function saveTokenAsCookie(this: AlgoliaAnalytics): void {
43+
const foundToken = getCookie(COOKIE_KEY);
44+
if (
45+
this._userToken &&
46+
(!foundToken || foundToken === "" || foundToken.indexOf("anonymous-") !== 0)
47+
) {
48+
setCookie(COOKIE_KEY, this._userToken, this._cookieDuration);
49+
}
50+
}
51+
3452
export function setAnonymousUserToken(
3553
this: AlgoliaAnalytics,
3654
inMemory = false

lib/init.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DEFAULT_ALGOLIA_AGENTS } from "./_algoliaAgent";
2-
import { MONTH } from "./_tokenUtils";
2+
import { checkIfAnonymousToken, MONTH } from "./_tokenUtils";
33
import type AlgoliaAnalytics from "./insights";
44
import { isUndefined, isNumber } from "./utils";
55

@@ -80,6 +80,8 @@ You can visit https://algolia.com/events/debugger instead.`);
8080
this.setUserToken(options.userToken);
8181
} else if (!this._userToken && !this._userHasOptedOut && this._useCookie) {
8282
this.setAnonymousUserToken();
83+
} else if (checkIfTokenNeedsToBeSaved(this)) {
84+
this.saveTokenAsCookie();
8385
}
8486
}
8587

@@ -110,3 +112,15 @@ function setOptions(
110112
)
111113
);
112114
}
115+
116+
function checkIfTokenNeedsToBeSaved(target: AlgoliaAnalytics): boolean {
117+
if (target._userToken === undefined) {
118+
return false;
119+
}
120+
121+
return (
122+
checkIfAnonymousToken(target._userToken) &&
123+
target._useCookie &&
124+
!target._userHasOptedOut
125+
);
126+
}

lib/insights.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
MONTH,
1212
setAuthenticatedUserToken,
1313
onAuthenticatedUserTokenChange,
14-
getAuthenticatedUserToken
14+
getAuthenticatedUserToken,
15+
saveTokenAsCookie
1516
} from "./_tokenUtils";
1617
import {
1718
clickedObjectIDsAfterSearch,
@@ -78,6 +79,7 @@ class AlgoliaAnalytics {
7879
getVersion: typeof getVersion;
7980
addAlgoliaAgent: typeof addAlgoliaAgent;
8081

82+
saveTokenAsCookie: typeof saveTokenAsCookie;
8183
setUserToken: typeof setUserToken;
8284
setAnonymousUserToken: typeof setAnonymousUserToken;
8385
getUserToken: typeof getUserToken;
@@ -110,6 +112,7 @@ class AlgoliaAnalytics {
110112

111113
this.addAlgoliaAgent = addAlgoliaAgent.bind(this);
112114

115+
this.saveTokenAsCookie = saveTokenAsCookie.bind(this);
113116
this.setUserToken = setUserToken.bind(this);
114117
this.setAnonymousUserToken = setAnonymousUserToken.bind(this);
115118
this.getUserToken = getUserToken.bind(this);

0 commit comments

Comments
 (0)