Skip to content

Commit bd398ee

Browse files
dhayabsarahdayan
andauthored
feat(insights): support authenticatedUserToken (#1233)
--------- Co-authored-by: Sarah Dayan <[email protected]>
1 parent e741fe1 commit bd398ee

File tree

6 files changed

+215
-56
lines changed

6 files changed

+215
-56
lines changed

packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe('createAutocomplete', () => {
137137
insights: { insightsClient },
138138
});
139139

140-
expect(insightsClient).toHaveBeenCalledTimes(3);
140+
expect(insightsClient).toHaveBeenCalledTimes(5);
141141
expect(insightsClient).toHaveBeenCalledWith(
142142
'addAlgoliaAgent',
143143
'insights-plugin'
@@ -168,7 +168,7 @@ describe('createAutocomplete', () => {
168168
});
169169

170170
expect(defaultInsightsClient).toHaveBeenCalledTimes(0);
171-
expect(userInsightsClient).toHaveBeenCalledTimes(3);
171+
expect(userInsightsClient).toHaveBeenCalledTimes(5);
172172
expect(userInsightsClient).toHaveBeenCalledWith(
173173
'addAlgoliaAgent',
174174
'insights-plugin'

packages/autocomplete-js/src/__tests__/autocomplete.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,7 @@ See: https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocom
724724
algoliaInsightsPlugin: expect.objectContaining({
725725
insights: expect.objectContaining({
726726
init: expect.any(Function),
727+
setAuthenticatedUserToken: expect.any(Function),
727728
setUserToken: expect.any(Function),
728729
clickedObjectIDsAfterSearch: expect.any(Function),
729730
clickedObjectIDs: expect.any(Function),
@@ -751,16 +752,16 @@ See: https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocom
751752
insights: { insightsClient: defaultInsightsClient },
752753
});
753754

754-
expect(defaultInsightsClient).toHaveBeenCalledTimes(3);
755+
expect(defaultInsightsClient).toHaveBeenCalledTimes(5);
755756
expect(userInsightsClient).toHaveBeenCalledTimes(0);
756757

757758
const insightsPlugin = createAlgoliaInsightsPlugin({
758759
insightsClient: userInsightsClient,
759760
});
760761
update({ plugins: [insightsPlugin] });
761762

762-
expect(defaultInsightsClient).toHaveBeenCalledTimes(3);
763-
expect(userInsightsClient).toHaveBeenCalledTimes(3);
763+
expect(defaultInsightsClient).toHaveBeenCalledTimes(5);
764+
expect(userInsightsClient).toHaveBeenCalledTimes(5);
764765
});
765766
});
766767
});

packages/autocomplete-plugin-algolia-insights/src/__tests__/createAlgoliaInsightsPlugin.test.ts

Lines changed: 169 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ describe('createAlgoliaInsightsPlugin', () => {
6666
algoliaInsightsPlugin: expect.objectContaining({
6767
insights: expect.objectContaining({
6868
init: expect.any(Function),
69+
setAuthenticatedUserToken: expect.any(Function),
6970
setUserToken: expect.any(Function),
7071
clickedObjectIDsAfterSearch: expect.any(Function),
7172
clickedObjectIDs: expect.any(Function),
@@ -91,7 +92,7 @@ describe('createAlgoliaInsightsPlugin', () => {
9192

9293
createPlayground(createAutocomplete, { plugins: [insightsPlugin] });
9394

94-
expect(insightsClient).toHaveBeenCalledTimes(3);
95+
expect(insightsClient).toHaveBeenCalledTimes(5);
9596
expect(insightsClient).toHaveBeenCalledWith(
9697
'addAlgoliaAgent',
9798
'insights-plugin'
@@ -180,54 +181,6 @@ describe('createAlgoliaInsightsPlugin', () => {
180181
]);
181182
});
182183

183-
test('forwards `userToken` from Search Insights to Algolia API requests', async () => {
184-
const insightsPlugin = createAlgoliaInsightsPlugin({ insightsClient });
185-
186-
const searchClient = createSearchClient({
187-
search: jest.fn(() =>
188-
Promise.resolve(
189-
createMultiSearchResponse({
190-
hits: [{ objectID: '1' }],
191-
})
192-
)
193-
),
194-
});
195-
196-
insightsClient('setUserToken', 'customUserToken');
197-
198-
const playground = createPlayground(createAutocomplete, {
199-
plugins: [insightsPlugin],
200-
getSources({ query }) {
201-
return [
202-
{
203-
sourceId: 'hits',
204-
getItems() {
205-
return getAlgoliaResults({
206-
searchClient,
207-
queries: [{ indexName: 'indexName', query }],
208-
});
209-
},
210-
templates: {
211-
item({ item }) {
212-
return item.objectID;
213-
},
214-
},
215-
},
216-
];
217-
},
218-
});
219-
220-
userEvent.type(playground.inputElement, 'a');
221-
await runAllMicroTasks();
222-
223-
expect(searchClient.search).toHaveBeenCalledTimes(1);
224-
expect(searchClient.search).toHaveBeenCalledWith([
225-
expect.objectContaining({
226-
params: expect.objectContaining({ userToken: 'customUserToken' }),
227-
}),
228-
]);
229-
});
230-
231184
test('does not call `init` if `insightsInitParams` not passed', () => {
232185
const insightsClient = jest.fn();
233186
createAlgoliaInsightsPlugin({
@@ -250,6 +203,173 @@ describe('createAlgoliaInsightsPlugin', () => {
250203
});
251204
});
252205

206+
describe('user token', () => {
207+
afterEach(() => {
208+
insightsClient('setAuthenticatedUserToken', undefined);
209+
});
210+
211+
test('forwards `userToken` from Search Insights to Algolia API requests', async () => {
212+
const insightsPlugin = createAlgoliaInsightsPlugin({ insightsClient });
213+
214+
const searchClient = createSearchClient({
215+
search: jest.fn(() =>
216+
Promise.resolve(
217+
createMultiSearchResponse({
218+
hits: [{ objectID: '1' }],
219+
})
220+
)
221+
),
222+
});
223+
224+
insightsClient('setUserToken', 'customUserToken');
225+
226+
const playground = createPlayground(createAutocomplete, {
227+
plugins: [insightsPlugin],
228+
getSources({ query }) {
229+
return [
230+
{
231+
sourceId: 'hits',
232+
getItems() {
233+
return getAlgoliaResults({
234+
searchClient,
235+
queries: [{ indexName: 'indexName', query }],
236+
});
237+
},
238+
templates: {
239+
item({ item }) {
240+
return item.objectID;
241+
},
242+
},
243+
},
244+
];
245+
},
246+
});
247+
248+
userEvent.type(playground.inputElement, 'a');
249+
await runAllMicroTasks();
250+
251+
expect(searchClient.search).toHaveBeenCalledTimes(1);
252+
expect(searchClient.search).toHaveBeenCalledWith([
253+
expect.objectContaining({
254+
params: expect.objectContaining({ userToken: 'customUserToken' }),
255+
}),
256+
]);
257+
});
258+
259+
test('forwards `authenticatedUserToken` from Search Insights to Algolia API requests', async () => {
260+
const insightsPlugin = createAlgoliaInsightsPlugin({ insightsClient });
261+
262+
const searchClient = createSearchClient({
263+
search: jest.fn(() =>
264+
Promise.resolve(
265+
createMultiSearchResponse({
266+
hits: [{ objectID: '1' }],
267+
})
268+
)
269+
),
270+
});
271+
272+
insightsClient('setAuthenticatedUserToken', 'customAuthUserToken');
273+
274+
const playground = createPlayground(createAutocomplete, {
275+
plugins: [insightsPlugin],
276+
getSources({ query }) {
277+
return [
278+
{
279+
sourceId: 'hits',
280+
getItems() {
281+
return getAlgoliaResults({
282+
searchClient,
283+
queries: [{ indexName: 'indexName', query }],
284+
});
285+
},
286+
templates: {
287+
item({ item }) {
288+
return item.objectID;
289+
},
290+
},
291+
},
292+
];
293+
},
294+
});
295+
296+
userEvent.type(playground.inputElement, 'a');
297+
await runAllMicroTasks();
298+
299+
expect(searchClient.search).toHaveBeenCalledTimes(1);
300+
expect(searchClient.search).toHaveBeenCalledWith([
301+
expect.objectContaining({
302+
params: expect.objectContaining({ userToken: 'customAuthUserToken' }),
303+
}),
304+
]);
305+
});
306+
307+
test('uses `authenticatedUserToken` in priority over `userToken`', async () => {
308+
const insightsPlugin = createAlgoliaInsightsPlugin({
309+
insightsClient,
310+
insightsInitParams: {
311+
userToken: 'customUserToken',
312+
},
313+
});
314+
315+
const searchClient = createSearchClient({
316+
search: jest.fn(() =>
317+
Promise.resolve(
318+
createMultiSearchResponse({
319+
hits: [{ objectID: '1' }],
320+
})
321+
)
322+
),
323+
});
324+
325+
insightsClient('setAuthenticatedUserToken', 'customAuthUserToken');
326+
327+
const playground = createPlayground(createAutocomplete, {
328+
plugins: [insightsPlugin],
329+
getSources({ query }) {
330+
return [
331+
{
332+
sourceId: 'hits',
333+
getItems() {
334+
return getAlgoliaResults({
335+
searchClient,
336+
queries: [{ indexName: 'indexName', query }],
337+
});
338+
},
339+
templates: {
340+
item({ item }) {
341+
return item.objectID;
342+
},
343+
},
344+
},
345+
];
346+
},
347+
});
348+
349+
userEvent.type(playground.inputElement, 'a');
350+
await runAllMicroTasks();
351+
352+
expect(searchClient.search).toHaveBeenCalledTimes(1);
353+
expect(searchClient.search).toHaveBeenCalledWith([
354+
expect.objectContaining({
355+
params: expect.objectContaining({ userToken: 'customAuthUserToken' }),
356+
}),
357+
]);
358+
359+
insightsClient('setAuthenticatedUserToken', undefined);
360+
361+
userEvent.type(playground.inputElement, 'b');
362+
await runAllMicroTasks();
363+
364+
expect(searchClient.search).toHaveBeenCalledTimes(2);
365+
expect(searchClient.search).toHaveBeenLastCalledWith([
366+
expect.objectContaining({
367+
params: expect.objectContaining({ userToken: 'customUserToken' }),
368+
}),
369+
]);
370+
});
371+
});
372+
253373
describe('automatic pulling', () => {
254374
const consoleError = jest
255375
.spyOn(console, 'error')

packages/autocomplete-plugin-algolia-insights/src/createAlgoliaInsightsPlugin.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
AlgoliaInsightsHit,
2121
AutocompleteInsightsApi,
2222
InsightsClient,
23+
InsightsEvent,
2324
InsightsMethodMap,
2425
OnActiveParams,
2526
OnItemsChangeParams,
@@ -182,7 +183,7 @@ export function createAlgoliaInsightsPlugin(
182183
return {
183184
name: 'aa.algoliaInsightsPlugin',
184185
subscribe({ setContext, onSelect, onActive }) {
185-
function setInsightsContext(userToken?: string | number) {
186+
function setInsightsContext(userToken?: InsightsEvent['userToken']) {
186187
setContext({
187188
algoliaInsightsPlugin: {
188189
__algoliaSearchParameters: {
@@ -201,11 +202,36 @@ export function createAlgoliaInsightsPlugin(
201202
insightsClient('addAlgoliaAgent', 'insights-plugin');
202203

203204
setInsightsContext();
205+
206+
// Handles user token changes
204207
insightsClient('onUserTokenChange', setInsightsContext);
205208
insightsClient('getUserToken', null, (_error, userToken) => {
206209
setInsightsContext(userToken);
207210
});
208211

212+
// Handles authenticated user token changes
213+
insightsClient(
214+
'onAuthenticatedUserTokenChange',
215+
(authenticatedUserToken) => {
216+
if (authenticatedUserToken) {
217+
setInsightsContext(authenticatedUserToken);
218+
} else {
219+
insightsClient('getUserToken', null, (_error, userToken) =>
220+
setInsightsContext(userToken)
221+
);
222+
}
223+
}
224+
);
225+
insightsClient(
226+
'getAuthenticatedUserToken',
227+
null,
228+
(_error, authenticatedUserToken) => {
229+
if (authenticatedUserToken) {
230+
setInsightsContext(authenticatedUserToken);
231+
}
232+
}
233+
);
234+
209235
onSelect(({ item, state, event, source }) => {
210236
if (!isAlgoliaInsightsHit(item)) {
211237
return;
@@ -323,6 +349,8 @@ function loadInsights(environment: typeof window) {
323349
* While `search-insights` supports both string and number user tokens,
324350
* the Search API only accepts strings. This function normalizes the user token.
325351
*/
326-
function normalizeUserToken(userToken: string | number): string {
352+
function normalizeUserToken(
353+
userToken: NonNullable<InsightsEvent['userToken']>
354+
): string {
327355
return typeof userToken === 'number' ? userToken.toString() : userToken;
328356
}

packages/autocomplete-plugin-algolia-insights/src/createSearchInsightsApi.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ export function createSearchInsightsApi(searchInsights: InsightsClient) {
7171
init(appId: string, apiKey: string) {
7272
searchInsights('init', { appId, apiKey });
7373
},
74+
/**
75+
* Sets the authenticated user token to attach to events.
76+
* Unsets the authenticated token by passing `undefined`.
77+
*
78+
* @link https://www.algolia.com/doc/api-reference/api-methods/set-authenticated-user-token/
79+
*/
80+
setAuthenticatedUserToken(authenticatedUserToken: string | undefined) {
81+
searchInsights('setAuthenticatedUserToken', authenticatedUserToken);
82+
},
7483
/**
7584
* Sets the user token to attach to events.
7685
*/

packages/autocomplete-plugin-algolia-insights/src/types/InsightsClient.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type {
99
SetUserToken as InsightsSetUserToken,
1010
GetUserToken as InsightsGetUserToken,
1111
OnUserTokenChange as InsightsOnUserTokenChange,
12+
InsightsEvent,
1213
} from 'search-insights';
1314

1415
export type InsightsMethodMap = _InsightsMethodMap;

0 commit comments

Comments
 (0)