Skip to content

Commit 826f860

Browse files
committed
feat(javascript): allow legacy signature for search method
1 parent a1f7642 commit 826f860

File tree

11 files changed

+211
-19
lines changed

11 files changed

+211
-19
lines changed

.github/workflows/check.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,10 @@ jobs:
294294
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
295295
run: yarn cli build clients javascript algoliasearch
296296

297+
- name: Run 'algoliasearch' client tests
298+
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
299+
run: yarn workspace @experimental-api-clients-automation/algoliasearch test
300+
297301
- name: Zip artifact before storing
298302
run: zip -r -y client-javascript-algoliasearch.zip clients/algoliasearch-client-javascript/packages/algoliasearch/ -x "**/node_modules**" "clients/algoliasearch-client-javascript/.yarn**" "**/.github**"
299303

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: 5 additions & 2 deletions
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": {
26-
"@types/node": "16.11.26",
27+
"@types/jest": "27.5.2",
28+
"@types/node": "16.11.38",
29+
"jest": "28.1.1",
2730
"typescript": "4.6.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
}

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: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@ import { {{classname}} } from '{{filename}}';
2525

2626
{{#operations}}
2727
import type {
28-
{{#operation}}
29-
{{#vendorExtensions.x-create-wrapping-object}}
30-
{{#lambda.titlecase}}{{nickname}}{{/lambda.titlecase}}Props,
31-
{{/vendorExtensions.x-create-wrapping-object}}
32-
{{/operation}}
28+
{{#operation}}
29+
{{#vendorExtensions}}
30+
{{#x-create-wrapping-object}}
31+
{{#lambda.titlecase}}{{nickname}}{{/lambda.titlecase}}Props,
32+
{{/x-create-wrapping-object}}
33+
{{#x-legacy-signature}}
34+
LegacySearchMethodProps,
35+
{{/x-legacy-signature}}
36+
{{/vendorExtensions}}
37+
{{/operation}}
3338
} from '../model/clientMethodProps';
3439
{{/operations}}
3540

@@ -180,12 +185,16 @@ export function create{{capitalizedApiName}}({
180185
{{/x-create-wrapping-object}}
181186
{{#x-is-single-body-param}}
182187
{{#bodyParams}}
183-
{{paramName}}: {{{dataType}}},
188+
{{paramName}}: {{{dataType}}} {{#x-legacy-signature}} | LegacySearchMethodProps{{/x-legacy-signature}},
184189
{{/bodyParams}}
185190
{{/x-is-single-body-param}}
186191
{{/vendorExtensions}}
187192
requestOptions?: RequestOptions
188193
) : Promise<{{{returnType}}}> {
194+
{{#vendorExtensions.x-legacy-signature}}
195+
{{> legacySearchCompatible/implementation}}
196+
{{/vendorExtensions.x-legacy-signature}}
197+
189198
{{#allParams}}
190199
{{#required}}
191200
if ({{#isBoolean}}{{paramName}} === null || {{paramName}} === undefined{{/isBoolean}}{{^isBoolean}}!{{paramName}}{{/isBoolean}}) {

templates/javascript/clientMethodProps.mustache

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { {{classname}} } from '{{filename}}';
66

77
{{#operations}}
88
{{#operation}}
9-
{{#vendorExtensions.x-create-wrapping-object}}
9+
10+
{{#vendorExtensions}}
11+
{{#x-create-wrapping-object}}
1012
/**
1113
* Properties for the `{{nickname}}` method.
1214
*/
@@ -20,7 +22,11 @@ export type {{#lambda.titlecase}}{{nickname}}{{/lambda.titlecase}}Props = {
2022
{{paramName}}{{^required}}?{{/required}}: {{{dataType}}};
2123
{{/allParams}}
2224
}
23-
{{/vendorExtensions.x-create-wrapping-object}}
25+
{{/x-create-wrapping-object}}
26+
27+
{{#x-legacy-signature}}{{> legacySearchCompatible/model}}{{/x-legacy-signature}}
28+
29+
{{/vendorExtensions}}
2430

2531
{{/operation}}
2632
{{/operations}}
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: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { SearchForFacetsOptions } from './searchForFacetsOptions';
2+
import type { SearchForHitsOptions } from './searchForHitsOptions';
3+
import { SearchParamsObject } from './searchParamsObject';
4+
5+
/**
6+
* In v4, the search parameters are wrapped in a `params` parameter.
7+
*
8+
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
9+
*/
10+
type LegacySearchParams = {
11+
params?: Exclude<SearchParamsObject, 'query'>;
12+
query?: SearchParamsObject['query'];
13+
};
14+
15+
/**
16+
* In v4, the search parameters are wrapped in a `params` parameter.
17+
*
18+
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
19+
*/
20+
type LegacySearchForFacets = LegacySearchParams & SearchForFacetsOptions;
21+
22+
/**
23+
* In v4, the search parameters are wrapped in a `params` parameter.
24+
*
25+
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
26+
*/
27+
type LegacySearchForHits = LegacySearchParams & SearchForHitsOptions;
28+
29+
type LegacySearchQuery = LegacySearchForFacets | LegacySearchForHits;
30+
31+
/**
32+
* 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.
33+
*
34+
* @deprecated This signature will be removed from the next major version, we recommend using the `SearchMethodParams` type for performances and future proof reasons.
35+
*/
36+
export type LegacySearchMethodProps = LegacySearchQuery[];

yarn.lock

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,9 @@ __metadata:
14531453
"@experimental-api-clients-automation/client-search": 0.4.0
14541454
"@experimental-api-clients-automation/requester-browser-xhr": 0.4.0
14551455
"@experimental-api-clients-automation/requester-node-http": 0.4.0
1456-
"@types/node": 16.11.26
1456+
"@types/jest": 27.5.2
1457+
"@types/node": 16.11.38
1458+
jest: 28.1.1
14571459
typescript: 4.6.3
14581460
languageName: unknown
14591461
linkType: soft
@@ -4585,13 +4587,6 @@ __metadata:
45854587
languageName: node
45864588
linkType: hard
45874589

4588-
"@types/node@npm:16.11.26":
4589-
version: 16.11.26
4590-
resolution: "@types/node@npm:16.11.26"
4591-
checksum: 57757caaba3f0d95de82198cb276a1002c49b710108c932a1d02d7c91ff2fa57cfe2dd19fde60853b6dd90b0964b3cf35557981d2628e20aed6a909057aedfe6
4592-
languageName: node
4593-
linkType: hard
4594-
45954590
"@types/node@npm:16.11.38":
45964591
version: 16.11.38
45974592
resolution: "@types/node@npm:16.11.38"

0 commit comments

Comments
 (0)