Skip to content

Commit 98b40e8

Browse files
authored
feat(javascript): allow legacy signature for search method (#665)
1 parent 867d3e2 commit 98b40e8

File tree

16 files changed

+240
-132
lines changed

16 files changed

+240
-132
lines changed

.github/workflows/check.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ jobs:
228228
if: ${{ steps.cache.outputs.cache-hit != 'true' && matrix.client.language == 'javascript' }}
229229
run: yarn cli build clients javascript algoliasearch
230230

231+
- name: Run JavaScript 'algoliasearch' client tests
232+
if: ${{ steps.cache.outputs.cache-hit != 'true' && matrix.client.language == 'javascript' }}
233+
run: yarn workspace @experimental-api-clients-automation/algoliasearch test
234+
231235
- name: Clean CTS output before generate
232236
run: rm -rf ${{ matrix.client.testsToDelete }} || true
233237

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import type { EchoResponse } from '@experimental-api-clients-automation/client-common';
2+
import { echoRequester } from '@experimental-api-clients-automation/requester-node-http';
3+
4+
import { algoliasearch, apiClientVersion } from '../builds/node';
5+
6+
const client = algoliasearch('APP_ID', 'API_KEY', {
7+
requester: echoRequester(),
8+
});
9+
10+
describe('api', () => {
11+
it('sets the user agent', async () => {
12+
const req = (await client.post({
13+
path: '/test',
14+
})) as unknown as EchoResponse;
15+
16+
expect(req.algoliaAgent).toMatchInlineSnapshot(
17+
`"Algolia%20for%20JavaScript%20(${apiClientVersion});%20Search%20(${apiClientVersion});%20Node.js%20(${process.versions.node})"`
18+
);
19+
});
20+
21+
it('throws with undefined API key', () => {
22+
try {
23+
algoliasearch('APP_ID', '');
24+
} catch (e) {
25+
expect((e as Error).message).toMatch('`apiKey` is missing.');
26+
}
27+
});
28+
29+
it('throws with undefined app ID', () => {
30+
try {
31+
algoliasearch('', 'API_KEY');
32+
} catch (e) {
33+
expect((e as Error).message).toMatch('`appId` is missing.');
34+
}
35+
});
36+
37+
it('provides the search client at the root of the API', () => {
38+
expect(client.search).not.toBeUndefined();
39+
});
40+
41+
it('provides an init method for the analytics client', () => {
42+
expect(client.initAnalytics).not.toBeUndefined();
43+
});
44+
45+
it('provides an init method for the personalization client', () => {
46+
expect(client.initPersonalization).not.toBeUndefined();
47+
});
48+
});
49+
50+
/**
51+
* We only test the legacy signature, as `algoliasearch` inherits methods from the `client-search`.
52+
* The new signatures are already tested in the CTS.
53+
*/
54+
describe('search with legacy signature', () => {
55+
it('allows searching for query', async () => {
56+
const req = (await client.search([
57+
{
58+
indexName: 'theIndexName',
59+
},
60+
])) as unknown as EchoResponse;
61+
62+
expect(req.path).toEqual('/1/indexes/*/queries');
63+
expect(req.method).toEqual('POST');
64+
expect(req.data).toEqual({ requests: [{ indexName: 'theIndexName' }] });
65+
expect(req.searchParams).toStrictEqual(undefined);
66+
});
67+
68+
it('allows searching for facet', async () => {
69+
const req = (await client.search([
70+
{
71+
indexName: 'theIndexName',
72+
type: 'facet',
73+
facet: 'theFacet',
74+
},
75+
])) as unknown as EchoResponse;
76+
77+
expect(req.path).toEqual('/1/indexes/*/queries');
78+
expect(req.method).toEqual('POST');
79+
expect(req.data).toEqual({
80+
requests: [
81+
{ indexName: 'theIndexName', type: 'facet', facet: 'theFacet' },
82+
],
83+
});
84+
expect(req.searchParams).toStrictEqual(undefined);
85+
});
86+
87+
it('accepts a `params` parameter for `searchParams`', async () => {
88+
const req = (await client.search([
89+
{
90+
indexName: 'theIndexName',
91+
params: {
92+
hitsPerPage: 42,
93+
},
94+
},
95+
])) as unknown as EchoResponse;
96+
97+
expect(req.path).toEqual('/1/indexes/*/queries');
98+
expect(req.method).toEqual('POST');
99+
expect(req.data).toEqual({
100+
requests: [{ indexName: 'theIndexName', hitsPerPage: 42 }],
101+
});
102+
expect(req.searchParams).toStrictEqual(undefined);
103+
});
104+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { Config } from '@jest/types';
2+
3+
const config: Config.InitialOptions = {
4+
preset: 'ts-jest',
5+
testEnvironment: 'node',
6+
roots: ['__tests__'],
7+
};
8+
9+
export default config;

clients/algoliasearch-client-javascript/packages/algoliasearch/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"browser": "dist/algoliasearch.cjs.browser.js",
1313
"types": "index.d.ts",
1414
"scripts": {
15-
"clean": "rm -rf ./dist"
15+
"clean": "rm -rf ./dist",
16+
"test": "jest"
1617
},
1718
"dependencies": {
1819
"@experimental-api-clients-automation/client-analytics": "0.4.0",
@@ -23,7 +24,9 @@
2324
"@experimental-api-clients-automation/requester-node-http": "0.4.0"
2425
},
2526
"devDependencies": {
27+
"@types/jest": "28.1.1",
2628
"@types/node": "16.11.39",
29+
"jest": "28.1.1",
2730
"typescript": "4.7.3"
2831
},
2932
"engines": {
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"extends": "../../tsconfig.json",
33
"compilerOptions": {
4+
"types": ["node", "jest"],
45
"outDir": "dist"
56
},
67
"include": ["builds/node.ts", "builds/browser.ts"],
7-
"exclude": ["dist", "node_modules"]
8+
"exclude": ["dist", "node_modules", "__tests__"]
89
}

playground/javascript/node/algoliasearch.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ async function testAlgoliasearch() {
4242
});
4343

4444
console.log(`[OK search]`, res);
45+
46+
const resWithLegacySignature: SearchResponses = await client.search([
47+
{
48+
indexName: searchIndex,
49+
params: {
50+
query: searchQuery,
51+
hitsPerPage: 50,
52+
},
53+
},
54+
]);
55+
56+
console.log(`[OK legacy search]`, resWithLegacySignature);
4557
} catch (e) {
4658
if (e instanceof ApiError) {
4759
return console.log(`[${e.status}] ${e.message}`, e.stackTrace);

playground/javascript/node/package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@
1515
"start:predict": "ts-node predict.ts"
1616
},
1717
"dependencies": {
18-
"@experimental-api-clients-automation/algoliasearch": "0.3.0",
19-
"@experimental-api-clients-automation/client-abtesting": "0.3.0",
20-
"@experimental-api-clients-automation/client-analytics": "0.3.0",
21-
"@experimental-api-clients-automation/client-common": "0.3.0",
22-
"@experimental-api-clients-automation/client-insights": "0.3.0",
23-
"@experimental-api-clients-automation/client-personalization": "0.3.0",
24-
"@experimental-api-clients-automation/client-predict": "0.3.0",
25-
"@experimental-api-clients-automation/client-query-suggestions": "0.3.0",
26-
"@experimental-api-clients-automation/client-search": "0.3.0",
27-
"@experimental-api-clients-automation/client-sources": "0.3.0",
28-
"@experimental-api-clients-automation/recommend": "0.3.0",
29-
"@experimental-api-clients-automation/requester-node-http": "0.3.0"
18+
"@experimental-api-clients-automation/algoliasearch": "0.4.0",
19+
"@experimental-api-clients-automation/client-abtesting": "0.4.0",
20+
"@experimental-api-clients-automation/client-analytics": "0.4.0",
21+
"@experimental-api-clients-automation/client-common": "0.4.0",
22+
"@experimental-api-clients-automation/client-insights": "0.4.0",
23+
"@experimental-api-clients-automation/client-personalization": "0.4.0",
24+
"@experimental-api-clients-automation/client-predict": "0.4.0",
25+
"@experimental-api-clients-automation/client-query-suggestions": "0.4.0",
26+
"@experimental-api-clients-automation/client-search": "0.4.0",
27+
"@experimental-api-clients-automation/client-sources": "0.4.0",
28+
"@experimental-api-clients-automation/recommend": "0.4.0",
29+
"@experimental-api-clients-automation/requester-node-http": "0.4.0"
3030
},
3131
"devDependencies": {
3232
"dotenv": "16.0.1",

specs/search/paths/search/search.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ post:
44
operationId: search
55
x-use-read-transporter: true
66
x-cacheable: true
7+
x-legacy-signature: true
78
summary: Search multiple indices.
89
description: Perform a search operation targeting one or many indices.
910
requestBody:

templates/javascript/api-single.mustache

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ export function create{{capitalizedApiName}}({
6868
{{#operation}}
6969
{{> api/operation/jsdoc}}
7070
{{nickname}}( {{> api/operation/parameters}} ) : Promise<{{{returnType}}}> {
71+
{{#vendorExtensions.x-legacy-signature}}
72+
{{> api/operation/legacySearchCompatible/implementation}}
73+
{{/vendorExtensions.x-legacy-signature}}
74+
7175
{{#allParams}}
7276
{{#required}}
7377
if ({{#isBoolean}}{{paramName}} === null || {{paramName}} === undefined{{/isBoolean}}{{^isBoolean}}!{{paramName}}{{/isBoolean}}) {

templates/javascript/api/imports.mustache

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import type {
3030
{{#x-create-wrapping-object}}
3131
{{#lambda.titlecase}}{{nickname}}{{/lambda.titlecase}}Props,
3232
{{/x-create-wrapping-object}}
33+
{{#x-legacy-signature}}
34+
LegacySearchMethodProps,
35+
{{/x-legacy-signature}}
3336
{{/vendorExtensions}}
3437
{{/operation}}
3538
} from '../model/clientMethodProps';
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
if (searchMethodParams && Array.isArray(searchMethodParams)) {
2+
const newSignatureRequest: SearchMethodParams = {
3+
requests: searchMethodParams.map(({ params, ...legacyRequest }) => {
4+
if (legacyRequest.type === 'facet') {
5+
return {
6+
...legacyRequest,
7+
...params,
8+
type: 'facet',
9+
};
10+
}
11+
12+
return {
13+
...legacyRequest,
14+
...params,
15+
facet: undefined,
16+
maxFacetHits: undefined,
17+
facetQuery: undefined,
18+
};
19+
}),
20+
};
21+
22+
// eslint-disable-next-line no-param-reassign
23+
searchMethodParams = newSignatureRequest;
24+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import type { SearchForFacetsOptions } from './searchForFacetsOptions';
2+
import type { SearchForHitsOptions } from './searchForHitsOptions';
3+
import { SearchParamsObject } from './searchParamsObject';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* In v4, the search parameters are wrapped in a `params` parameter.
3+
*
4+
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
5+
*/
6+
type LegacySearchParams = {
7+
params?: SearchParamsObject;
8+
};
9+
10+
/**
11+
* In v4, the search parameters are wrapped in a `params` parameter.
12+
*
13+
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
14+
*/
15+
type LegacySearchForFacets = LegacySearchParams & SearchForFacetsOptions;
16+
17+
/**
18+
* In v4, the search parameters are wrapped in a `params` parameter.
19+
*
20+
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
21+
*/
22+
type LegacySearchForHits = LegacySearchParams & SearchForHitsOptions;
23+
24+
type LegacySearchQuery = LegacySearchForFacets | LegacySearchForHits;
25+
26+
/**
27+
* Search method signature compatible with the `algoliasearch` v4 package. When using this signature, extra computation will be required to make it match the new signature.
28+
*
29+
* @deprecated This signature will be removed from the next major version, we recommend using the `SearchMethodParams` type for performances and future proof reasons.
30+
*/
31+
export type LegacySearchMethodProps = LegacySearchQuery[];

templates/javascript/api/operation/parameters.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{{/x-create-wrapping-object}}
99
{{#x-is-single-body-param}}
1010
{{#bodyParams}}
11-
{{paramName}}: {{{dataType}}},
11+
{{paramName}}: {{{dataType}}} {{#x-legacy-signature}} | LegacySearchMethodProps{{/x-legacy-signature}},
1212
{{/bodyParams}}
1313
{{/x-is-single-body-param}}
1414
{{/vendorExtensions}}

templates/javascript/clientMethodProps.mustache

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
import { {{classname}} } from '{{filename}}';
55
{{/imports}}
66

7+
{{! Imports for the legacy search method signature }}
8+
{{#operations}}{{#operation}}{{#vendorExtensions.x-legacy-signature}}{{> api/operation/legacySearchCompatible/imports}}{{/vendorExtensions.x-legacy-signature}}{{/operation}}{{/operations}}
9+
710
{{#operations}}
811
{{#operation}}
9-
{{#vendorExtensions.x-create-wrapping-object}}
12+
13+
{{#vendorExtensions}}
14+
{{#x-create-wrapping-object}}
1015
/**
1116
* Properties for the `{{nickname}}` method.
1217
*/
@@ -20,7 +25,11 @@ export type {{#lambda.titlecase}}{{nickname}}{{/lambda.titlecase}}Props = {
2025
{{paramName}}{{^required}}?{{/required}}: {{{dataType}}};
2126
{{/allParams}}
2227
}
23-
{{/vendorExtensions.x-create-wrapping-object}}
28+
{{/x-create-wrapping-object}}
29+
30+
{{#x-legacy-signature}}{{> api/operation/legacySearchCompatible/model}}{{/x-legacy-signature}}
31+
32+
{{/vendorExtensions}}
2433

2534
{{/operation}}
2635
{{/operations}}

0 commit comments

Comments
 (0)