Skip to content

feat(javascript): allow legacy signature for search method #665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ jobs:
if: ${{ steps.cache.outputs.cache-hit != 'true' && matrix.client.language == 'javascript' }}
run: yarn cli build clients javascript algoliasearch

- name: Run JavaScript 'algoliasearch' client tests
if: ${{ steps.cache.outputs.cache-hit != 'true' && matrix.client.language == 'javascript' }}
run: yarn workspace @experimental-api-clients-automation/algoliasearch test

- name: Clean CTS output before generate
run: rm -rf ${{ matrix.client.testsToDelete }} || true

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { EchoResponse } from '@experimental-api-clients-automation/client-common';
import { echoRequester } from '@experimental-api-clients-automation/requester-node-http';

import { algoliasearch, apiClientVersion } from '../builds/node';

const client = algoliasearch('APP_ID', 'API_KEY', {
requester: echoRequester(),
});

describe('api', () => {
it('sets the user agent', async () => {
const req = (await client.post({
path: '/test',
})) as unknown as EchoResponse;

expect(req.algoliaAgent).toMatchInlineSnapshot(
`"Algolia%20for%20JavaScript%20(${apiClientVersion});%20Search%20(${apiClientVersion});%20Node.js%20(${process.versions.node})"`
);
});

it('throws with undefined API key', () => {
try {
algoliasearch('APP_ID', '');
} catch (e) {
expect((e as Error).message).toMatch('`apiKey` is missing.');
}
});

it('throws with undefined app ID', () => {
try {
algoliasearch('', 'API_KEY');
} catch (e) {
expect((e as Error).message).toMatch('`appId` is missing.');
}
});

it('provides the search client at the root of the API', () => {
expect(client.search).not.toBeUndefined();
});

it('provides an init method for the analytics client', () => {
expect(client.initAnalytics).not.toBeUndefined();
});

it('provides an init method for the personalization client', () => {
expect(client.initPersonalization).not.toBeUndefined();
});
});

/**
* We only test the legacy signature, as `algoliasearch` inherits methods from the `client-search`.
* The new signatures are already tested in the CTS.
*/
describe('search with legacy signature', () => {
it('allows searching for query', async () => {
const req = (await client.search([
{
indexName: 'theIndexName',
},
])) as unknown as EchoResponse;

expect(req.path).toEqual('/1/indexes/*/queries');
expect(req.method).toEqual('POST');
expect(req.data).toEqual({ requests: [{ indexName: 'theIndexName' }] });
expect(req.searchParams).toStrictEqual(undefined);
});

it('allows searching for facet', async () => {
const req = (await client.search([
{
indexName: 'theIndexName',
type: 'facet',
facet: 'theFacet',
},
])) as unknown as EchoResponse;

expect(req.path).toEqual('/1/indexes/*/queries');
expect(req.method).toEqual('POST');
expect(req.data).toEqual({
requests: [
{ indexName: 'theIndexName', type: 'facet', facet: 'theFacet' },
],
});
expect(req.searchParams).toStrictEqual(undefined);
});

it('accepts a `params` parameter for `searchParams`', async () => {
const req = (await client.search([
{
indexName: 'theIndexName',
params: {
hitsPerPage: 42,
},
},
])) as unknown as EchoResponse;

expect(req.path).toEqual('/1/indexes/*/queries');
expect(req.method).toEqual('POST');
expect(req.data).toEqual({
requests: [{ indexName: 'theIndexName', hitsPerPage: 42 }],
});
expect(req.searchParams).toStrictEqual(undefined);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Config } from '@jest/types';

const config: Config.InitialOptions = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['__tests__'],
};

export default config;
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"browser": "dist/algoliasearch.cjs.browser.js",
"types": "index.d.ts",
"scripts": {
"clean": "rm -rf ./dist"
"clean": "rm -rf ./dist",
"test": "jest"
},
"dependencies": {
"@experimental-api-clients-automation/client-analytics": "0.4.0",
Expand All @@ -23,7 +24,9 @@
"@experimental-api-clients-automation/requester-node-http": "0.4.0"
},
"devDependencies": {
"@types/jest": "28.1.1",
"@types/node": "16.11.39",
"jest": "28.1.1",
"typescript": "4.7.3"
},
"engines": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node", "jest"],
"outDir": "dist"
},
"include": ["builds/node.ts", "builds/browser.ts"],
"exclude": ["dist", "node_modules"]
"exclude": ["dist", "node_modules", "__tests__"]
}
12 changes: 12 additions & 0 deletions playground/javascript/node/algoliasearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ async function testAlgoliasearch() {
});

console.log(`[OK search]`, res);

const resWithLegacySignature: SearchResponses = await client.search([
{
indexName: searchIndex,
params: {
query: searchQuery,
hitsPerPage: 50,
},
},
]);

console.log(`[OK legacy search]`, resWithLegacySignature);
} catch (e) {
if (e instanceof ApiError) {
return console.log(`[${e.status}] ${e.message}`, e.stackTrace);
Expand Down
24 changes: 12 additions & 12 deletions playground/javascript/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@
"start:predict": "ts-node predict.ts"
},
"dependencies": {
"@experimental-api-clients-automation/algoliasearch": "0.3.0",
"@experimental-api-clients-automation/client-abtesting": "0.3.0",
"@experimental-api-clients-automation/client-analytics": "0.3.0",
"@experimental-api-clients-automation/client-common": "0.3.0",
"@experimental-api-clients-automation/client-insights": "0.3.0",
"@experimental-api-clients-automation/client-personalization": "0.3.0",
"@experimental-api-clients-automation/client-predict": "0.3.0",
"@experimental-api-clients-automation/client-query-suggestions": "0.3.0",
"@experimental-api-clients-automation/client-search": "0.3.0",
"@experimental-api-clients-automation/client-sources": "0.3.0",
"@experimental-api-clients-automation/recommend": "0.3.0",
"@experimental-api-clients-automation/requester-node-http": "0.3.0"
"@experimental-api-clients-automation/algoliasearch": "0.4.0",
"@experimental-api-clients-automation/client-abtesting": "0.4.0",
"@experimental-api-clients-automation/client-analytics": "0.4.0",
"@experimental-api-clients-automation/client-common": "0.4.0",
"@experimental-api-clients-automation/client-insights": "0.4.0",
"@experimental-api-clients-automation/client-personalization": "0.4.0",
"@experimental-api-clients-automation/client-predict": "0.4.0",
"@experimental-api-clients-automation/client-query-suggestions": "0.4.0",
"@experimental-api-clients-automation/client-search": "0.4.0",
"@experimental-api-clients-automation/client-sources": "0.4.0",
"@experimental-api-clients-automation/recommend": "0.4.0",
"@experimental-api-clients-automation/requester-node-http": "0.4.0"
},
"devDependencies": {
"dotenv": "16.0.1",
Expand Down
1 change: 1 addition & 0 deletions specs/search/paths/search/search.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ post:
operationId: search
x-use-read-transporter: true
x-cacheable: true
x-legacy-signature: true
summary: Search multiple indices.
description: Perform a search operation targeting one or many indices.
requestBody:
Expand Down
4 changes: 4 additions & 0 deletions templates/javascript/api-single.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ export function create{{capitalizedApiName}}({
{{#operation}}
{{> api/operation/jsdoc}}
{{nickname}}( {{> api/operation/parameters}} ) : Promise<{{{returnType}}}> {
{{#vendorExtensions.x-legacy-signature}}
{{> api/operation/legacySearchCompatible/implementation}}
{{/vendorExtensions.x-legacy-signature}}

{{#allParams}}
{{#required}}
if ({{#isBoolean}}{{paramName}} === null || {{paramName}} === undefined{{/isBoolean}}{{^isBoolean}}!{{paramName}}{{/isBoolean}}) {
Expand Down
3 changes: 3 additions & 0 deletions templates/javascript/api/imports.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import type {
{{#x-create-wrapping-object}}
{{#lambda.titlecase}}{{nickname}}{{/lambda.titlecase}}Props,
{{/x-create-wrapping-object}}
{{#x-legacy-signature}}
LegacySearchMethodProps,
{{/x-legacy-signature}}
{{/vendorExtensions}}
{{/operation}}
} from '../model/clientMethodProps';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
if (searchMethodParams && Array.isArray(searchMethodParams)) {
const newSignatureRequest: SearchMethodParams = {
requests: searchMethodParams.map(({ params, ...legacyRequest }) => {
if (legacyRequest.type === 'facet') {
return {
...legacyRequest,
...params,
type: 'facet',
};
}

return {
...legacyRequest,
...params,
facet: undefined,
maxFacetHits: undefined,
facetQuery: undefined,
};
}),
};

// eslint-disable-next-line no-param-reassign
searchMethodParams = newSignatureRequest;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SearchForFacetsOptions } from './searchForFacetsOptions';
import type { SearchForHitsOptions } from './searchForHitsOptions';
import { SearchParamsObject } from './searchParamsObject';
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* In v4, the search parameters are wrapped in a `params` parameter.
*
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
*/
type LegacySearchParams = {
params?: SearchParamsObject;
};

/**
* In v4, the search parameters are wrapped in a `params` parameter.
*
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
*/
type LegacySearchForFacets = LegacySearchParams & SearchForFacetsOptions;

/**
* In v4, the search parameters are wrapped in a `params` parameter.
*
* @deprecated The `search` method now accepts flat `searchParams` at the root of the method.
*/
type LegacySearchForHits = LegacySearchParams & SearchForHitsOptions;

type LegacySearchQuery = LegacySearchForFacets | LegacySearchForHits;

/**
* 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.
*
* @deprecated This signature will be removed from the next major version, we recommend using the `SearchMethodParams` type for performances and future proof reasons.
*/
export type LegacySearchMethodProps = LegacySearchQuery[];
2 changes: 1 addition & 1 deletion templates/javascript/api/operation/parameters.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{{/x-create-wrapping-object}}
{{#x-is-single-body-param}}
{{#bodyParams}}
{{paramName}}: {{{dataType}}},
{{paramName}}: {{{dataType}}} {{#x-legacy-signature}} | LegacySearchMethodProps{{/x-legacy-signature}},
{{/bodyParams}}
{{/x-is-single-body-param}}
{{/vendorExtensions}}
Expand Down
13 changes: 11 additions & 2 deletions templates/javascript/clientMethodProps.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
import { {{classname}} } from '{{filename}}';
{{/imports}}

{{! Imports for the legacy search method signature }}
{{#operations}}{{#operation}}{{#vendorExtensions.x-legacy-signature}}{{> api/operation/legacySearchCompatible/imports}}{{/vendorExtensions.x-legacy-signature}}{{/operation}}{{/operations}}

{{#operations}}
{{#operation}}
{{#vendorExtensions.x-create-wrapping-object}}

{{#vendorExtensions}}
{{#x-create-wrapping-object}}
/**
* Properties for the `{{nickname}}` method.
*/
Expand All @@ -20,7 +25,11 @@ export type {{#lambda.titlecase}}{{nickname}}{{/lambda.titlecase}}Props = {
{{paramName}}{{^required}}?{{/required}}: {{{dataType}}};
{{/allParams}}
}
{{/vendorExtensions.x-create-wrapping-object}}
{{/x-create-wrapping-object}}

{{#x-legacy-signature}}{{> api/operation/legacySearchCompatible/model}}{{/x-legacy-signature}}

{{/vendorExtensions}}

{{/operation}}
{{/operations}}
Expand Down
Loading