From 4bacf25076c64de9db80e84340c7d17c103e8608 Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Fri, 17 Dec 2021 15:42:02 +0100 Subject: [PATCH] feat: add Rules endpoints for search client (#48) --- .../client-search/model/anchoring.ts | 9 + .../model/automaticFacetFilter.ts | 17 ++ .../client-search/model/condition.ts | 17 ++ .../client-search/model/consequence.ts | 26 ++ .../client-search/model/consequenceHide.ts | 9 + .../client-search/model/deletedRule.ts | 10 + .../client-search/model/models.ts | 13 + .../client-search/model/params.ts | 19 ++ .../client-search/model/promote.ts | 17 ++ .../client-search/model/rule.ts | 30 ++ .../client-search/model/searchRulesParams.ts | 32 ++ .../model/searchRulesResponse.ts | 20 ++ .../model/searchUserIdsResponse.ts | 2 +- .../client-search/model/timeRange.ts | 10 + .../model/updatedRuleResponse.ts | 14 + .../updatedRuleResponseWithoutObjectID.ts | 10 + .../client-search/src/searchApi.ts | 284 ++++++++++++++++++ openapitools.json | 2 +- specs/common/parameters.yml | 15 + .../paths/multiclusters/searchUserIds.yml | 2 +- specs/search/paths/rules/batchRules.yml | 33 ++ specs/search/paths/rules/clearRules.yml | 23 ++ .../search/paths/rules/common/parameters.yml | 7 + specs/search/paths/rules/common/schemas.yml | 182 +++++++++++ specs/search/paths/rules/rule.yml | 79 +++++ specs/search/paths/rules/searchRules.yml | 76 +++++ specs/search/spec.yml | 16 +- tests/CTS/clients/search/batchRules.json | 67 +++++ tests/CTS/clients/search/clearRules.json | 11 + tests/CTS/clients/search/deleteRule.json | 11 + tests/CTS/clients/search/getRule.json | 11 + tests/CTS/clients/search/saveRule.json | 39 +++ tests/CTS/clients/search/searchRules.json | 14 + tests/output/javascript/search.test.ts | 91 ++++++ 34 files changed, 1207 insertions(+), 11 deletions(-) create mode 100644 clients/algoliasearch-client-javascript/client-search/model/anchoring.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/automaticFacetFilter.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/condition.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/consequence.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/consequenceHide.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/deletedRule.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/params.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/promote.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/rule.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/searchRulesParams.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/searchRulesResponse.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/timeRange.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponse.ts create mode 100644 clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponseWithoutObjectID.ts create mode 100644 specs/search/paths/rules/common/parameters.yml create mode 100644 specs/search/paths/rules/common/schemas.yml create mode 100644 tests/CTS/clients/search/batchRules.json create mode 100644 tests/CTS/clients/search/clearRules.json create mode 100644 tests/CTS/clients/search/deleteRule.json create mode 100644 tests/CTS/clients/search/getRule.json create mode 100644 tests/CTS/clients/search/saveRule.json create mode 100644 tests/CTS/clients/search/searchRules.json diff --git a/clients/algoliasearch-client-javascript/client-search/model/anchoring.ts b/clients/algoliasearch-client-javascript/client-search/model/anchoring.ts new file mode 100644 index 0000000000..46fd248e5a --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/anchoring.ts @@ -0,0 +1,9 @@ +/** + * Whether the pattern parameter must match the beginning or the end of the query string, or both, or none. + */ +export enum Anchoring { + Is = 'is', + StartsWith = 'startsWith', + EndsWith = 'endsWith', + Contains = 'contains', +} diff --git a/clients/algoliasearch-client-javascript/client-search/model/automaticFacetFilter.ts b/clients/algoliasearch-client-javascript/client-search/model/automaticFacetFilter.ts new file mode 100644 index 0000000000..9db0524583 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/automaticFacetFilter.ts @@ -0,0 +1,17 @@ +/** + * Automatic facet Filter. + */ +export type AutomaticFacetFilter = { + /** + * Attribute to filter on. This must match a facet placeholder in the Rule’s pattern. + */ + facet: string; + /** + * Score for the filter. Typically used for optional or disjunctive filters. + */ + score?: number; + /** + * Whether the filter is disjunctive (true) or conjunctive (false). + */ + disjunctive?: boolean; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/condition.ts b/clients/algoliasearch-client-javascript/client-search/model/condition.ts new file mode 100644 index 0000000000..41db9a3cc1 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/condition.ts @@ -0,0 +1,17 @@ +import type { Anchoring } from './anchoring'; + +export type Condition = { + /** + * Query pattern syntax. + */ + pattern?: string; + anchoring?: Anchoring; + /** + * Whether the pattern matches on plurals, synonyms, and typos. + */ + alternatives?: boolean; + /** + * Rule context format: [A-Za-z0-9_-]+). + */ + context?: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/consequence.ts b/clients/algoliasearch-client-javascript/client-search/model/consequence.ts new file mode 100644 index 0000000000..b41a4583d2 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/consequence.ts @@ -0,0 +1,26 @@ +import type { ConsequenceHide } from './consequenceHide'; +import type { Params } from './params'; +import type { Promote } from './promote'; + +/** + * Consequence of the Rule. + */ +export type Consequence = { + params?: Params; + /** + * Objects to promote as hits. + */ + promote?: Promote[]; + /** + * Only use in combination with the promote consequence. When true, promoted results will be restricted to match the filters of the current search. When false, the promoted results will show up regardless of the filters. + */ + filterPromotes?: boolean; + /** + * Objects to hide from hits. Each object must contain an objectID field. By default, you can hide up to 50 items per rule. + */ + hide?: ConsequenceHide[]; + /** + * Custom JSON object that will be appended to the userData array in the response. This object isn’t interpreted by the API. It’s limited to 1kB of minified JSON. + */ + userData?: { [key: string]: Record }; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/consequenceHide.ts b/clients/algoliasearch-client-javascript/client-search/model/consequenceHide.ts new file mode 100644 index 0000000000..28f87c9c0c --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/consequenceHide.ts @@ -0,0 +1,9 @@ +/** + * Unique identifier of the object to hide. + */ +export type ConsequenceHide = { + /** + * Unique identifier of the object. + */ + objectID: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/deletedRule.ts b/clients/algoliasearch-client-javascript/client-search/model/deletedRule.ts new file mode 100644 index 0000000000..d25b854ac5 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/deletedRule.ts @@ -0,0 +1,10 @@ +export type DeletedRule = { + /** + * Date of last update (ISO-8601 format). + */ + updatedAt: Date; + /** + * TaskID of the indexing task to wait for. + */ + taskID: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/models.ts b/clients/algoliasearch-client-javascript/client-search/model/models.ts index cb84373b1a..e7665c4aff 100644 --- a/clients/algoliasearch-client-javascript/client-search/model/models.ts +++ b/clients/algoliasearch-client-javascript/client-search/model/models.ts @@ -2,10 +2,12 @@ import type { RequestOptions } from '../utils/types'; export * from './addApiKeyResponse'; +export * from './anchoring'; export * from './apiKey'; export * from './appendSourceResponse'; export * from './assignUserIdObject'; export * from './assignUserIdResponse'; +export * from './automaticFacetFilter'; export * from './baseIndexSettings'; export * from './baseSearchParams'; export * from './baseSearchResponse'; @@ -15,6 +17,9 @@ export * from './batchAssignUserIdsResponse'; export * from './batchObject'; export * from './batchResponse'; export * from './clearAllSynonymsResponse'; +export * from './condition'; +export * from './consequence'; +export * from './consequenceHide'; export * from './createdAtObject'; export * from './deleteApiKeyResponse'; export * from './deleteIndexResponse'; @@ -42,11 +47,14 @@ export * from './multipleQueriesResponse'; export * from './operation'; export * from './operationIndexObject'; export * from './operationIndexResponse'; +export * from './params'; +export * from './promote'; export * from './rankingInfo'; export * from './rankingInfoMatchedGeoLocation'; export * from './record'; export * from './removeUserIdResponse'; export * from './replaceSourceResponse'; +export * from './rule'; export * from './saveObjectResponse'; export * from './saveSynonymResponse'; export * from './saveSynonymsResponse'; @@ -54,6 +62,8 @@ export * from './searchHits'; export * from './searchParams'; export * from './searchParamsAsString'; export * from './searchResponse'; +export * from './searchRulesParams'; +export * from './searchRulesResponse'; export * from './searchSynonymsResponse'; export * from './searchUserIdsObject'; export * from './searchUserIdsResponse'; @@ -64,7 +74,10 @@ export * from './snippetResult'; export * from './source'; export * from './synonymHit'; export * from './synonymHitHighlightResult'; +export * from './timeRange'; export * from './updateApiKeyResponse'; +export * from './updatedRuleResponse'; +export * from './updatedRuleResponseWithoutObjectID'; export * from './userId'; export interface Authentication { diff --git a/clients/algoliasearch-client-javascript/client-search/model/params.ts b/clients/algoliasearch-client-javascript/client-search/model/params.ts new file mode 100644 index 0000000000..59c91f2bc1 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/params.ts @@ -0,0 +1,19 @@ +import type { AutomaticFacetFilter } from './automaticFacetFilter'; + +/** + * Additional search parameters. Any valid search parameter is allowed. + */ +export type Params = { + /** + * Query string. + */ + query?: string; + /** + * Names of facets to which automatic filtering must be applied; they must match the facet name of a facet value placeholder in the query pattern. + */ + automaticFacetFilters?: AutomaticFacetFilter[]; + /** + * Same syntax as automaticFacetFilters, but the engine treats the filters as optional. + */ + automaticOptionalFacetFilters?: AutomaticFacetFilter[]; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/promote.ts b/clients/algoliasearch-client-javascript/client-search/model/promote.ts new file mode 100644 index 0000000000..915ad295da --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/promote.ts @@ -0,0 +1,17 @@ +/** + * Object to promote as hits. + */ +export type Promote = { + /** + * Unique identifier of the object to promote. + */ + objectID?: string; + /** + * Array of unique identifiers of the objects to promote. + */ + objectIDs?: string[]; + /** + * The position to promote the objects to (zero-based). If you pass objectIDs, the objects are placed at this position as a group. For example, if you pass four objectIDs to position 0, the objects take the first four positions. + */ + position: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/rule.ts b/clients/algoliasearch-client-javascript/client-search/model/rule.ts new file mode 100644 index 0000000000..5c606d79d1 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/rule.ts @@ -0,0 +1,30 @@ +import type { Condition } from './condition'; +import type { Consequence } from './consequence'; +import type { TimeRange } from './timeRange'; + +/** + * Rule object. + */ +export type Rule = { + /** + * Unique identifier of the object. + */ + objectID: string; + /** + * A list of conditions that should apply to activate a Rule. You can use up to 25 conditions per Rule. + */ + conditions?: Condition[]; + consequence: Consequence; + /** + * This field is intended for Rule management purposes, in particular to ease searching for Rules and presenting them to human readers. It’s not interpreted by the API. + */ + description?: string; + /** + * Whether the Rule is enabled. Disabled Rules remain in the index, but aren’t applied at query time. + */ + enabled?: boolean; + /** + * By default, Rules are permanently valid. When validity periods are specified, the Rule applies only during those periods; it’s ignored the rest of the time. The list must not be empty. + */ + validity?: TimeRange[]; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/searchRulesParams.ts b/clients/algoliasearch-client-javascript/client-search/model/searchRulesParams.ts new file mode 100644 index 0000000000..164b30ba4d --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/searchRulesParams.ts @@ -0,0 +1,32 @@ +import type { Anchoring } from './anchoring'; + +/** + * Parameters for the search. + */ +export type SearchRulesParams = { + /** + * Full text query. + */ + query?: string; + anchoring?: Anchoring; + /** + * Restricts matches to contextual rules with a specific context (exact match). + */ + context?: string; + /** + * Requested page (zero-based). + */ + page?: number; + /** + * Maximum number of hits in a page. Minimum is 1, maximum is 1000. + */ + hitsPerPage?: number; + /** + * When specified, restricts matches to rules with a specific enabled status. When absent (default), all rules are retrieved, regardless of their enabled status. + */ + enabled?: boolean; + /** + * A mapping of requestOptions to send along with the request. + */ + requestOptions?: Array<{ [key: string]: Record }>; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/searchRulesResponse.ts b/clients/algoliasearch-client-javascript/client-search/model/searchRulesResponse.ts new file mode 100644 index 0000000000..ce4f100e43 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/searchRulesResponse.ts @@ -0,0 +1,20 @@ +import type { Rule } from './rule'; + +export type SearchRulesResponse = { + /** + * Fetched rules. + */ + hits: Rule[]; + /** + * Number of fetched rules. + */ + nbHits: number; + /** + * Current page. + */ + page: number; + /** + * Number of pages. + */ + nbPages: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/searchUserIdsResponse.ts b/clients/algoliasearch-client-javascript/client-search/model/searchUserIdsResponse.ts index 521512b5f3..ffbfc31ee9 100644 --- a/clients/algoliasearch-client-javascript/client-search/model/searchUserIdsResponse.ts +++ b/clients/algoliasearch-client-javascript/client-search/model/searchUserIdsResponse.ts @@ -14,7 +14,7 @@ export type SearchUserIdsResponse = { */ page: number; /** - * Set the number of hits per page. + * Maximum number of hits in a page. Minimum is 1, maximum is 1000. */ hitsPerPage: number; /** diff --git a/clients/algoliasearch-client-javascript/client-search/model/timeRange.ts b/clients/algoliasearch-client-javascript/client-search/model/timeRange.ts new file mode 100644 index 0000000000..2c0a2444fd --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/timeRange.ts @@ -0,0 +1,10 @@ +export type TimeRange = { + /** + * Lower bound of the time range (Unix timestamp). + */ + from: number; + /** + * Upper bound of the time range (Unix timestamp). + */ + until: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponse.ts b/clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponse.ts new file mode 100644 index 0000000000..6abeef1c93 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponse.ts @@ -0,0 +1,14 @@ +export type UpdatedRuleResponse = { + /** + * Unique identifier of the object. + */ + objectID: string; + /** + * Date of last update (ISO-8601 format). + */ + updatedAt: Date; + /** + * TaskID of the indexing task to wait for. + */ + taskID: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponseWithoutObjectID.ts b/clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponseWithoutObjectID.ts new file mode 100644 index 0000000000..cb245abbdf --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-search/model/updatedRuleResponseWithoutObjectID.ts @@ -0,0 +1,10 @@ +export type UpdatedRuleResponseWithoutObjectID = { + /** + * Date of last update (ISO-8601 format). + */ + updatedAt: Date; + /** + * TaskID of the indexing task to wait for. + */ + taskID: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-search/src/searchApi.ts b/clients/algoliasearch-client-javascript/client-search/src/searchApi.ts index ca724d260a..dc63e00ad4 100644 --- a/clients/algoliasearch-client-javascript/client-search/src/searchApi.ts +++ b/clients/algoliasearch-client-javascript/client-search/src/searchApi.ts @@ -29,12 +29,15 @@ import type { OperationIndexObject } from '../model/operationIndexObject'; import type { OperationIndexResponse } from '../model/operationIndexResponse'; import type { RemoveUserIdResponse } from '../model/removeUserIdResponse'; import type { ReplaceSourceResponse } from '../model/replaceSourceResponse'; +import type { Rule } from '../model/rule'; import type { SaveObjectResponse } from '../model/saveObjectResponse'; import type { SaveSynonymResponse } from '../model/saveSynonymResponse'; import type { SaveSynonymsResponse } from '../model/saveSynonymsResponse'; import type { SearchParams } from '../model/searchParams'; import type { SearchParamsAsString } from '../model/searchParamsAsString'; import type { SearchResponse } from '../model/searchResponse'; +import type { SearchRulesParams } from '../model/searchRulesParams'; +import type { SearchRulesResponse } from '../model/searchRulesResponse'; import type { SearchSynonymsResponse } from '../model/searchSynonymsResponse'; import type { SearchUserIdsObject } from '../model/searchUserIdsObject'; import type { SearchUserIdsResponse } from '../model/searchUserIdsResponse'; @@ -42,6 +45,8 @@ import type { SetSettingsResponse } from '../model/setSettingsResponse'; import type { Source } from '../model/source'; import type { SynonymHit } from '../model/synonymHit'; import type { UpdateApiKeyResponse } from '../model/updateApiKeyResponse'; +import type { UpdatedRuleResponse } from '../model/updatedRuleResponse'; +import type { UpdatedRuleResponseWithoutObjectID } from '../model/updatedRuleResponseWithoutObjectID'; import type { UserId } from '../model/userId'; import { Transporter } from '../utils/Transporter'; import { shuffle } from '../utils/helpers'; @@ -327,6 +332,61 @@ export class SearchApi { return this.sendRequest(request, requestOptions); } + /** + * Create or update a batch of Rules. + * + * @summary Batch Rules. + * @param indexName - The index in which to perform the request. + * @param rule - The rule. + * @param forwardToReplicas - When true, changes are also propagated to replicas of the given indexName. + * @param clearExistingRules - When true, existing Rules are cleared before adding this batch. When false, existing Rules are kept. + */ + batchRules( + indexName: string, + rule: Rule[], + forwardToReplicas?: boolean, + clearExistingRules?: boolean + ): Promise { + const path = '/1/indexes/{indexName}/rules/batch'.replace( + '{indexName}', + encodeURIComponent(String(indexName)) + ); + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if (indexName === null || indexName === undefined) { + throw new Error( + 'Required parameter indexName was null or undefined when calling batchRules.' + ); + } + + if (rule === null || rule === undefined) { + throw new Error( + 'Required parameter rule was null or undefined when calling batchRules.' + ); + } + + if (forwardToReplicas !== undefined) { + queryParameters.forwardToReplicas = forwardToReplicas.toString(); + } + + if (clearExistingRules !== undefined) { + queryParameters.clearExistingRules = clearExistingRules.toString(); + } + + const request: Request = { + method: 'POST', + path, + data: rule, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } /** * Remove all synonyms from an index. * @@ -367,6 +427,46 @@ export class SearchApi { return this.sendRequest(request, requestOptions); } + /** + * Delete all Rules in the index. + * + * @summary Clear Rules. + * @param indexName - The index in which to perform the request. + * @param forwardToReplicas - When true, changes are also propagated to replicas of the given indexName. + */ + clearRules( + indexName: string, + forwardToReplicas?: boolean + ): Promise { + const path = '/1/indexes/{indexName}/rules/clear'.replace( + '{indexName}', + encodeURIComponent(String(indexName)) + ); + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if (indexName === null || indexName === undefined) { + throw new Error( + 'Required parameter indexName was null or undefined when calling clearRules.' + ); + } + + if (forwardToReplicas !== undefined) { + queryParameters.forwardToReplicas = forwardToReplicas.toString(); + } + + const request: Request = { + method: 'POST', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } /** * Delete an existing API Key. * @@ -431,6 +531,53 @@ export class SearchApi { return this.sendRequest(request, requestOptions); } + /** + * Delete the Rule with the specified objectID. + * + * @summary Delete a rule. + * @param indexName - The index in which to perform the request. + * @param objectID - Unique identifier of an object. + * @param forwardToReplicas - When true, changes are also propagated to replicas of the given indexName. + */ + deleteRule( + indexName: string, + objectID: string, + forwardToReplicas?: boolean + ): Promise { + const path = '/1/indexes/{indexName}/rules/{objectID}' + .replace('{indexName}', encodeURIComponent(String(indexName))) + .replace('{objectID}', encodeURIComponent(String(objectID))); + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if (indexName === null || indexName === undefined) { + throw new Error( + 'Required parameter indexName was null or undefined when calling deleteRule.' + ); + } + + if (objectID === null || objectID === undefined) { + throw new Error( + 'Required parameter objectID was null or undefined when calling deleteRule.' + ); + } + + if (forwardToReplicas !== undefined) { + queryParameters.forwardToReplicas = forwardToReplicas.toString(); + } + + const request: Request = { + method: 'DELETE', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } /** * Remove a single source from the list of allowed sources. * @@ -587,6 +734,44 @@ export class SearchApi { return this.sendRequest(request, requestOptions); } + /** + * Retrieve the Rule with the specified objectID. + * + * @summary Get a rule. + * @param indexName - The index in which to perform the request. + * @param objectID - Unique identifier of an object. + */ + getRule(indexName: string, objectID: string): Promise { + const path = '/1/indexes/{indexName}/rules/{objectID}' + .replace('{indexName}', encodeURIComponent(String(indexName))) + .replace('{objectID}', encodeURIComponent(String(objectID))); + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if (indexName === null || indexName === undefined) { + throw new Error( + 'Required parameter indexName was null or undefined when calling getRule.' + ); + } + + if (objectID === null || objectID === undefined) { + throw new Error( + 'Required parameter objectID was null or undefined when calling getRule.' + ); + } + + const request: Request = { + method: 'GET', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } /** * Retrieve settings of a given indexName. * @@ -1111,6 +1296,62 @@ export class SearchApi { return this.sendRequest(request, requestOptions); } + /** + * Create or update the Rule with the specified objectID. + * + * @summary Save/Update a rule. + * @param indexName - The index in which to perform the request. + * @param objectID - Unique identifier of an object. + * @param rule - The rule. + * @param forwardToReplicas - When true, changes are also propagated to replicas of the given indexName. + */ + saveRule( + indexName: string, + objectID: string, + rule: Rule, + forwardToReplicas?: boolean + ): Promise { + const path = '/1/indexes/{indexName}/rules/{objectID}' + .replace('{indexName}', encodeURIComponent(String(indexName))) + .replace('{objectID}', encodeURIComponent(String(objectID))); + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if (indexName === null || indexName === undefined) { + throw new Error( + 'Required parameter indexName was null or undefined when calling saveRule.' + ); + } + + if (objectID === null || objectID === undefined) { + throw new Error( + 'Required parameter objectID was null or undefined when calling saveRule.' + ); + } + + if (rule === null || rule === undefined) { + throw new Error( + 'Required parameter rule was null or undefined when calling saveRule.' + ); + } + + if (forwardToReplicas !== undefined) { + queryParameters.forwardToReplicas = forwardToReplicas.toString(); + } + + const request: Request = { + method: 'PUT', + path, + data: rule, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } /** * Create a new synonym object or update the existing synonym object with the given object ID. * @@ -1268,6 +1509,49 @@ export class SearchApi { return this.sendRequest(request, requestOptions); } + /** + * Search for rules matching various criteria. + * + * @summary Search for rules. + * @param indexName - The index in which to perform the request. + * @param searchRulesParams - The searchRulesParams. + */ + searchRules( + indexName: string, + searchRulesParams: SearchRulesParams + ): Promise { + const path = '/1/indexes/{indexName}/rules/search'.replace( + '{indexName}', + encodeURIComponent(String(indexName)) + ); + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if (indexName === null || indexName === undefined) { + throw new Error( + 'Required parameter indexName was null or undefined when calling searchRules.' + ); + } + + if (searchRulesParams === null || searchRulesParams === undefined) { + throw new Error( + 'Required parameter searchRulesParams was null or undefined when calling searchRules.' + ); + } + + const request: Request = { + method: 'POST', + path, + data: searchRulesParams, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } /** * Search or browse all synonyms, optionally filtering them by type. * diff --git a/openapitools.json b/openapitools.json index 87b241293d..68b579e23b 100644 --- a/openapitools.json +++ b/openapitools.json @@ -13,7 +13,7 @@ "gitHost": "algolia", "gitUserId": "algolia", "gitRepoId": "algoliasearch-client-javascript", - "reservedWordsMappings": "queryParameters=queryParameters", + "reservedWordsMappings": "queryParameters=queryParameters,requestOptions=requestOptions", "additionalProperties": { "modelPropertyNaming": "original", "supportsES6": true, diff --git a/specs/common/parameters.yml b/specs/common/parameters.yml index 9f51a80c21..38b93aff02 100644 --- a/specs/common/parameters.yml +++ b/specs/common/parameters.yml @@ -126,3 +126,18 @@ indexName: type: string example: products description: The Algolia index name. + +hitsPerPage: + type: integer + default: 20 + description: Maximum number of hits in a page. Minimum is 1, maximum is 1000. + +query: + type: string + description: Full text query. + default: '' + +page: + type: integer + default: 0 + description: Requested page (zero-based). diff --git a/specs/search/paths/multiclusters/searchUserIds.yml b/specs/search/paths/multiclusters/searchUserIds.yml index ba8d8aab88..e0e40d8746 100644 --- a/specs/search/paths/multiclusters/searchUserIds.yml +++ b/specs/search/paths/multiclusters/searchUserIds.yml @@ -81,7 +81,7 @@ post: page: $ref: '../../common/schemas/SearchResponse.yml#/baseSearchResponse/properties/page' hitsPerPage: - $ref: '../../common/schemas/SearchResponse.yml#/baseSearchResponse/properties/hitsPerPage' + $ref: '../../../common/parameters.yml#/hitsPerPage' updatedAt: $ref: '../../../common/parameters.yml#/updatedAt' required: diff --git a/specs/search/paths/rules/batchRules.yml b/specs/search/paths/rules/batchRules.yml index 6adb517bf4..3e66e882ce 100644 --- a/specs/search/paths/rules/batchRules.yml +++ b/specs/search/paths/rules/batchRules.yml @@ -1 +1,34 @@ post: + tags: + - search + operationId: batchRules + summary: Batch Rules. + description: Create or update a batch of Rules. + parameters: + - $ref: '../../../common/parameters.yml#/IndexName' + - $ref: '../../../common/parameters.yml#/ForwardToReplicas' + - $ref: 'common/parameters.yml#/ClearExistingRules' + requestBody: + required: true + content: + application/json: + schema: + type: array + description: Rules to add. + items: + $ref: 'common/schemas.yml#/rule' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: './common/schemas.yml#/updatedRuleResponseWithoutObjectID' + '400': + $ref: '../../../common/responses/BadRequest.yml' + '402': + $ref: '../../../common/responses/FeatureNotEnabled.yml' + '403': + $ref: '../../../common/responses/MethodNotAllowed.yml' + '404': + $ref: '../../../common/responses/IndexNotFound.yml' diff --git a/specs/search/paths/rules/clearRules.yml b/specs/search/paths/rules/clearRules.yml index 6adb517bf4..a6b1d3255c 100644 --- a/specs/search/paths/rules/clearRules.yml +++ b/specs/search/paths/rules/clearRules.yml @@ -1 +1,24 @@ post: + tags: + - search + operationId: clearRules + summary: Clear Rules. + description: Delete all Rules in the index. + parameters: + - $ref: '../../../common/parameters.yml#/IndexName' + - $ref: '../../../common/parameters.yml#/ForwardToReplicas' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: './common/schemas.yml#/updatedRuleResponseWithoutObjectID' + '400': + $ref: '../../../common/responses/BadRequest.yml' + '402': + $ref: '../../../common/responses/FeatureNotEnabled.yml' + '403': + $ref: '../../../common/responses/MethodNotAllowed.yml' + '404': + $ref: '../../../common/responses/IndexNotFound.yml' diff --git a/specs/search/paths/rules/common/parameters.yml b/specs/search/paths/rules/common/parameters.yml new file mode 100644 index 0000000000..df87cba731 --- /dev/null +++ b/specs/search/paths/rules/common/parameters.yml @@ -0,0 +1,7 @@ +ClearExistingRules: + in: query + name: clearExistingRules + required: false + schema: + type: boolean + description: When true, existing Rules are cleared before adding this batch. When false, existing Rules are kept. diff --git a/specs/search/paths/rules/common/schemas.yml b/specs/search/paths/rules/common/schemas.yml new file mode 100644 index 0000000000..9c9d0e2101 --- /dev/null +++ b/specs/search/paths/rules/common/schemas.yml @@ -0,0 +1,182 @@ +rule: + type: object + description: Rule object. + additionalProperties: false + properties: + objectID: + $ref: '../../../../common/parameters.yml#/objectID' + conditions: + type: array + description: A list of conditions that should apply to activate a Rule. You can use up to 25 conditions per Rule. + items: + $ref: '#/condition' + consequence: + $ref: '#/consequence' + description: + type: string + description: This field is intended for Rule management purposes, in particular to ease searching for Rules and presenting them to human readers. It’s not interpreted by the API. + enabled: + type: boolean + default: true + description: Whether the Rule is enabled. Disabled Rules remain in the index, but aren’t applied at query time. + validity: + type: array + description: By default, Rules are permanently valid. When validity periods are specified, the Rule applies only during those periods; it’s ignored the rest of the time. The list must not be empty. + items: + $ref: '#/timeRange' + required: + - objectID + - consequence + +condition: + type: object + additionalProperties: false + properties: + pattern: + type: string + description: Query pattern syntax + anchoring: + $ref: '#/anchoring' + alternatives: + type: boolean + description: Whether the pattern matches on plurals, synonyms, and typos. + default: false + context: + type: string + description: 'Rule context format: [A-Za-z0-9_-]+).' + +anchoring: + type: string + description: Whether the pattern parameter must match the beginning or the end of the query string, or both, or none. + enum: ['is', 'startsWith', 'endsWith', 'contains'] + +consequence: + type: object + description: Consequence of the Rule. + additionalProperties: false + properties: + params: + $ref: '#/params' + promote: + type: array + description: Objects to promote as hits. + items: + $ref: '#/promote' + filterPromotes: + type: boolean + default: false + description: Only use in combination with the promote consequence. When true, promoted results will be restricted to match the filters of the current search. When false, the promoted results will show up regardless of the filters. + hide: + type: array + description: Objects to hide from hits. Each object must contain an objectID field. By default, you can hide up to 50 items per rule. + items: + type: object + description: Unique identifier of the object to hide. + additionalProperties: false + properties: + objectID: + $ref: '../../../../common/parameters.yml#/objectID' + required: + - objectID + userData: + type: object + description: Custom JSON object that will be appended to the userData array in the response. This object isn’t interpreted by the API. It’s limited to 1kB of minified JSON. + additionalProperties: true + +promote: + type: object + description: Object to promote as hits. + additionalProperties: false + properties: + objectID: + type: string + description: Unique identifier of the object to promote. + objectIDs: + type: array + description: Array of unique identifiers of the objects to promote. + items: + type: string + position: + type: integer + description: The position to promote the objects to (zero-based). If you pass objectIDs, the objects are placed at this position as a group. For example, if you pass four objectIDs to position 0, the objects take the first four positions. + required: + - position + +params: + type: object + description: Additional search parameters. Any valid search parameter is allowed. + additionalProperties: false + properties: + query: + type: string + description: Query string. + automaticFacetFilters: + type: array + description: Names of facets to which automatic filtering must be applied; they must match the facet name of a facet value placeholder in the query pattern. + items: + $ref: '#/automaticFacetFilter' + automaticOptionalFacetFilters: + type: object + description: Same syntax as automaticFacetFilters, but the engine treats the filters as optional. + items: + $ref: '#/automaticFacetFilter' + +automaticFacetFilter: + type: object + description: Automatic facet Filter. + additionalProperties: false + properties: + facet: + type: string + description: Attribute to filter on. This must match a facet placeholder in the Rule’s pattern. + score: + type: integer + default: 1 + description: Score for the filter. Typically used for optional or disjunctive filters. + disjunctive: + type: boolean + default: false + description: Whether the filter is disjunctive (true) or conjunctive (false). + required: + - facet + +timeRange: + type: object + additionalProperties: false + properties: + from: + type: integer + description: Lower bound of the time range (Unix timestamp). + until: + type: integer + description: Upper bound of the time range (Unix timestamp). + required: + - from + - until + +updatedRuleResponse: + type: object + additionalProperties: false + properties: + objectID: + $ref: '../../../../common/parameters.yml#/objectID' + updatedAt: + $ref: '../../../../common/parameters.yml#/updatedAt' + taskID: + $ref: '../../../../common/parameters.yml#/taskID' + required: + - objectID + - updatedAt + - taskID + +updatedRuleResponseWithoutObjectID: + type: object + additionalProperties: false + properties: + updatedAt: + $ref: '../../../../common/parameters.yml#/updatedAt' + taskID: + $ref: '../../../../common/parameters.yml#/taskID' + required: + - updatedAt + - taskID diff --git a/specs/search/paths/rules/rule.yml b/specs/search/paths/rules/rule.yml index 9157e5af9a..6060e1a22c 100644 --- a/specs/search/paths/rules/rule.yml +++ b/specs/search/paths/rules/rule.yml @@ -1,3 +1,82 @@ put: + tags: + - search + operationId: saveRule + summary: Save/Update a rule. + description: Create or update the Rule with the specified objectID. + parameters: + - $ref: '../../../common/parameters.yml#/IndexName' + - $ref: '../../../common/parameters.yml#/ObjectID' + - $ref: '../../../common/parameters.yml#/ForwardToReplicas' + requestBody: + required: true + content: + application/json: + schema: + $ref: 'common/schemas.yml#/rule' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: './common/schemas.yml#/updatedRuleResponse' + '400': + $ref: '../../../common/responses/BadRequest.yml' + '402': + $ref: '../../../common/responses/FeatureNotEnabled.yml' + '403': + $ref: '../../../common/responses/MethodNotAllowed.yml' + '404': + $ref: '../../../common/responses/IndexNotFound.yml' + get: + tags: + - search + operationId: getRule + summary: Get a rule. + description: Retrieve the Rule with the specified objectID. + parameters: + - $ref: '../../../common/parameters.yml#/IndexName' + - $ref: '../../../common/parameters.yml#/ObjectID' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: './common/schemas.yml#/rule' + '400': + $ref: '../../../common/responses/BadRequest.yml' + '402': + $ref: '../../../common/responses/FeatureNotEnabled.yml' + '403': + $ref: '../../../common/responses/MethodNotAllowed.yml' + '404': + $ref: '../../../common/responses/IndexNotFound.yml' + delete: + tags: + - search + operationId: deleteRule + summary: Delete a rule. + description: Delete the Rule with the specified objectID. + parameters: + - $ref: '../../../common/parameters.yml#/IndexName' + - $ref: '../../../common/parameters.yml#/ObjectID' + - $ref: '../../../common/parameters.yml#/ForwardToReplicas' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: './common/schemas.yml#/updatedRuleResponseWithoutObjectID' + '400': + $ref: '../../../common/responses/BadRequest.yml' + '402': + $ref: '../../../common/responses/FeatureNotEnabled.yml' + '403': + $ref: '../../../common/responses/MethodNotAllowed.yml' + '404': + $ref: '../../../common/responses/IndexNotFound.yml' diff --git a/specs/search/paths/rules/searchRules.yml b/specs/search/paths/rules/searchRules.yml index 6adb517bf4..25654a99fe 100644 --- a/specs/search/paths/rules/searchRules.yml +++ b/specs/search/paths/rules/searchRules.yml @@ -1 +1,77 @@ post: + tags: + - search + operationId: searchRules + summary: Search for rules. + description: Search for rules matching various criteria. + parameters: + - $ref: '../../../common/parameters.yml#/IndexName' + requestBody: + required: true + content: + application/json: + schema: + type: object + title: searchRulesParams + description: Parameters for the search. + additionalProperties: false + properties: + query: + $ref: '../../../common/parameters.yml#/query' + anchoring: + $ref: './common/schemas.yml#/anchoring' + context: + type: string + description: Restricts matches to contextual rules with a specific context (exact match). + page: + $ref: '../../../common/parameters.yml#/page' + hitsPerPage: + $ref: '../../../common/parameters.yml#/hitsPerPage' + enabled: + type: boolean + default: null + description: When specified, restricts matches to rules with a specific enabled status. When absent (default), all rules are retrieved, regardless of their enabled status. + requestOptions: + type: array + description: A mapping of requestOptions to send along with the request. + items: + type: object + description: Request Option. + additionalProperties: true + responses: + '200': + description: OK + content: + application/json: + schema: + title: searchRulesResponse + type: object + additionalProperties: false + required: + - hits + - nbHits + - page + - nbPages + properties: + hits: + type: array + description: Fetched rules. + items: + $ref: 'common/schemas.yml#/rule' + nbHits: + type: integer + description: Number of fetched rules. + page: + type: integer + description: Current page. + nbPages: + type: integer + description: Number of pages. + '400': + $ref: '../../../common/responses/BadRequest.yml' + '402': + $ref: '../../../common/responses/FeatureNotEnabled.yml' + '403': + $ref: '../../../common/responses/MethodNotAllowed.yml' + '404': + $ref: '../../../common/responses/IndexNotFound.yml' diff --git a/specs/search/spec.yml b/specs/search/spec.yml index c8d19a52ec..38638f8f08 100644 --- a/specs/search/spec.yml +++ b/specs/search/spec.yml @@ -78,14 +78,14 @@ paths: # ####################### # ### Rules Endpoints ### # ####################### - # /1/indexes/{indexName}/rules/{objectID}: - # $ref: './paths/rules/rule.yml' - # /1/indexes/{indexName}/rules/batch: - # $ref: './paths/rules/batchRules.yml' - # /1/indexes/{indexName}/rules/clear: - # $ref: './paths/rules/clearRules.yml' - # /1/indexes/{indexName}/rules/search: - # $ref: './paths/rules/searchRules.yml' + /1/indexes/{indexName}/rules/{objectID}: + $ref: './paths/rules/rule.yml' + /1/indexes/{indexName}/rules/batch: + $ref: './paths/rules/batchRules.yml' + /1/indexes/{indexName}/rules/clear: + $ref: './paths/rules/clearRules.yml' + /1/indexes/{indexName}/rules/search: + $ref: './paths/rules/searchRules.yml' # ############################## # ### Dictionaries Endpoints ### diff --git a/tests/CTS/clients/search/batchRules.json b/tests/CTS/clients/search/batchRules.json new file mode 100644 index 0000000000..507de4d6e4 --- /dev/null +++ b/tests/CTS/clients/search/batchRules.json @@ -0,0 +1,67 @@ +[ + { + "method": "batchRules", + "parameters": [ + "indexName", + [ + { + "objectID": "a-rule-id", + "conditions": [{ + "pattern": "smartphone", + "anchoring": "contains" + }], + "consequence": { + "params": { + "filters": "category:smartphone" + } + } + }, + { + "objectID": "a-second-rule-id", + "conditions": [{ + "pattern": "apple", + "anchoring": "contains" + }], + "consequence": { + "params": { + "filters": "brand:apple" + } + } + } + ], + true, + true + ], + "request": { + "path": "/1/indexes/indexName/rules/batch", + "method": "POST", + "data": [ + { + "objectID": "a-rule-id", + "conditions": [{ + "pattern": "smartphone", + "anchoring": "contains" + }], + "consequence": { + "params": { + "filters": "category:smartphone" + } + } + }, + { + "objectID": "a-second-rule-id", + "conditions": [{ + "pattern": "apple", + "anchoring": "contains" + }], + "consequence": { + "params": { + "filters": "brand:apple" + } + } + } + ] + } + } +] + \ No newline at end of file diff --git a/tests/CTS/clients/search/clearRules.json b/tests/CTS/clients/search/clearRules.json new file mode 100644 index 0000000000..0a205332e1 --- /dev/null +++ b/tests/CTS/clients/search/clearRules.json @@ -0,0 +1,11 @@ +[ + { + "method": "clearRules", + "parameters": ["indexName"], + "request": { + "path": "/1/indexes/indexName/rules/clear", + "method": "POST" + } + } +] + \ No newline at end of file diff --git a/tests/CTS/clients/search/deleteRule.json b/tests/CTS/clients/search/deleteRule.json new file mode 100644 index 0000000000..36bf62d073 --- /dev/null +++ b/tests/CTS/clients/search/deleteRule.json @@ -0,0 +1,11 @@ +[ + { + "method": "deleteRule", + "parameters": ["indexName", "id1"], + "request": { + "path": "/1/indexes/indexName/rules/id1", + "method": "DELETE" + } + } +] + \ No newline at end of file diff --git a/tests/CTS/clients/search/getRule.json b/tests/CTS/clients/search/getRule.json new file mode 100644 index 0000000000..93203a795f --- /dev/null +++ b/tests/CTS/clients/search/getRule.json @@ -0,0 +1,11 @@ +[ + { + "method": "getRule", + "parameters": ["indexName", "id1"], + "request": { + "path": "/1/indexes/indexName/rules/id1", + "method": "GET" + } + } +] + \ No newline at end of file diff --git a/tests/CTS/clients/search/saveRule.json b/tests/CTS/clients/search/saveRule.json new file mode 100644 index 0000000000..68761da37d --- /dev/null +++ b/tests/CTS/clients/search/saveRule.json @@ -0,0 +1,39 @@ +[ + { + "method": "saveRule", + "parameters": [ + "indexName", + "id1", + { + "objectID": "id1", + "conditions": [{ + "pattern": "apple", + "anchoring": "contains" + }], + "consequence": { + "params": { + "filters": "brand:apple" + } + } + }, + true + ], + "request": { + "path": "/1/indexes/indexName/rules/id1", + "method": "PUT", + "data": { + "objectID": "id1", + "conditions": [{ + "pattern": "apple", + "anchoring": "contains" + }], + "consequence": { + "params": { + "filters": "brand:apple" + } + } + } + } + } +] + \ No newline at end of file diff --git a/tests/CTS/clients/search/searchRules.json b/tests/CTS/clients/search/searchRules.json new file mode 100644 index 0000000000..8417218673 --- /dev/null +++ b/tests/CTS/clients/search/searchRules.json @@ -0,0 +1,14 @@ +[ + { + "method": "searchRules", + "parameters": [ + "indexName", + {"query": "something"} + ], + "request": { + "path": "/1/indexes/indexName/rules/search", + "method": "POST", + "data": {"query": "something"} + } + } +] diff --git a/tests/output/javascript/search.test.ts b/tests/output/javascript/search.test.ts index 2fd84d64a3..1e64e47e2e 100644 --- a/tests/output/javascript/search.test.ts +++ b/tests/output/javascript/search.test.ts @@ -58,6 +58,14 @@ describe('Common Test Suite', () => { }); }); + test('deleteRule', async () => { + const req = await client.deleteRule('indexName', 'id1'); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/rules/id1', + method: 'DELETE', + }); + }); + test('getSynonym', async () => { const req = await client.getSynonym('indexName', 'id1'); expect(req).toMatchObject({ @@ -75,6 +83,50 @@ describe('Common Test Suite', () => { }); }); + test('getRule', async () => { + const req = await client.getRule('indexName', 'id1'); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/rules/id1', + method: 'GET', + }); + }); + + test('batchRules', async () => { + const req = await client.batchRules( + 'indexName', + [ + { + objectID: 'a-rule-id', + conditions: [{ pattern: 'smartphone', anchoring: 'contains' }], + consequence: { params: { filters: 'category:smartphone' } }, + }, + { + objectID: 'a-second-rule-id', + conditions: [{ pattern: 'apple', anchoring: 'contains' }], + consequence: { params: { filters: 'brand:apple' } }, + }, + ], + true, + true + ); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/rules/batch', + method: 'POST', + data: [ + { + objectID: 'a-rule-id', + conditions: [{ pattern: 'smartphone', anchoring: 'contains' }], + consequence: { params: { filters: 'category:smartphone' } }, + }, + { + objectID: 'a-second-rule-id', + conditions: [{ pattern: 'apple', anchoring: 'contains' }], + consequence: { params: { filters: 'brand:apple' } }, + }, + ], + }); + }); + test('updateApiKey', async () => { const req = await client.updateApiKey('myApiKey', { acl: ['search', 'addObject'], @@ -102,6 +154,15 @@ describe('Common Test Suite', () => { }); }); + test('searchRules', async () => { + const req = await client.searchRules('indexName', { query: 'something' }); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/rules/search', + method: 'POST', + data: { query: 'something' }, + }); + }); + test('clearAllSynonyms', async () => { const req = await client.clearAllSynonyms('indexName'); expect(req).toMatchObject({ @@ -110,6 +171,28 @@ describe('Common Test Suite', () => { }); }); + test('saveRule', async () => { + const req = await client.saveRule( + 'indexName', + 'id1', + { + objectID: 'id1', + conditions: [{ pattern: 'apple', anchoring: 'contains' }], + consequence: { params: { filters: 'brand:apple' } }, + }, + true + ); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/rules/id1', + method: 'PUT', + data: { + objectID: 'id1', + conditions: [{ pattern: 'apple', anchoring: 'contains' }], + consequence: { params: { filters: 'brand:apple' } }, + }, + }); + }); + test('addApiKey', async () => { const req = await client.addApiKey({ acl: ['search', 'addObject'], @@ -155,6 +238,14 @@ describe('Common Test Suite', () => { }); }); + test('clearRules', async () => { + const req = await client.clearRules('indexName'); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/rules/clear', + method: 'POST', + }); + }); + test('listApiKeys', async () => { const req = await client.listApiKeys(); expect(req).toMatchObject({