Skip to content

Commit 3d0cb5b

Browse files
authoredMar 24, 2021
feat(metadata): expose client's algolia agent (#4694)
* feat(metadata): expose client's user agent The user agent can be used to inspect whether the client is using InstantSearch.js via an integration like Vue InstantSearch or Angular InstantSearch * ci(tsc): ignore this file for v3 * easier tests
1 parent 48c080e commit 3d0cb5b

File tree

5 files changed

+207
-40
lines changed

5 files changed

+207
-40
lines changed
 

‎.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
yarn add @types/algoliasearch@3.34.10 algoliasearch@3.35.1
7272
- run:
7373
name: Type Checking
74-
command: yarn run type-check
74+
command: yarn run type-check:v3
7575

7676
'unit tests':
7777
<<: *defaults

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"lint": "eslint --ext .js,.ts,.tsx .",
3535
"lint:fix": "eslint --ext .js,.ts,.tsx --fix .",
3636
"type-check": "tsc",
37+
"type-check:v3": "tsc --project tsconfig.v3.json",
3738
"type-check:js": "tsc --project tsconfig.checkjs.json",
3839
"type-check:watch": "yarn type-check --watch",
3940
"test": "jest",

‎src/middlewares/__tests__/createMetadataMiddleware.ts

+189-39
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import algoliasearch from 'algoliasearch';
2+
import algoliasearchV3 from 'algoliasearch-v3';
13
import { createMetadataMiddleware } from '..';
24
import { createSearchClient } from '../../../test/mock/createSearchClient';
35
import { wait } from '../../../test/utils/wait';
@@ -110,45 +112,193 @@ describe('createMetadataMiddleware', () => {
110112

111113
expect(JSON.parse(document.head.querySelector('meta')!.content))
112114
.toMatchInlineSnapshot(`
113-
Object {
114-
"widgets": Array [
115-
Object {
116-
"params": Array [],
117-
"type": "ais.searchBox",
118-
"widgetType": "ais.searchBox",
119-
},
120-
Object {
121-
"params": Array [],
122-
"type": "ais.searchBox",
123-
"widgetType": "ais.searchBox",
124-
},
125-
Object {
126-
"params": Array [
127-
"escapeHTML",
128-
],
129-
"type": "ais.hits",
130-
"widgetType": "ais.hits",
131-
},
132-
Object {
133-
"params": Array [],
134-
"type": "ais.index",
135-
"widgetType": "ais.index",
136-
},
137-
Object {
138-
"params": Array [],
139-
"type": "ais.pagination",
140-
"widgetType": "ais.pagination",
141-
},
142-
Object {
143-
"params": Array [
144-
"searchParameters",
145-
],
146-
"type": "ais.configure",
147-
"widgetType": "ais.configure",
148-
},
149-
],
150-
}
151-
`);
115+
Object {
116+
"widgets": Array [
117+
Object {
118+
"params": Array [],
119+
"type": "ais.searchBox",
120+
"widgetType": "ais.searchBox",
121+
},
122+
Object {
123+
"params": Array [],
124+
"type": "ais.searchBox",
125+
"widgetType": "ais.searchBox",
126+
},
127+
Object {
128+
"params": Array [
129+
"escapeHTML",
130+
],
131+
"type": "ais.hits",
132+
"widgetType": "ais.hits",
133+
},
134+
Object {
135+
"params": Array [],
136+
"type": "ais.index",
137+
"widgetType": "ais.index",
138+
},
139+
Object {
140+
"params": Array [],
141+
"type": "ais.pagination",
142+
"widgetType": "ais.pagination",
143+
},
144+
Object {
145+
"params": Array [
146+
"searchParameters",
147+
],
148+
"type": "ais.configure",
149+
"widgetType": "ais.configure",
150+
},
151+
],
152+
}
153+
`);
154+
});
155+
156+
describe('fills it with user agent after start', () => {
157+
it('for the v4 client', async () => {
158+
const fakeSearchClient = createSearchClient();
159+
const searchClient = algoliasearch('', '');
160+
// @ts-expect-error overriding the search method for testing
161+
searchClient.search = fakeSearchClient.search;
162+
163+
// not using createMetadataMiddleware() here,
164+
// since metadata is built into instantsearch
165+
const search = instantsearch({
166+
searchClient,
167+
indexName: 'test',
168+
});
169+
170+
search.start();
171+
172+
await wait(100);
173+
174+
expect(document.head.children).toHaveLength(1);
175+
expect(document.head.children[0]).toEqual(expect.any(HTMLMetaElement));
176+
177+
expect(
178+
JSON.parse(document.head.querySelector('meta')!.content)
179+
).toEqual({
180+
ua: expect.stringMatching(
181+
/^Algolia for JavaScript \(4\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\)$/
182+
),
183+
widgets: [],
184+
});
185+
});
186+
187+
it('for the v4 client with custom user agent', async () => {
188+
const fakeSearchClient = createSearchClient();
189+
const searchClient = algoliasearch('', '');
190+
// @ts-expect-error overriding the search method for testing
191+
searchClient.search = fakeSearchClient.search;
192+
193+
// not using createMetadataMiddleware() here,
194+
// since metadata is built into instantsearch
195+
const search = instantsearch({
196+
searchClient,
197+
indexName: 'test',
198+
});
199+
200+
search.start();
201+
202+
searchClient.addAlgoliaAgent('test', 'cool');
203+
204+
await wait(100);
205+
206+
expect(document.head.children).toHaveLength(1);
207+
expect(document.head.children[0]).toEqual(expect.any(HTMLMetaElement));
208+
209+
expect(
210+
JSON.parse(document.head.querySelector('meta')!.content)
211+
).toEqual({
212+
ua: expect.stringMatching(
213+
/^Algolia for JavaScript \(4\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\); test \(cool\)$/
214+
),
215+
widgets: [],
216+
});
217+
});
218+
219+
it('for the v3 client', async () => {
220+
const fakeSearchClient = createSearchClient();
221+
const searchClient = algoliasearchV3('qsdf', 'qsdf');
222+
searchClient.search = fakeSearchClient.search;
223+
224+
// not using createMetadataMiddleware() here,
225+
// since metadata is built into instantsearch
226+
const search = instantsearch({
227+
searchClient,
228+
indexName: 'test',
229+
});
230+
231+
search.start();
232+
233+
await wait(100);
234+
235+
expect(document.head.children).toHaveLength(1);
236+
expect(document.head.children[0]).toEqual(expect.any(HTMLMetaElement));
237+
238+
expect(
239+
JSON.parse(document.head.querySelector('meta')!.content)
240+
).toEqual({
241+
ua: expect.stringMatching(
242+
/^Algolia for JavaScript \(3\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\)$/
243+
),
244+
widgets: [],
245+
});
246+
});
247+
248+
it('for the v3 client with custom user agent', async () => {
249+
const fakeSearchClient = createSearchClient();
250+
const searchClient = algoliasearchV3('qsdf', 'qsdf');
251+
searchClient.search = fakeSearchClient.search;
252+
253+
// not using createMetadataMiddleware() here,
254+
// since metadata is built into instantsearch
255+
const search = instantsearch({
256+
searchClient,
257+
indexName: 'test',
258+
});
259+
260+
search.start();
261+
262+
searchClient.addAlgoliaAgent('test (cool)');
263+
264+
await wait(100);
265+
266+
expect(document.head.children).toHaveLength(1);
267+
expect(document.head.children[0]).toEqual(expect.any(HTMLMetaElement));
268+
269+
expect(
270+
JSON.parse(document.head.querySelector('meta')!.content)
271+
).toEqual({
272+
ua: expect.stringMatching(
273+
/^Algolia for JavaScript \(3\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\); test \(cool\)$/
274+
),
275+
widgets: [],
276+
});
277+
});
278+
279+
it('for a custom client (does not error)', async () => {
280+
const searchClient = createSearchClient();
281+
282+
// not using createMetadataMiddleware() here,
283+
// since metadata is built into instantsearch
284+
const search = instantsearch({
285+
searchClient,
286+
indexName: 'test',
287+
});
288+
289+
search.start();
290+
291+
await wait(100);
292+
293+
expect(document.head.children).toHaveLength(1);
294+
expect(document.head.children[0]).toEqual(expect.any(HTMLMetaElement));
295+
296+
expect(
297+
JSON.parse(document.head.querySelector('meta')!.content)
298+
).toEqual({
299+
widgets: [],
300+
});
301+
});
152302
});
153303
});
154304
});

‎src/middlewares/createMetadataMiddleware.ts

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type WidgetMetaData = {
99

1010
type Payload = {
1111
widgets: WidgetMetaData[];
12+
ua?: string;
1213
};
1314

1415
function extractPayload(
@@ -93,6 +94,12 @@ export function createMetadataMiddleware(): Middleware {
9394
subscribe() {
9495
// using setTimeout here to delay extraction until widgets have been added in a tick (e.g. Vue)
9596
setTimeout(() => {
97+
const client = instantSearchInstance.client as any;
98+
payload.ua =
99+
client.transporter && client.transporter.userAgent
100+
? client.transporter.userAgent.value
101+
: client._ua;
102+
96103
extractPayload(
97104
instantSearchInstance.mainIndex.getWidgets(),
98105
instantSearchInstance,

‎tsconfig.v3.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "./tsconfig",
3+
"exclude": [
4+
"examples",
5+
"es",
6+
// this test has specific code for v3 and v4, so already checked in the v4 test
7+
"src/middlewares/__tests__/createMetadataMiddleware.ts"
8+
]
9+
}

0 commit comments

Comments
 (0)
Please sign in to comment.