From 9f7ebcfc59cf4e394e627a207633db53e3a76ae9 Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 10 Jan 2022 16:05:09 +0100 Subject: [PATCH 1/9] create query-suggestions client spec --- openapitools.json | 21 +++++ scripts/multiplexer.sh | 3 +- specs/common/responses/InternalError.yml | 5 ++ specs/common/responses/Unauthorized.yml | 5 ++ specs/query-suggestions/common/parameters.yml | 78 +++++++++++++++++++ .../common/responses/StatusForbidden.yml | 5 ++ .../responses/StatusUnprocessableEntity.yml | 5 ++ .../common/responses/Success.yml | 16 ++++ .../common/schemas/QuerySuggestionsIndex.yml | 45 +++++++++++ .../paths/getConfigurationStatus.yml | 34 ++++++++ specs/query-suggestions/paths/getLogFile.yml | 43 ++++++++++ specs/query-suggestions/paths/qsConfig.yml | 61 +++++++++++++++ specs/query-suggestions/paths/qsConfigs.yml | 48 ++++++++++++ specs/query-suggestions/spec.yml | 23 ++++++ .../paths/getConfigurationStatus.yml | 1 - specs/query_suggestions/paths/getLogFile.yml | 1 - specs/query_suggestions/paths/qsConfig.yml | 3 - specs/query_suggestions/paths/qsConfigs.yml | 2 - specs/query_suggestions/spec.yml | 23 ------ templates/javascript/api-single.mustache | 6 ++ tests/generateCTS.ts | 4 +- tests/package.json | 1 + yarn.lock | 10 +++ 23 files changed, 411 insertions(+), 32 deletions(-) create mode 100644 specs/common/responses/InternalError.yml create mode 100644 specs/common/responses/Unauthorized.yml create mode 100644 specs/query-suggestions/common/parameters.yml create mode 100644 specs/query-suggestions/common/responses/StatusForbidden.yml create mode 100644 specs/query-suggestions/common/responses/StatusUnprocessableEntity.yml create mode 100644 specs/query-suggestions/common/responses/Success.yml create mode 100644 specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml create mode 100644 specs/query-suggestions/paths/getConfigurationStatus.yml create mode 100644 specs/query-suggestions/paths/getLogFile.yml create mode 100644 specs/query-suggestions/paths/qsConfig.yml create mode 100644 specs/query-suggestions/paths/qsConfigs.yml create mode 100644 specs/query-suggestions/spec.yml delete mode 100644 specs/query_suggestions/paths/getConfigurationStatus.yml delete mode 100644 specs/query_suggestions/paths/getLogFile.yml delete mode 100644 specs/query_suggestions/paths/qsConfig.yml delete mode 100644 specs/query_suggestions/paths/qsConfigs.yml delete mode 100644 specs/query_suggestions/spec.yml diff --git a/openapitools.json b/openapitools.json index 8cf8fe32b6..4c3aace476 100644 --- a/openapitools.json +++ b/openapitools.json @@ -107,6 +107,27 @@ "isInsightsHost": true } }, + "javascript-query-suggestions": { + "generatorName": "typescript-node", + "templateDir": "#{cwd}/templates/javascript/", + "config": "#{cwd}/openapitools.json", + "apiPackage": "src", + "output": "#{cwd}/clients/algoliasearch-client-javascript/client-query-suggestions", + "glob": "specs/query-suggestions/spec.yml", + "gitHost": "algolia", + "gitUserId": "algolia", + "gitRepoId": "algoliasearch-client-javascript", + "additionalProperties": { + "modelPropertyNaming": "original", + "supportsES6": true, + "npmName": "@algolia/client-query-suggestions", + "npmVersion": "5.0.0", + + "packageName": "@algolia/client-query-suggestions", + "hasRegionalHost": true, + "isQuerySuggestionsHost": true + } + }, "java-search": { "generatorName": "java", "templateDir": "#{cwd}/templates/java/", diff --git a/scripts/multiplexer.sh b/scripts/multiplexer.sh index fdff747776..fdcbff16bf 100755 --- a/scripts/multiplexer.sh +++ b/scripts/multiplexer.sh @@ -36,7 +36,7 @@ find_clients_and_languages() { GENERATORS=( $(cat openapitools.json | jq '."generator-cli".generators' | jq -r 'keys[]') ) for generator in "${GENERATORS[@]}"; do - local lang=${generator%-*} + local lang=${generator%%-*} local client=${generator#*-} if [[ ! ${LANGUAGES[*]} =~ $lang ]]; then @@ -73,6 +73,7 @@ for lang in "${LANGUAGE[@]}"; do for client in "${CLIENT[@]}"; do if [[ " ${GENERATORS[*]} " =~ " ${lang}-${client} " ]]; then $CMD $lang $client + echo "" fi done done diff --git a/specs/common/responses/InternalError.yml b/specs/common/responses/InternalError.yml new file mode 100644 index 0000000000..d13873a8aa --- /dev/null +++ b/specs/common/responses/InternalError.yml @@ -0,0 +1,5 @@ +description: Internal error +content: + application/json: + schema: + $ref: ../schemas/ErrorBase.yml diff --git a/specs/common/responses/Unauthorized.yml b/specs/common/responses/Unauthorized.yml new file mode 100644 index 0000000000..292fe38b4c --- /dev/null +++ b/specs/common/responses/Unauthorized.yml @@ -0,0 +1,5 @@ +description: Unauthorized +content: + application/json: + schema: + $ref: ../schemas/ErrorBase.yml diff --git a/specs/query-suggestions/common/parameters.yml b/specs/query-suggestions/common/parameters.yml new file mode 100644 index 0000000000..c7bd8abca6 --- /dev/null +++ b/specs/query-suggestions/common/parameters.yml @@ -0,0 +1,78 @@ +SourceIndex: + type: object + additionalProperties: false + required: + - indexName + properties: + indexName: + type: string + description: Source index name. + analyticsTags: + type: array + items: + type: string + default: [] + description: List of analytics tags to filter the popular searches per tag. + facets: + type: array + items: + type: object + default: [] + description: List of facets to define as categories for the query suggestions. + minHits: + type: number + description: Minimum number of hits (e.g., matching records in the source index) to generate a suggestions. + minLetters: + type: number + description: Minimum number of required letters for a suggestion to remain. + generate: + type: array + items: + type: array + items: + type: string + default: [] + description: List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). + example: [['facetA', 'facetB'], ['facetC']] + external: + type: array + items: + type: object + additionalProperties: false + required: + - query + - count + properties: + query: + type: string + count: + type: number + +SourceIndices: + type: array + items: + $ref: '#/SourceIndex' + description: List of source indices used to generate a Query Suggestions index. + +QuerySuggestionIndexParam: + type: object + additionalProperties: false + required: + - indexName + - sourceIndices + properties: + indexName: + type: string + description: Index name to target. + sourceIndices: + $ref: ../common/parameters.yml#/SourceIndex + languages: + type: array + items: + type: string + description: De-duplicate singular and plural suggestions. For example, let's say your index contains English content, and that two suggestions “shoe” and “shoes” end up in your Query Suggestions index. If the English language is configured, only the most popular of those two suggestions would remain. + exclude: + type: array + items: + type: string + description: List of words and patterns to exclude from the Query Suggestions index. diff --git a/specs/query-suggestions/common/responses/StatusForbidden.yml b/specs/query-suggestions/common/responses/StatusForbidden.yml new file mode 100644 index 0000000000..ec809c530e --- /dev/null +++ b/specs/query-suggestions/common/responses/StatusForbidden.yml @@ -0,0 +1,5 @@ +description: Status forbidden +content: + application/json: + schema: + $ref: ../../../common/schemas/ErrorBase.yml diff --git a/specs/query-suggestions/common/responses/StatusUnprocessableEntity.yml b/specs/query-suggestions/common/responses/StatusUnprocessableEntity.yml new file mode 100644 index 0000000000..7aa14e7c4d --- /dev/null +++ b/specs/query-suggestions/common/responses/StatusUnprocessableEntity.yml @@ -0,0 +1,5 @@ +description: Status unprocessable entity +content: + application/json: + schema: + $ref: ../../../common/schemas/ErrorBase.yml diff --git a/specs/query-suggestions/common/responses/Success.yml b/specs/query-suggestions/common/responses/Success.yml new file mode 100644 index 0000000000..b1ef663f41 --- /dev/null +++ b/specs/query-suggestions/common/responses/Success.yml @@ -0,0 +1,16 @@ +description: Success +content: + application/json: + schema: + type: object + additionalProperties: false + required: + - status + - message + properties: + status: + type: number + example: 200 + message: + type: string + example: Success diff --git a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml new file mode 100644 index 0000000000..92b2479ec7 --- /dev/null +++ b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml @@ -0,0 +1,45 @@ +QuerySuggestionsIndex: + type: object + additionalProperties: false + required: + - indexName + - sourceIndices + - languages + - exclude + properties: + indexName: + type: string + description: Index name to target. + sourceIndices: + $ref: '#/SourceIndicesWithReplicas' + languages: + type: array + items: + type: string + description: De-duplicate singular and plural suggestions. For example, let's say your index contains English content, and that two suggestions “shoe” and “shoes” end up in your Query Suggestions index. If the English language is configured, only the most popular of those two suggestions would remain. + exclude: + type: array + items: + type: string + description: List of words and patterns to exclude from the Query Suggestions index. + +SourceIndexWithReplicas: + allOf: + - $ref: '../parameters.yml#/SourceIndex' + - type: object + required: + - replicas + - analyticsTags + - facets + - minHits + - minLetters + - generate + - external + properties: + replicas: + type: boolean + +SourceIndicesWithReplicas: + type: array + items: + type: '#/SourceIndexWithReplicas' diff --git a/specs/query-suggestions/paths/getConfigurationStatus.yml b/specs/query-suggestions/paths/getConfigurationStatus.yml new file mode 100644 index 0000000000..291156b99b --- /dev/null +++ b/specs/query-suggestions/paths/getConfigurationStatus.yml @@ -0,0 +1,34 @@ +get: + tags: + - query-suggestions + operationId: getConfigStatus + description: > + Get the status of a Query Suggestion's index. + + The status includes whether the Query Suggestions index is currently in the process of being built, and the last build time. + parameters: + - $ref: ../../common/parameters.yml#/IndexName + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + additionalProperties: false + required: + - indexName + - isRunning + - lastBuiltAt + properties: + indexName: + type: string + isRunning: + type: boolean + lastBuiltAt: + type: string + format: data-time + '401': + $ref: ../../common/responses/Unauthorized.yml + '500': + $ref: ../../common/responses/InternalError.yml diff --git a/specs/query-suggestions/paths/getLogFile.yml b/specs/query-suggestions/paths/getLogFile.yml new file mode 100644 index 0000000000..7df78b890d --- /dev/null +++ b/specs/query-suggestions/paths/getLogFile.yml @@ -0,0 +1,43 @@ +get: + tags: + - query-suggestions + operationId: getLogFile + description: Get the log file of the last build of a single Query Suggestion index. + parameters: + - $ref: ../../common/parameters.yml#/IndexName + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + type: object + additionalProperties: false + required: + - timestamp + - level + - message + - contextLevel + properties: + timestamp: + type: string + format: date-time + description: date and time of creation of the record. + level: + type: string + enum: ['INFO', 'SKIP', 'ERROR'] + description: type of the record, can be one of three values (INFO, SKIP or ERROR). + message: + type: string + description: detailed description of what happened. + contextLevel: + type: number + description: indicates the hierarchy of the records. For example, a record with contextLevel=1 belongs to a preceding record with contextLevel=0. + '401': + $ref: ../../common/responses/Unauthorized.yml + '404': + $ref: ../../common/responses/IndexNotFound.yml + '500': + $ref: ../../common/responses/InternalError.yml diff --git a/specs/query-suggestions/paths/qsConfig.yml b/specs/query-suggestions/paths/qsConfig.yml new file mode 100644 index 0000000000..0f891ebce4 --- /dev/null +++ b/specs/query-suggestions/paths/qsConfig.yml @@ -0,0 +1,61 @@ +put: + tags: + - query-suggestions + operationId: updateConfig + description: Update the configuration of a Query Suggestions index. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../common/parameters.yml#/QuerySuggestionIndexParam + responses: + '200': + $ref: ../common/responses/Success.yml + '401': + $ref: ../../common/responses/Unauthorized.yml + '500': + $ref: ../../common/responses/InternalError.yml + +delete: + tags: + - query-suggestions + operationId: deleteConfig + description: > + Delete a configuration of a Query Suggestion's index. + + By deleting a configuraton, you stop all updates to the underlying query suggestion index. + + Note that when doing this, the underlying index does not change - existing suggestions remain untouched. + parameters: + - $ref: ../../common/parameters.yml#/IndexName + responses: + '200': + $ref: ../common/responses/Success.yml + '401': + $ref: ../../common/responses/Unauthorized.yml + '500': + $ref: ../../common/responses/InternalError.yml + +get: + tags: + - query-suggestions + operationId: getConfig + description: Get the configuration of a single Query Suggestions index. + parameters: + - $ref: ../../common/parameters.yml#/IndexName + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: ../common/schemas/QuerySuggestionsIndex.yml#/QuerySuggestionsIndex + '400': + $ref: ../../common/responses/BadRequest.yml + '401': + $ref: ../../common/responses/Unauthorized.yml + '404': + $ref: ../../common/responses/IndexNotFound.yml + '500': + $ref: ../../common/responses/InternalError.yml diff --git a/specs/query-suggestions/paths/qsConfigs.yml b/specs/query-suggestions/paths/qsConfigs.yml new file mode 100644 index 0000000000..803e9c4c3d --- /dev/null +++ b/specs/query-suggestions/paths/qsConfigs.yml @@ -0,0 +1,48 @@ +post: + tags: + - query-suggestions + operationId: createConfig + description: Create a configuration of a Query Suggestions index. There's a limit of 100 configurations per application. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../common/parameters.yml#/QuerySuggestionIndexParam + responses: + '200': + $ref: ../common/responses/Success.yml + '400': + $ref: ../../common/responses/BadRequest.yml + '401': + $ref: ../../common/responses/Unauthorized.yml + '403': + $ref: ../common/responses/StatusForbidden.yml + '422': + $ref: ../common/responses/StatusUnprocessableEntity.yml + '500': + $ref: ../../common/responses/InternalError.yml + +get: + tags: + - query-suggestions + operationId: getAllConfigs + description: > + Get all the configurations of Query Suggestions. + + For each index, you get a block of JSON with a list of its configuration settings. + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: ../common/schemas/QuerySuggestionsIndex.yml#/QuerySuggestionsIndex + '401': + $ref: ../../common/responses/Unauthorized.yml + '422': + $ref: ../common/responses/StatusUnprocessableEntity.yml + '500': + $ref: ../../common/responses/InternalError.yml diff --git a/specs/query-suggestions/spec.yml b/specs/query-suggestions/spec.yml new file mode 100644 index 0000000000..fc59726ab9 --- /dev/null +++ b/specs/query-suggestions/spec.yml @@ -0,0 +1,23 @@ +openapi: 3.0.2 +info: + title: Query Suggestions API + description: API powering the Query Suggestions feature of Algolia. + version: 0.0.1 +components: + securitySchemes: + appId: + $ref: '../common/securitySchemes.yml#/appId' + apiKey: + $ref: '../common/securitySchemes.yml#/apiKey' +security: + - appId: [] + apiKey: [] +paths: + /1/configs: + $ref: paths/qsConfigs.yml + /1/configs/{indexName}: + $ref: 'paths/qsConfig.yml' + /1/configs/{indexName}/status: + $ref: 'paths/getConfigurationStatus.yml' + /1/logs/{indexName}: + $ref: 'paths/getLogFile.yml' diff --git a/specs/query_suggestions/paths/getConfigurationStatus.yml b/specs/query_suggestions/paths/getConfigurationStatus.yml deleted file mode 100644 index 01e867a0c4..0000000000 --- a/specs/query_suggestions/paths/getConfigurationStatus.yml +++ /dev/null @@ -1 +0,0 @@ -# get: diff --git a/specs/query_suggestions/paths/getLogFile.yml b/specs/query_suggestions/paths/getLogFile.yml deleted file mode 100644 index 01e867a0c4..0000000000 --- a/specs/query_suggestions/paths/getLogFile.yml +++ /dev/null @@ -1 +0,0 @@ -# get: diff --git a/specs/query_suggestions/paths/qsConfig.yml b/specs/query_suggestions/paths/qsConfig.yml deleted file mode 100644 index 9c17774007..0000000000 --- a/specs/query_suggestions/paths/qsConfig.yml +++ /dev/null @@ -1,3 +0,0 @@ -# put: -# delete: -# get: diff --git a/specs/query_suggestions/paths/qsConfigs.yml b/specs/query_suggestions/paths/qsConfigs.yml deleted file mode 100644 index f687ddeb1f..0000000000 --- a/specs/query_suggestions/paths/qsConfigs.yml +++ /dev/null @@ -1,2 +0,0 @@ -# post: -# get: diff --git a/specs/query_suggestions/spec.yml b/specs/query_suggestions/spec.yml deleted file mode 100644 index 5afa2da29c..0000000000 --- a/specs/query_suggestions/spec.yml +++ /dev/null @@ -1,23 +0,0 @@ -# openapi: 3.0.2 -# info: -# title: Query Suggestions API -# description: API powering the Query Suggestions feature of Algolia. -# version: 0.0.1 -# components: -# securitySchemes: -# appId: -# $ref: '../common/securitySchemes.yml#/appId' -# apiKey: -# $ref: '../common/securitySchemes.yml#/apiKey' -# security: -# - appId: [] -# apiKey: [] -# paths: -# /1/configs: -# $ref: paths/qsConfigs.yml -# /1/configs/{indexName}: -# $ref: 'paths/qsConfig.yml' -# /1/configs/{indexName}/status: -# $ref: 'paths/getConfigurationStatus.yml' -# /1/logs/{indexName}: -# $ref: 'paths/getLogFile.yml' diff --git a/templates/javascript/api-single.mustache b/templates/javascript/api-single.mustache index aad28652c6..0d44808439 100644 --- a/templates/javascript/api-single.mustache +++ b/templates/javascript/api-single.mustache @@ -119,6 +119,12 @@ export class {{classname}} { } {{/isInsightsHost}} + {{#isQuerySuggestionsHost}} + public getDefaultHosts(region: string = 'us'): Host[] { + return [{ url: `query-suggestions.${region}.algolia.com`, accept: 'readWrite', protocol: 'https' }]; + } + {{/isQuerySuggestionsHost}} + public setRequest(requester: Requester): void { this.transporter.setRequester(requester); } diff --git a/tests/generateCTS.ts b/tests/generateCTS.ts index 0af2103492..f493510217 100644 --- a/tests/generateCTS.ts +++ b/tests/generateCTS.ts @@ -54,7 +54,9 @@ const packageNames: Record> = Object.entries( openapitools['generator-cli'].generators ).reduce((prev, [clientName, clientConfig]) => { const obj = prev; - const [lang, client] = clientName.split('-') as [Language, string]; + const parts = clientName.split('-'); + const lang = parts[0] as Language; + const client = parts.slice(1).join('-'); if (!(lang in prev)) { obj[lang] = {}; diff --git a/tests/package.json b/tests/package.json index 383462b08f..e578318b6b 100644 --- a/tests/package.json +++ b/tests/package.json @@ -11,6 +11,7 @@ "@algolia/client-analytics": "5.0.0", "@algolia/client-insights": "5.0.0", "@algolia/client-personalization": "5.0.0", + "@algolia/client-query-suggestions": "5.0.0", "@algolia/client-search": "5.0.0", "@algolia/recommend": "5.0.0" }, diff --git a/yarn.lock b/yarn.lock index b4ae5af5bb..73923ccc64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -58,6 +58,15 @@ __metadata: languageName: unknown linkType: soft +"@algolia/client-query-suggestions@5.0.0, @algolia/client-query-suggestions@workspace:clients/algoliasearch-client-javascript/client-query-suggestions": + version: 0.0.0-use.local + resolution: "@algolia/client-query-suggestions@workspace:clients/algoliasearch-client-javascript/client-query-suggestions" + dependencies: + "@types/node": 16.11.11 + typescript: 4.5.4 + languageName: unknown + linkType: soft + "@algolia/client-search@5.0.0, @algolia/client-search@workspace:clients/algoliasearch-client-javascript/client-search": version: 0.0.0-use.local resolution: "@algolia/client-search@workspace:clients/algoliasearch-client-javascript/client-search" @@ -5695,6 +5704,7 @@ fsevents@^2.3.2: "@algolia/client-analytics": 5.0.0 "@algolia/client-insights": 5.0.0 "@algolia/client-personalization": 5.0.0 + "@algolia/client-query-suggestions": 5.0.0 "@algolia/client-search": 5.0.0 "@algolia/recommend": 5.0.0 "@apidevtools/swagger-parser": 10.0.3 From fe65c87920ff006f79c3b4567dadb524c0f6bc54 Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 10 Jan 2022 16:05:27 +0100 Subject: [PATCH 2/9] generate the client --- .../client-query-suggestions/.gitignore | 4 + .../.openapi-generator-ignore | 4 + .../client-query-suggestions/api.ts | 3 + .../model/errorBase.ts | 6 + .../model/inlineResponse200.ts | 4 + .../model/inlineResponse2001.ts | 5 + .../model/inlineResponse2002.ts | 20 + .../client-query-suggestions/model/models.ts | 50 +++ .../model/querySuggestionIndexParam.ts | 17 + .../model/querySuggestionsIndex.ts | 17 + .../model/sourceIndex.ts | 29 ++ .../model/sourceIndexExternal.ts | 4 + .../client-query-suggestions/package.json | 24 ++ .../client-query-suggestions/src/apis.ts | 7 + .../src/querySuggestionsApi.ts | 382 ++++++++++++++++++ .../client-query-suggestions/tsconfig.json | 22 + .../utils/Response.ts | 23 ++ .../utils/StatefulHost.ts | 34 ++ .../utils/Transporter.ts | 243 +++++++++++ .../utils/cache/Cache.ts | 27 ++ .../utils/cache/MemoryCache.ts | 39 ++ .../client-query-suggestions/utils/errors.ts | 38 ++ .../client-query-suggestions/utils/helpers.ts | 117 ++++++ .../utils/requester/EchoRequester.ts | 61 +++ .../utils/requester/HttpRequester.ts | 94 +++++ .../utils/requester/Requester.ts | 8 + .../utils/stackTrace.ts | 30 ++ .../client-query-suggestions/utils/types.ts | 65 +++ 28 files changed, 1377 insertions(+) create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/.gitignore create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/.openapi-generator-ignore create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/api.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/errorBase.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse200.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2001.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2002.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionIndexParam.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/package.json create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/src/apis.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/tsconfig.json create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/Response.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/StatefulHost.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/Transporter.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/Cache.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/MemoryCache.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/errors.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/helpers.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/EchoRequester.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/HttpRequester.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/Requester.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/stackTrace.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/utils/types.ts diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/.gitignore b/clients/algoliasearch-client-javascript/client-query-suggestions/.gitignore new file mode 100644 index 0000000000..8aafcdc3fd --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.openapi-generator +.env diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/.openapi-generator-ignore b/clients/algoliasearch-client-javascript/client-query-suggestions/.openapi-generator-ignore new file mode 100644 index 0000000000..8a9707d102 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/.openapi-generator-ignore @@ -0,0 +1,4 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +git_push.sh diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/api.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/api.ts new file mode 100644 index 0000000000..59b02c4607 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/api.ts @@ -0,0 +1,3 @@ +// This is the entrypoint for the package +export * from './src/apis'; +export * from './model/models'; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/errorBase.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/errorBase.ts new file mode 100644 index 0000000000..a533aa7a15 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/errorBase.ts @@ -0,0 +1,6 @@ +/** + * Error. + */ +export type ErrorBase = { + message?: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse200.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse200.ts new file mode 100644 index 0000000000..bc691b0069 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse200.ts @@ -0,0 +1,4 @@ +export type InlineResponse200 = { + status: number; + message: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2001.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2001.ts new file mode 100644 index 0000000000..b9cb8648ab --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2001.ts @@ -0,0 +1,5 @@ +export type InlineResponse2001 = { + indexName: string; + isRunning: boolean; + lastBuiltAt: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2002.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2002.ts new file mode 100644 index 0000000000..19fd308f9b --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2002.ts @@ -0,0 +1,20 @@ +export type InlineResponse2002 = { + /** + * Date and time of creation of the record. + */ + timestamp: Date; + /** + * Type of the record, can be one of three values (INFO, SKIP or ERROR). + */ + level: InlineResponse2002Level; + /** + * Detailed description of what happened. + */ + message: string; + /** + * Indicates the hierarchy of the records. For example, a record with contextLevel=1 belongs to a preceding record with contextLevel=0. + */ + contextLevel: number; +}; + +export type InlineResponse2002Level = 'ERROR' | 'INFO' | 'SKIP'; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts new file mode 100644 index 0000000000..e40f02a42c --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts @@ -0,0 +1,50 @@ +/* eslint-disable no-param-reassign */ +import type { RequestOptions } from '../utils/types'; + +export * from './errorBase'; +export * from './inlineResponse200'; +export * from './inlineResponse2001'; +export * from './inlineResponse2002'; +export * from './querySuggestionIndexParam'; +export * from './querySuggestionsIndex'; +export * from './sourceIndex'; +export * from './sourceIndexExternal'; + +export interface Authentication { + /** + * Apply authentication settings to header and query params. + */ + applyToRequest: (requestOptions: RequestOptions) => Promise | void; +} + +export class ApiKeyAuth implements Authentication { + apiKey: string = ''; + + constructor(private location: string, private paramName: string) {} + + applyToRequest(requestOptions: RequestOptions): void { + if (this.location === 'query') { + requestOptions.queryParameters[this.paramName] = this.apiKey; + } else if ( + this.location === 'header' && + requestOptions && + requestOptions.headers + ) { + requestOptions.headers[this.paramName] = this.apiKey; + } else if ( + this.location === 'cookie' && + requestOptions && + requestOptions.headers + ) { + if (requestOptions.headers.Cookie) { + requestOptions.headers.Cookie += `; ${ + this.paramName + }=${encodeURIComponent(this.apiKey)}`; + } else { + requestOptions.headers.Cookie = `${this.paramName}=${encodeURIComponent( + this.apiKey + )}`; + } + } + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionIndexParam.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionIndexParam.ts new file mode 100644 index 0000000000..11788b506c --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionIndexParam.ts @@ -0,0 +1,17 @@ +import type { SourceIndex } from './sourceIndex'; + +export type QuerySuggestionIndexParam = { + /** + * Index name to target. + */ + indexName: string; + sourceIndices: SourceIndex; + /** + * De-duplicate singular and plural suggestions. For example, let\'s say your index contains English content, and that two suggestions “shoe” and “shoes” end up in your Query Suggestions index. If the English language is configured, only the most popular of those two suggestions would remain. + */ + languages?: string[]; + /** + * List of words and patterns to exclude from the Query Suggestions index. + */ + exclude?: string[]; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts new file mode 100644 index 0000000000..950fd1e2e2 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts @@ -0,0 +1,17 @@ +import type { SourceIndexWithReplicas } from './sourceIndexWithReplicas'; + +export type QuerySuggestionsIndex = { + /** + * Index name to target. + */ + indexName: string; + sourceIndices: SourceIndexWithReplicas[]; + /** + * De-duplicate singular and plural suggestions. For example, let\'s say your index contains English content, and that two suggestions “shoe” and “shoes” end up in your Query Suggestions index. If the English language is configured, only the most popular of those two suggestions would remain. + */ + languages: string[]; + /** + * List of words and patterns to exclude from the Query Suggestions index. + */ + exclude: string[]; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts new file mode 100644 index 0000000000..fc5288e4e1 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts @@ -0,0 +1,29 @@ +import type { SourceIndexExternal } from './sourceIndexExternal'; + +export type SourceIndex = { + /** + * Source index name. + */ + indexName: string; + /** + * List of analytics tags to filter the popular searches per tag. + */ + analyticsTags?: string[]; + /** + * List of facets to define as categories for the query suggestions. + */ + facets?: Array>; + /** + * Minimum number of hits (e.g., matching records in the source index) to generate a suggestions. + */ + minHits?: number; + /** + * Minimum number of required letters for a suggestion to remain. + */ + minLetters?: number; + /** + * List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). + */ + generate?: string[][]; + external?: SourceIndexExternal[]; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts new file mode 100644 index 0000000000..e6ba5b9cec --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts @@ -0,0 +1,4 @@ +export type SourceIndexExternal = { + query: string; + count: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/package.json b/clients/algoliasearch-client-javascript/client-query-suggestions/package.json new file mode 100644 index 0000000000..680f226100 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/package.json @@ -0,0 +1,24 @@ +{ + "name": "@algolia/client-query-suggestions", + "version": "5.0.0", + "description": "JavaScript client for @algolia/client-query-suggestions", + "repository": "algolia/algoliasearch-client-javascript", + "author": "Algolia", + "private": true, + "license": "MIT", + "main": "dist/api.js", + "types": "dist/api.d.ts", + "scripts": { + "clean": "rm -Rf node_modules/ *.js", + "build": "tsc", + "test": "yarn build && node dist/client.js" + }, + "engines": { + "node": "^16.0.0", + "yarn": "^3.0.0" + }, + "devDependencies": { + "@types/node": "16.11.11", + "typescript": "4.5.4" + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/src/apis.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/src/apis.ts new file mode 100644 index 0000000000..6356f1fc76 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/src/apis.ts @@ -0,0 +1,7 @@ +import { QuerySuggestionsApi } from './querySuggestionsApi'; + +export * from './querySuggestionsApi'; +export * from '../utils/errors'; +export { EchoRequester } from '../utils/requester/EchoRequester'; + +export const APIS = [QuerySuggestionsApi]; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts new file mode 100644 index 0000000000..a594f9c687 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts @@ -0,0 +1,382 @@ +import type { InlineResponse200 } from '../model/inlineResponse200'; +import type { InlineResponse2001 } from '../model/inlineResponse2001'; +import type { InlineResponse2002 } from '../model/inlineResponse2002'; +import { ApiKeyAuth } from '../model/models'; +import type { QuerySuggestionIndexParam } from '../model/querySuggestionIndexParam'; +import type { QuerySuggestionsIndex } from '../model/querySuggestionsIndex'; +import { Transporter } from '../utils/Transporter'; +import type { Requester } from '../utils/requester/Requester'; +import type { Headers, Host, Request, RequestOptions } from '../utils/types'; + +export enum QuerySuggestionsApiKeys { + apiKey, + appId, +} + +export class QuerySuggestionsApi { + protected authentications = { + apiKey: new ApiKeyAuth('header', 'X-Algolia-API-Key'), + appId: new ApiKeyAuth('header', 'X-Algolia-Application-Id'), + }; + + private transporter: Transporter; + + private sendRequest( + request: Request, + requestOptions: RequestOptions + ): Promise { + if (this.authentications.apiKey.apiKey) { + this.authentications.apiKey.applyToRequest(requestOptions); + } + + if (this.authentications.appId.apiKey) { + this.authentications.appId.applyToRequest(requestOptions); + } + + return this.transporter.request(request, requestOptions); + } + + constructor( + appId: string, + apiKey: string, + options?: { requester?: Requester; hosts?: Host[] } + ) { + this.setApiKey(QuerySuggestionsApiKeys.appId, appId); + this.setApiKey(QuerySuggestionsApiKeys.apiKey, apiKey); + + this.transporter = new Transporter({ + hosts: options?.hosts ?? this.getDefaultHosts(region), + baseHeaders: { + 'content-type': 'application/x-www-form-urlencoded', + }, + userAgent: 'Algolia for Javascript', + timeouts: { + connect: 2, + read: 5, + write: 30, + }, + requester: options?.requester, + }); + } + + getDefaultHosts(region: string = 'us'): Host[] { + return [ + { + url: `query-suggestions.${region}.algolia.com`, + accept: 'readWrite', + protocol: 'https', + }, + ]; + } + + setRequest(requester: Requester): void { + this.transporter.setRequester(requester); + } + + setHosts(hosts: Host[]): void { + this.transporter.setHosts(hosts); + } + + setApiKey(key: QuerySuggestionsApiKeys, value: string): void { + this.authentications[QuerySuggestionsApiKeys[key]].apiKey = value; + } + + /** + * Create a configuration of a Query Suggestions index. There\'s a limit of 100 configurations per application. + * + * @param createConfig - The createConfig parameters. + * @param createConfig.querySuggestionIndexParam - The querySuggestionIndexParam. + */ + createConfig({ + querySuggestionIndexParam, + }: CreateConfigProps): Promise { + const path = '/1/configs'; + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if ( + querySuggestionIndexParam === null || + querySuggestionIndexParam === undefined + ) { + throw new Error( + 'Required parameter querySuggestionIndexParam was null or undefined when calling createConfig.' + ); + } + + if ( + querySuggestionIndexParam.indexName === null || + querySuggestionIndexParam.indexName === undefined + ) { + throw new Error( + 'Required parameter querySuggestionIndexParam.indexName was null or undefined when calling createConfig.' + ); + } + if ( + querySuggestionIndexParam.sourceIndices === null || + querySuggestionIndexParam.sourceIndices === undefined + ) { + throw new Error( + 'Required parameter querySuggestionIndexParam.sourceIndices was null or undefined when calling createConfig.' + ); + } + + const request: Request = { + method: 'POST', + path, + data: querySuggestionIndexParam, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } + /** + * Delete a configuration of a Query Suggestion\'s index. By deleting a configuraton, you stop all updates to the underlying query suggestion index. Note that when doing this, the underlying index does not change - existing suggestions remain untouched. + * + * @param deleteConfig - The deleteConfig parameters. + * @param deleteConfig.indexName - The index in which to perform the request. + */ + deleteConfig({ indexName }: DeleteConfigProps): Promise { + const path = '/1/configs/{indexName}'.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 deleteConfig.' + ); + } + + const request: Request = { + method: 'DELETE', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } + /** + * Get all the configurations of Query Suggestions. For each index, you get a block of JSON with a list of its configuration settings. + * + * @param getAllConfigs - The getAllConfigs parameters. + */ + getAllConfigs(): Promise { + const path = '/1/configs'; + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + const request: Request = { + method: 'GET', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } + /** + * Get the configuration of a single Query Suggestions index. + * + * @param getConfig - The getConfig parameters. + * @param getConfig.indexName - The index in which to perform the request. + */ + getConfig({ indexName }: GetConfigProps): Promise { + const path = '/1/configs/{indexName}'.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 getConfig.' + ); + } + + const request: Request = { + method: 'GET', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } + /** + * Get the status of a Query Suggestion\'s index. The status includes whether the Query Suggestions index is currently in the process of being built, and the last build time. + * + * @param getConfigStatus - The getConfigStatus parameters. + * @param getConfigStatus.indexName - The index in which to perform the request. + */ + getConfigStatus({ + indexName, + }: GetConfigStatusProps): Promise { + const path = '/1/configs/{indexName}/status'.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 getConfigStatus.' + ); + } + + const request: Request = { + method: 'GET', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } + /** + * Get the log file of the last build of a single Query Suggestion index. + * + * @param getLogFile - The getLogFile parameters. + * @param getLogFile.indexName - The index in which to perform the request. + */ + getLogFile({ indexName }: GetLogFileProps): Promise { + const path = '/1/logs/{indexName}'.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 getLogFile.' + ); + } + + const request: Request = { + method: 'GET', + path, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } + /** + * Update the configuration of a Query Suggestions index. + * + * @param updateConfig - The updateConfig parameters. + * @param updateConfig.querySuggestionIndexParam - The querySuggestionIndexParam. + */ + updateConfig({ + querySuggestionIndexParam, + }: UpdateConfigProps): Promise { + const path = '/1/configs/{indexName}'; + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if ( + querySuggestionIndexParam === null || + querySuggestionIndexParam === undefined + ) { + throw new Error( + 'Required parameter querySuggestionIndexParam was null or undefined when calling updateConfig.' + ); + } + + if ( + querySuggestionIndexParam.indexName === null || + querySuggestionIndexParam.indexName === undefined + ) { + throw new Error( + 'Required parameter querySuggestionIndexParam.indexName was null or undefined when calling updateConfig.' + ); + } + if ( + querySuggestionIndexParam.sourceIndices === null || + querySuggestionIndexParam.sourceIndices === undefined + ) { + throw new Error( + 'Required parameter querySuggestionIndexParam.sourceIndices was null or undefined when calling updateConfig.' + ); + } + + const request: Request = { + method: 'PUT', + path, + data: querySuggestionIndexParam, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } +} + +export type CreateConfigProps = { + /** + * The querySuggestionIndexParam. + */ + querySuggestionIndexParam: QuerySuggestionIndexParam; +}; + +export type DeleteConfigProps = { + /** + * The index in which to perform the request. + */ + indexName: string; +}; + +export type GetConfigProps = { + /** + * The index in which to perform the request. + */ + indexName: string; +}; + +export type GetConfigStatusProps = { + /** + * The index in which to perform the request. + */ + indexName: string; +}; + +export type GetLogFileProps = { + /** + * The index in which to perform the request. + */ + indexName: string; +}; + +export type UpdateConfigProps = { + /** + * The querySuggestionIndexParam. + */ + querySuggestionIndexParam: QuerySuggestionIndexParam; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/tsconfig.json b/clients/algoliasearch-client-javascript/client-query-suggestions/tsconfig.json new file mode 100644 index 0000000000..2f72c93ccb --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": false, + "suppressImplicitAnyIndexErrors": true, + "target": "ES6", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "strict": true, + "moduleResolution": "node", + "removeComments": true, + "sourceMap": true, + "noLib": false, + "declaration": true, + "lib": ["dom", "es6", "es5", "dom.iterable", "scripthost"], + "outDir": "dist", + "typeRoots": ["node_modules/@types"], + "types": ["node"] + }, + "include": ["src", "model", "api.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/Response.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/Response.ts new file mode 100644 index 0000000000..bd22de7df9 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/Response.ts @@ -0,0 +1,23 @@ +import type { Response } from './types'; + +export function isNetworkError({ + isTimedOut, + status, +}: Omit): boolean { + return !isTimedOut && ~~status === 0; +} + +export function isRetryable({ + isTimedOut, + status, +}: Omit): boolean { + return ( + isTimedOut || + isNetworkError({ isTimedOut, status }) || + (~~(status / 100) !== 2 && ~~(status / 100) !== 4) + ); +} + +export function isSuccess({ status }: Pick): boolean { + return ~~(status / 100) === 2; +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/StatefulHost.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/StatefulHost.ts new file mode 100644 index 0000000000..162c4ed1c6 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/StatefulHost.ts @@ -0,0 +1,34 @@ +import type { Host } from './types'; + +const EXPIRATION_DELAY = 2 * 60 * 1000; + +export class StatefulHost implements Host { + url: string; + accept: 'read' | 'readWrite' | 'write'; + protocol: 'http' | 'https'; + + private lastUpdate: number; + private status: 'down' | 'timedout' | 'up'; + + constructor(host: Host, status: StatefulHost['status'] = 'up') { + this.url = host.url; + this.accept = host.accept; + this.protocol = host.protocol; + + this.status = status; + this.lastUpdate = Date.now(); + } + + isUp(): boolean { + return ( + this.status === 'up' || Date.now() - this.lastUpdate > EXPIRATION_DELAY + ); + } + + isTimedout(): boolean { + return ( + this.status === 'timedout' && + Date.now() - this.lastUpdate <= EXPIRATION_DELAY + ); + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/Transporter.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/Transporter.ts new file mode 100644 index 0000000000..48b4edebfd --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/Transporter.ts @@ -0,0 +1,243 @@ +import { isRetryable, isSuccess } from './Response'; +import { StatefulHost } from './StatefulHost'; +import type { Cache } from './cache/Cache'; +import { MemoryCache } from './cache/MemoryCache'; +import { RetryError } from './errors'; +import { + deserializeFailure, + deserializeSuccess, + serializeData, + serializeHeaders, + serializeUrl, +} from './helpers'; +import { HttpRequester } from './requester/HttpRequester'; +import type { Requester } from './requester/Requester'; +import { + stackTraceWithoutCredentials, + stackFrameWithoutCredentials, +} from './stackTrace'; +import type { + Headers, + Host, + Request, + RequestOptions, + StackFrame, + Timeouts, + Response, + EndRequest, +} from './types'; + +export class Transporter { + private hosts: Host[]; + private baseHeaders: Headers; + private hostsCache: Cache; + private userAgent: string; + private timeouts: Timeouts; + private requester: Requester; + + constructor({ + hosts, + baseHeaders, + userAgent, + timeouts, + requester = new HttpRequester(), + }: { + hosts: Host[]; + baseHeaders: Headers; + userAgent: string; + timeouts: Timeouts; + requester?: Requester; + }) { + this.hosts = hosts; + this.hostsCache = new MemoryCache(); + this.baseHeaders = baseHeaders; + this.userAgent = userAgent; + this.timeouts = timeouts; + this.requester = requester; + } + + setHosts(hosts: Host[]): void { + this.hosts = hosts; + this.hostsCache.clear(); + } + + setRequester(requester: Requester): void { + this.requester = requester; + } + + async createRetryableOptions(compatibleHosts: Host[]): Promise<{ + hosts: Host[]; + getTimeout: (retryCount: number, timeout: number) => number; + }> { + const statefulHosts = await Promise.all( + compatibleHosts.map((statelessHost) => { + return this.hostsCache.get(statelessHost, () => { + return Promise.resolve(new StatefulHost(statelessHost)); + }); + }) + ); + const hostsUp = statefulHosts.filter((host) => host.isUp()); + const hostsTimeouted = statefulHosts.filter((host) => host.isTimedout()); + + /** + * Note, we put the hosts that previously timeouted on the end of the list. + */ + const hostsAvailable = [...hostsUp, ...hostsTimeouted]; + + const hosts = hostsAvailable.length > 0 ? hostsAvailable : compatibleHosts; + + return { + hosts, + getTimeout(timeoutsCount: number, baseTimeout: number): number { + /** + * Imagine that you have 4 hosts, if timeouts will increase + * on the following way: 1 (timeouted) > 4 (timeouted) > 5 (200). + * + * Note that, the very next request, we start from the previous timeout. + * + * 5 (timeouted) > 6 (timeouted) > 7 ... + * + * This strategy may need to be reviewed, but is the strategy on the our + * current v3 version. + */ + const timeoutMultiplier = + hostsTimeouted.length === 0 && timeoutsCount === 0 + ? 1 + : hostsTimeouted.length + 3 + timeoutsCount; + + return timeoutMultiplier * baseTimeout; + }, + }; + } + + async request( + request: Request, + requestOptions: RequestOptions + ): Promise { + const stackTrace: StackFrame[] = []; + + const isRead = request.method === 'GET'; + + /** + * First we prepare the payload that do not depend from hosts. + */ + const data = serializeData(request, requestOptions); + const headers = serializeHeaders(this.baseHeaders, requestOptions); + const method = request.method; + + // On `GET`, the data is proxied to query parameters. + const dataQueryParameters: Record = isRead + ? { + ...request.data, + ...requestOptions.data, + } + : {}; + + const queryParameters = { + 'x-algolia-agent': this.userAgent, + ...dataQueryParameters, + ...requestOptions.queryParameters, + }; + + let timeoutsCount = 0; + + const retry = async ( + hosts: Host[], + getTimeout: (timeoutsCount: number, timeout: number) => number + ): Promise => { + /** + * We iterate on each host, until there is no host left. + */ + const host = hosts.pop(); + if (host === undefined) { + throw new RetryError(stackTraceWithoutCredentials(stackTrace)); + } + + let responseTimeout = requestOptions.timeout; + if (responseTimeout === undefined) { + responseTimeout = isRead ? this.timeouts.read : this.timeouts.write; + } + + const payload: EndRequest = { + data, + headers, + method, + url: serializeUrl(host, request.path, queryParameters), + connectTimeout: getTimeout(timeoutsCount, this.timeouts.connect), + responseTimeout: getTimeout(timeoutsCount, responseTimeout), + }; + + /** + * The stackFrame is pushed to the stackTrace so we + * can have information about onRetry and onFailure + * decisions. + */ + const pushToStackTrace = (response: Response): StackFrame => { + const stackFrame: StackFrame = { + request: payload, + response, + host, + triesLeft: hosts.length, + }; + + stackTrace.push(stackFrame); + + return stackFrame; + }; + + const response = await this.requester.send(payload, request); + + if (isRetryable(response)) { + const stackFrame = pushToStackTrace(response); + + // If response is a timeout, we increase the number of timeouts so we can increase the timeout later. + if (response.isTimedOut) { + timeoutsCount++; + } + /** + * Failures are individually sent to the logger, allowing + * the end user to debug / store stack frames even + * when a retry error does not happen. + */ + // eslint-disable-next-line no-console -- this will be fixed with the new `Logger` + console.log( + 'Retryable failure', + stackFrameWithoutCredentials(stackFrame) + ); + + /** + * We also store the state of the host in failure cases. If the host, is + * down it will remain down for the next 2 minutes. In a timeout situation, + * this host will be added end of the list of hosts on the next request. + */ + await this.hostsCache.set( + host, + new StatefulHost(host, response.isTimedOut ? 'timedout' : 'down') + ); + return retry(hosts, getTimeout); + } + if (isSuccess(response)) { + return deserializeSuccess(response); + } + + pushToStackTrace(response); + throw deserializeFailure(response, stackTrace); + }; + + /** + * Finally, for each retryable host perform request until we got a non + * retryable response. Some notes here: + * + * 1. The reverse here is applied so we can apply a `pop` later on => more performant. + * 2. We also get from the retryable options a timeout multiplier that is tailored + * for the current context. + */ + const compatibleHosts = this.hosts.filter( + (host) => + host.accept === 'readWrite' || + (isRead ? host.accept === 'read' : host.accept === 'write') + ); + const options = await this.createRetryableOptions(compatibleHosts); + return retry([...options.hosts].reverse(), options.getTimeout); + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/Cache.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/Cache.ts new file mode 100644 index 0000000000..625862660c --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/Cache.ts @@ -0,0 +1,27 @@ +export interface Cache { + /** + * Gets the value of the given `key`. + */ + get: ( + key: Record | string, + defaultValue: () => Promise + ) => Promise; + + /** + * Sets the given value with the given `key`. + */ + set: ( + key: Record | string, + value: TValue + ) => Promise; + + /** + * Deletes the given `key`. + */ + delete: (key: Record | string) => Promise; + + /** + * Clears the cache. + */ + clear: () => Promise; +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/MemoryCache.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/MemoryCache.ts new file mode 100644 index 0000000000..f7853f39bc --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/cache/MemoryCache.ts @@ -0,0 +1,39 @@ +import type { Cache } from './Cache'; + +export class MemoryCache implements Cache { + private cache: Record = {}; + + async get( + key: Record | string, + defaultValue: () => Promise + ): Promise { + const keyAsString = JSON.stringify(key); + + if (keyAsString in this.cache) { + return Promise.resolve(this.cache[keyAsString]); + } + + return await defaultValue(); + } + + set( + key: Record | string, + value: TValue + ): Promise { + this.cache[JSON.stringify(key)] = value; + + return Promise.resolve(value); + } + + delete(key: Record | string): Promise { + delete this.cache[JSON.stringify(key)]; + + return Promise.resolve(); + } + + clear(): Promise { + this.cache = {}; + + return Promise.resolve(); + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/errors.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/errors.ts new file mode 100644 index 0000000000..a02f3004ad --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/errors.ts @@ -0,0 +1,38 @@ +import type { Response, StackFrame } from './types'; + +class ErrorWithStackTrace extends Error { + stackTrace: StackFrame[]; + + constructor(message: string, stackTrace: StackFrame[]) { + super(message); + // the array and object should be frozen to reflect the stackTrace at the time of the error + this.stackTrace = stackTrace; + } +} + +export class RetryError extends ErrorWithStackTrace { + constructor(stackTrace: StackFrame[]) { + super( + 'Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.', + stackTrace + ); + } +} + +export class ApiError extends ErrorWithStackTrace { + status: number; + + constructor(message: string, status: number, stackTrace: StackFrame[]) { + super(message, stackTrace); + this.status = status; + } +} + +export class DeserializationError extends Error { + response: Response; + + constructor(message: string, response: Response) { + super(message); + this.response = response; + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/helpers.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/helpers.ts new file mode 100644 index 0000000000..5d64ac8868 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/helpers.ts @@ -0,0 +1,117 @@ +import { ApiError, DeserializationError } from './errors'; +import type { + Headers, + Host, + Request, + RequestOptions, + Response, + StackFrame, +} from './types'; + +export function shuffle(array: TData[]): TData[] { + const shuffledArray = array; + + for (let c = array.length - 1; c > 0; c--) { + const b = Math.floor(Math.random() * (c + 1)); + const a = array[c]; + + shuffledArray[c] = array[b]; + shuffledArray[b] = a; + } + + return shuffledArray; +} + +export function serializeUrl( + host: Host, + path: string, + queryParameters: Readonly> +): string { + const queryParametersAsString = serializeQueryParameters(queryParameters); + let url = `${host.protocol}://${host.url}/${ + path.charAt(0) === '/' ? path.substr(1) : path + }`; + + if (queryParametersAsString.length) { + url += `?${queryParametersAsString}`; + } + + return url; +} + +export function serializeQueryParameters( + parameters: Readonly> +): string { + const isObjectOrArray = (value: any): boolean => + Object.prototype.toString.call(value) === '[object Object]' || + Object.prototype.toString.call(value) === '[object Array]'; + + return Object.keys(parameters) + .map( + (key) => + `${key}=${ + isObjectOrArray(parameters[key]) + ? JSON.stringify(parameters[key]) + : parameters[key] + }` + ) + .join('&'); +} + +export function serializeData( + request: Request, + requestOptions: RequestOptions +): string | undefined { + if ( + request.method === 'GET' || + (request.data === undefined && requestOptions.data === undefined) + ) { + return undefined; + } + + const data = Array.isArray(request.data) + ? request.data + : { ...request.data, ...requestOptions.data }; + + return JSON.stringify(data); +} + +export function serializeHeaders( + baseHeaders: Headers, + requestOptions: RequestOptions +): Headers { + const headers: Headers = { + ...baseHeaders, + ...requestOptions.headers, + }; + const serializedHeaders: Headers = {}; + + Object.keys(headers).forEach((header) => { + const value = headers[header]; + serializedHeaders[header.toLowerCase()] = value; + }); + + return serializedHeaders; +} + +export function deserializeSuccess(response: Response): TObject { + try { + return JSON.parse(response.content); + } catch (e) { + throw new DeserializationError((e as Error).message, response); + } +} + +export function deserializeFailure( + { content, status }: Response, + stackFrame: StackFrame[] +): Error { + let message = content; + try { + message = JSON.parse(content).message; + } catch (e) { + // .. + } + + return new ApiError(message, status, stackFrame); +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/EchoRequester.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/EchoRequester.ts new file mode 100644 index 0000000000..ec75b37a41 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/EchoRequester.ts @@ -0,0 +1,61 @@ +import type { EndRequest, Request, Response } from '../types'; + +import { Requester } from './Requester'; + +type AdditionalContent = { + headers: Record; + connectTimeout: number; + responseTimeout: number; + userAgent?: string; + searchParams?: Record; +}; + +function searchParamsWithoutUA( + params: URLSearchParams +): AdditionalContent['searchParams'] { + const searchParams = {}; + + for (const [k, v] of params) { + if (k === 'x-algolia-agent') { + continue; + } + + searchParams[k] = v; + } + + return Object.entries(searchParams).length === 0 ? undefined : searchParams; +} + +export class EchoRequester extends Requester { + constructor(private status = 200) { + super(); + } + + send( + { headers, url, connectTimeout, responseTimeout }: EndRequest, + { data, ...originalRequest }: Request + ): Promise { + const urlSearchParams = new URL(url).searchParams; + const userAgent = urlSearchParams.get('x-algolia-agent') || undefined; + const searchParams = searchParamsWithoutUA(urlSearchParams); + const additionalContent: AdditionalContent = { + headers, + connectTimeout, + responseTimeout, + userAgent: userAgent ? encodeURI(userAgent) : undefined, + searchParams, + }; + const originalData = + data && Object.entries(data).length > 0 ? data : undefined; + + return Promise.resolve({ + content: JSON.stringify({ + ...originalRequest, + ...additionalContent, + data: originalData, + }), + isTimedOut: false, + status: this.status, + }); + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/HttpRequester.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/HttpRequester.ts new file mode 100644 index 0000000000..3697d290fb --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/HttpRequester.ts @@ -0,0 +1,94 @@ +import http from 'http'; +import https from 'https'; + +import type { EndRequest, Response } from '../types'; + +import { Requester } from './Requester'; + +// Global agents allow us to reuse the TCP protocol with multiple clients +const agentOptions = { keepAlive: true }; +const httpAgent = new http.Agent(agentOptions); +const httpsAgent = new https.Agent(agentOptions); + +export class HttpRequester extends Requester { + send(request: EndRequest): Promise { + return new Promise((resolve) => { + let responseTimeout: NodeJS.Timeout | undefined; + // eslint-disable-next-line prefer-const -- linter thinks this is not reassigned + let connectTimeout: NodeJS.Timeout | undefined; + const url = new URL(request.url); + const path = + url.search === null ? url.pathname : `${url.pathname}${url.search}`; + const options: https.RequestOptions = { + agent: url.protocol === 'https:' ? httpsAgent : httpAgent, + hostname: url.hostname, + path, + method: request.method, + headers: request.headers, + ...(url.port !== undefined ? { port: url.port || '' } : {}), + }; + + const req = (url.protocol === 'https:' ? https : http).request( + options, + (response) => { + let contentBuffers: Buffer[] = []; + + response.on('data', (chunk) => { + contentBuffers = contentBuffers.concat(chunk); + }); + + response.on('end', () => { + clearTimeout(connectTimeout as NodeJS.Timeout); + clearTimeout(responseTimeout as NodeJS.Timeout); + + resolve({ + status: response.statusCode || 0, + content: Buffer.concat(contentBuffers).toString(), + isTimedOut: false, + }); + }); + } + ); + + const createTimeout = ( + timeout: number, + content: string + ): NodeJS.Timeout => { + return setTimeout(() => { + req.destroy(); + + resolve({ + status: 0, + content, + isTimedOut: true, + }); + }, timeout * 1000); + }; + + connectTimeout = createTimeout( + request.connectTimeout, + 'Connection timeout' + ); + + req.on('error', (error) => { + clearTimeout(connectTimeout as NodeJS.Timeout); + clearTimeout(responseTimeout!); + resolve({ status: 0, content: error.message, isTimedOut: false }); + }); + + req.once('response', () => { + clearTimeout(connectTimeout as NodeJS.Timeout); + responseTimeout = createTimeout( + request.responseTimeout, + 'Socket timeout' + ); + }); + + if (request.data !== undefined) { + req.write(request.data); + } + + req.end(); + }); + } +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/Requester.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/Requester.ts new file mode 100644 index 0000000000..41c0606575 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/requester/Requester.ts @@ -0,0 +1,8 @@ +import type { EndRequest, Request, Response } from '../types'; + +export abstract class Requester { + abstract send( + request: EndRequest, + originalRequest: Request + ): Promise; +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/stackTrace.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/stackTrace.ts new file mode 100644 index 0000000000..14750a54f2 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/stackTrace.ts @@ -0,0 +1,30 @@ +import type { StackFrame } from './types'; + +export function stackTraceWithoutCredentials( + stackTrace: StackFrame[] +): StackFrame[] { + return stackTrace.map((stackFrame) => + stackFrameWithoutCredentials(stackFrame) + ); +} + +export function stackFrameWithoutCredentials( + stackFrame: StackFrame +): StackFrame { + const modifiedHeaders: Record = stackFrame.request.headers[ + 'x-algolia-api-key' + ] + ? { 'x-algolia-api-key': '*****' } + : {}; + + return { + ...stackFrame, + request: { + ...stackFrame.request, + headers: { + ...stackFrame.request.headers, + ...modifiedHeaders, + }, + }, + }; +} diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/utils/types.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/types.ts new file mode 100644 index 0000000000..d2a478c1a0 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/utils/types.ts @@ -0,0 +1,65 @@ +export type Method = 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT'; + +export type Request = { + method: Method; + path: string; + data?: Record; +}; + +export type RequestOptions = { + /** + * Custom timeout for the request. Note that, in normal situacions + * the given timeout will be applied. But the transporter layer may + * increase this timeout if there is need for it. + */ + timeout?: number; + + /** + * Custom headers for the request. This headers are + * going to be merged the transporter headers. + */ + headers?: Record; + + /** + * Custom query parameters for the request. This query parameters are + * going to be merged the transporter query parameters. + */ + queryParameters: Record; + data?: Record; +}; + +export type EndRequest = { + method: Method; + url: string; + connectTimeout: number; + responseTimeout: number; + headers: Headers; + data?: string; +}; + +export type Response = { + content: string; + isTimedOut: boolean; + status: number; +}; + +export type Headers = Record; + +export type Host = { + url: string; + accept: 'read' | 'readWrite' | 'write'; + protocol: 'http' | 'https'; +}; + +export type StackFrame = { + request: EndRequest; + response: Response; + host: Host; + triesLeft: number; +}; + +export type Timeouts = { + readonly connect: number; + readonly read: number; + readonly write: number; +}; From eaf3d96991e2daea22fc2a7402a42581b3e9b3c6 Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 10 Jan 2022 16:05:38 +0100 Subject: [PATCH 3/9] add to the CI --- .github/actions/cache/action.yml | 7 ++++++ .github/actions/setup/action.yml | 12 ++++++--- .github/workflows/check.yml | 42 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml index 8672628a1e..1d4c85e8f7 100644 --- a/.github/actions/cache/action.yml +++ b/.github/actions/cache/action.yml @@ -25,6 +25,13 @@ runs: path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-javascript/recommend/dist key: ${{ runner.os }}-js-client-recommend-${{ hashFiles('clients/algoliasearch-client-javascript/recommend/**') }} + - name: Restore built JavaScript query-suggestions client + if: ${{ inputs.job == 'cts' }} + uses: actions/cache@v2 + with: + path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-javascript/client-query-suggestions/dist + key: ${{ runner.os }}-js-client-query-suggestions-${{ hashFiles('clients/algoliasearch-client-javascript/client-query-suggestions/**') }} + - name: Restore built JavaScript personalization client if: ${{ inputs.job == 'cts' }} uses: actions/cache@v2 diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index cc7afff473..fd7dd5a0bf 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -31,7 +31,7 @@ runs: echo "::set-output name=COMMON_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/common | wc -l)" echo "::set-output name=SEARCH_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/search | wc -l)" echo "::set-output name=RECOMMEND_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/recommend | wc -l)" - echo "::set-output name=QS_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/query_suggestions | wc -l)" + echo "::set-output name=QUERY_SUGGESTIONS_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/query-suggestions | wc -l)" echo "::set-output name=PERSO_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/personalization | wc -l)" echo "::set-output name=INSIGHTS_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/insights | wc -l)" echo "::set-output name=ANALYTICS_SPECS_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- specs/analytics | wc -l)" @@ -44,6 +44,7 @@ runs: echo "::set-output name=JS_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript | wc -l)" echo "::set-output name=JS_SEARCH_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-search | wc -l)" echo "::set-output name=JS_RECOMMEND_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/recommend | wc -l)" + echo "::set-output name=JS_QUERY_SUGGESTIONS_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-query-suggestions | wc -l)" echo "::set-output name=JS_PERSO_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-personalization | wc -l)" echo "::set-output name=JS_ANALYTICS_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-analytics | wc -l)" echo "::set-output name=JS_INSIGHTS_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-insights | wc -l)" @@ -60,9 +61,9 @@ outputs: RUN_SPECS_RECOMMEND: description: Determine if the `specs_recommend` job should run value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.RECOMMEND_SPECS_CHANGED > 0 }} - RUN_SPECS_QS: - description: Determine if the `specs_qs` job should run - value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.QS_SPECS_CHANGED > 0 }} + RUN_SPECS_QUERY_SUGGESTIONS: + description: Determine if the `specs_query_suggestions` job should run + value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.QUERY_SUGGESTIONS_SPECS_CHANGED > 0 }} RUN_SPECS_PERSO: description: Determine if the `specs_perso` job should run value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.PERSO_SPECS_CHANGED > 0 }} @@ -83,6 +84,9 @@ outputs: RUN_JS_CLIENT_RECOMMEND: description: Determine if the `client_javascript_recommend` job should run value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.RECOMMEND_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_RECOMMEND_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} + RUN_JS_CLIENT_QUERY_SUGGESTIONS: + description: Determine if the `client_javascript_query_suggestions` job should run + value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.QUERY_SUGGESTIONS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_QUERY_SUGGESTIONS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} RUN_JS_CLIENT_PERSO: description: Determine if the `client_javascript_perso` job should run value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.PERSO_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_PERSO_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index bda25d1b96..4d98969b98 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -76,6 +76,22 @@ jobs: - name: Lint recommend specs run: yarn eslint --ext=yml specs/recommend + specs_query_suggestions: + runs-on: ubuntu-20.04 + needs: setup + if: ${{ always() && needs.setup.outputs.RUN_SPECS_QUERY_SUGGESTIONS == 'true' }} + steps: + - uses: actions/checkout@v2 + + - name: Restore cache + uses: ./.github/actions/cache + + - name: Checking query-suggestions specs + run: yarn build:specs query-suggestions + + - name: Lint query-suggestions specs + run: yarn eslint --ext=yml specs/query-suggestions + specs_perso: runs-on: ubuntu-20.04 needs: setup @@ -174,6 +190,31 @@ jobs: if: steps.cache.outputs.cache-hit != 'true' run: yarn build:clients javascript recommend + client_javascript_query_suggestions: + runs-on: ubuntu-20.04 + needs: [specs_query_suggestions] + if: ${{ always() && needs.setup.outputs.RUN_JS_CLIENT_QUERY_SUGGESTIONS == 'true' }} + steps: + - uses: actions/checkout@v2 + + - name: Restore cache + uses: ./.github/actions/cache + + - name: Cache query-suggestions client + id: cache + uses: actions/cache@v2 + with: + path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-javascript/client-query-suggestions/dist + key: ${{ runner.os }}-js-client-query-suggestions-${{ hashFiles('clients/algoliasearch-client-javascript/client-query-suggestions/**') }} + + - name: Generate query-suggestions client + if: steps.cache.outputs.cache-hit != 'true' + run: yarn generate javascript query-suggestions + + - name: Build query-suggestions client + if: steps.cache.outputs.cache-hit != 'true' + run: yarn build:clients javascript query-suggestions + client_javascript_perso: runs-on: ubuntu-20.04 needs: [specs_perso] @@ -281,6 +322,7 @@ jobs: needs: - client_javascript_search - client_javascript_recommend + - client_javascript_query_suggestions - client_javascript_perso - client_javascript_analytics - client_javascript_insights From 5b9772bb7064d5de28ef752d4e3757b3749458dd Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 10 Jan 2022 16:49:31 +0100 Subject: [PATCH 4/9] fix CI --- .github/workflows/check.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 4d98969b98..2765c4543b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -28,7 +28,7 @@ jobs: outputs: RUN_SPECS_SEARCH: ${{ steps.setup.outputs.RUN_SPECS_SEARCH }} RUN_SPECS_RECOMMEND: ${{ steps.setup.outputs.RUN_SPECS_RECOMMEND }} - RUN_SPECS_QS: ${{ steps.setup.outputs.RUN_SPECS_QS }} + RUN_SPECS_QUERY_SUGGESTIONS: ${{ steps.setup.outputs.RUN_SPECS_QUERY_SUGGESTIONS }} RUN_SPECS_PERSO: ${{ steps.setup.outputs.RUN_SPECS_PERSO }} RUN_SPECS_INSIGHTS: ${{ steps.setup.outputs.RUN_SPECS_INSIGHTS }} RUN_SPECS_ANALYTICS: ${{ steps.setup.outputs.RUN_SPECS_ANALYTICS }} @@ -36,6 +36,7 @@ jobs: RUN_JS_CLIENT_SEARCH: ${{ steps.setup.outputs.RUN_JS_CLIENT_SEARCH }} RUN_JS_CLIENT_RECOMMEND: ${{ steps.setup.outputs.RUN_JS_CLIENT_RECOMMEND }} + RUN_JS_CLIENT_QUERY_SUGGESTIONS: ${{ steps.setup.outputs.RUN_JS_CLIENT_QUERY_SUGGESTIONS }} RUN_JS_CLIENT_PERSO: ${{ steps.setup.outputs.RUN_JS_CLIENT_PERSO }} RUN_JS_CLIENT_ANALYTICS: ${{ steps.setup.outputs.RUN_JS_CLIENT_ANALYTICS }} RUN_JS_CLIENT_INSIGHTS: ${{ steps.setup.outputs.RUN_JS_CLIENT_INSIGHTS }} From 5df564c369ce2178612e71dfec1cf290b031dd95 Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 10 Jan 2022 18:35:22 +0100 Subject: [PATCH 5/9] fix spec --- .../{inlineResponse2002.ts => logFile.ts} | 6 +- .../client-query-suggestions/model/models.ts | 12 ++- .../model/querySuggestionsIndex.ts | 3 + ...Param.ts => querySuggestionsIndexParam.ts} | 7 +- .../querySuggestionsIndexWithIndexParam.ts | 5 + ...uerySuggestionsIndexWithIndexParamAllOf.ts | 6 ++ .../model/sourceIndex.ts | 3 + .../model/sourceIndexWithReplicas.ts | 30 ++++++ .../model/sourceIndexWithReplicasExternal.ts | 7 ++ .../{inlineResponse2001.ts => status.ts} | 2 +- ...inlineResponse200.ts => sucessResponse.ts} | 2 +- .../src/querySuggestionsApi.ts | 96 +++++++++---------- specs/query-suggestions/common/parameters.yml | 21 ++-- .../common/responses/Success.yml | 1 + .../common/schemas/QuerySuggestionsIndex.yml | 75 ++++++++++++--- .../paths/getConfigurationStatus.yml | 1 + specs/query-suggestions/paths/getLogFile.yml | 1 + specs/query-suggestions/paths/qsConfig.yml | 4 +- specs/query-suggestions/paths/qsConfigs.yml | 2 +- templates/javascript/api-single.mustache | 3 + 20 files changed, 198 insertions(+), 89 deletions(-) rename clients/algoliasearch-client-javascript/client-query-suggestions/model/{inlineResponse2002.ts => logFile.ts} (75%) rename clients/algoliasearch-client-javascript/client-query-suggestions/model/{querySuggestionIndexParam.ts => querySuggestionsIndexParam.ts} (77%) create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParamAllOf.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts create mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts rename clients/algoliasearch-client-javascript/client-query-suggestions/model/{inlineResponse2001.ts => status.ts} (66%) rename clients/algoliasearch-client-javascript/client-query-suggestions/model/{inlineResponse200.ts => sucessResponse.ts} (54%) diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2002.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/logFile.ts similarity index 75% rename from clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2002.ts rename to clients/algoliasearch-client-javascript/client-query-suggestions/model/logFile.ts index 19fd308f9b..b59bcd3cb9 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2002.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/logFile.ts @@ -1,4 +1,4 @@ -export type InlineResponse2002 = { +export type LogFile = { /** * Date and time of creation of the record. */ @@ -6,7 +6,7 @@ export type InlineResponse2002 = { /** * Type of the record, can be one of three values (INFO, SKIP or ERROR). */ - level: InlineResponse2002Level; + level: LogFileLevel; /** * Detailed description of what happened. */ @@ -17,4 +17,4 @@ export type InlineResponse2002 = { contextLevel: number; }; -export type InlineResponse2002Level = 'ERROR' | 'INFO' | 'SKIP'; +export type LogFileLevel = 'ERROR' | 'INFO' | 'SKIP'; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts index e40f02a42c..812b07f3e0 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts @@ -2,13 +2,17 @@ import type { RequestOptions } from '../utils/types'; export * from './errorBase'; -export * from './inlineResponse200'; -export * from './inlineResponse2001'; -export * from './inlineResponse2002'; -export * from './querySuggestionIndexParam'; +export * from './logFile'; export * from './querySuggestionsIndex'; +export * from './querySuggestionsIndexParam'; +export * from './querySuggestionsIndexWithIndexParam'; +export * from './querySuggestionsIndexWithIndexParamAllOf'; export * from './sourceIndex'; export * from './sourceIndexExternal'; +export * from './sourceIndexWithReplicas'; +export * from './sourceIndexWithReplicasExternal'; +export * from './status'; +export * from './sucessResponse'; export interface Authentication { /** diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts index 950fd1e2e2..7169be6b83 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndex.ts @@ -5,6 +5,9 @@ export type QuerySuggestionsIndex = { * Index name to target. */ indexName: string; + /** + * List of source indices used to generate a Query Suggestions index. + */ sourceIndices: SourceIndexWithReplicas[]; /** * De-duplicate singular and plural suggestions. For example, let\'s say your index contains English content, and that two suggestions “shoe” and “shoes” end up in your Query Suggestions index. If the English language is configured, only the most popular of those two suggestions would remain. diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionIndexParam.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexParam.ts similarity index 77% rename from clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionIndexParam.ts rename to clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexParam.ts index 11788b506c..dfb47b2070 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionIndexParam.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexParam.ts @@ -1,11 +1,10 @@ import type { SourceIndex } from './sourceIndex'; -export type QuerySuggestionIndexParam = { +export type QuerySuggestionsIndexParam = { /** - * Index name to target. + * List of source indices used to generate a Query Suggestions index. */ - indexName: string; - sourceIndices: SourceIndex; + sourceIndices: SourceIndex[]; /** * De-duplicate singular and plural suggestions. For example, let\'s say your index contains English content, and that two suggestions “shoe” and “shoes” end up in your Query Suggestions index. If the English language is configured, only the most popular of those two suggestions would remain. */ diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts new file mode 100644 index 0000000000..d5a7ddafb9 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts @@ -0,0 +1,5 @@ +import type { QuerySuggestionsIndexParam } from './querySuggestionsIndexParam'; +import type { QuerySuggestionsIndexWithIndexParamAllOf } from './querySuggestionsIndexWithIndexParamAllOf'; + +export type QuerySuggestionsIndexWithIndexParam = QuerySuggestionsIndexParam & + QuerySuggestionsIndexWithIndexParamAllOf; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParamAllOf.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParamAllOf.ts new file mode 100644 index 0000000000..8edb131507 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParamAllOf.ts @@ -0,0 +1,6 @@ +export type QuerySuggestionsIndexWithIndexParamAllOf = { + /** + * Index name to target. + */ + indexName: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts index fc5288e4e1..7a2450b90c 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndex.ts @@ -25,5 +25,8 @@ export type SourceIndex = { * List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). */ generate?: string[][]; + /** + * List of external indices to use to generate custom Query Suggestions. + */ external?: SourceIndexExternal[]; }; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts new file mode 100644 index 0000000000..821f6da437 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts @@ -0,0 +1,30 @@ +import type { SourceIndexWithReplicasExternal } from './sourceIndexWithReplicasExternal'; + +export type SourceIndexWithReplicas = { + /** + * Source index name. + */ + indexName: string; + replicas: boolean; + /** + * List of analytics tags to filter the popular searches per tag. + */ + analyticsTags: string[]; + /** + * List of facets to define as categories for the query suggestions. + */ + facets: Array>; + /** + * Minimum number of hits (e.g., matching records in the source index) to generate a suggestions. + */ + minHits: number; + /** + * Minimum number of required letters for a suggestion to remain. + */ + minLetters: number; + /** + * List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). + */ + generate: string[][]; + external: SourceIndexWithReplicasExternal[]; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts new file mode 100644 index 0000000000..4931fca48a --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts @@ -0,0 +1,7 @@ +/** + * List of external indices to use to generate custom Query Suggestions. + */ +export type SourceIndexWithReplicasExternal = { + query: string; + count: number; +}; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2001.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/status.ts similarity index 66% rename from clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2001.ts rename to clients/algoliasearch-client-javascript/client-query-suggestions/model/status.ts index b9cb8648ab..6f5b4a9290 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse2001.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/status.ts @@ -1,4 +1,4 @@ -export type InlineResponse2001 = { +export type Status = { indexName: string; isRunning: boolean; lastBuiltAt: string; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse200.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sucessResponse.ts similarity index 54% rename from clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse200.ts rename to clients/algoliasearch-client-javascript/client-query-suggestions/model/sucessResponse.ts index bc691b0069..ff2b295060 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/inlineResponse200.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sucessResponse.ts @@ -1,4 +1,4 @@ -export type InlineResponse200 = { +export type SucessResponse = { status: number; message: string; }; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts index a594f9c687..4be2399c16 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/src/querySuggestionsApi.ts @@ -1,9 +1,10 @@ -import type { InlineResponse200 } from '../model/inlineResponse200'; -import type { InlineResponse2001 } from '../model/inlineResponse2001'; -import type { InlineResponse2002 } from '../model/inlineResponse2002'; +import type { LogFile } from '../model/logFile'; import { ApiKeyAuth } from '../model/models'; -import type { QuerySuggestionIndexParam } from '../model/querySuggestionIndexParam'; import type { QuerySuggestionsIndex } from '../model/querySuggestionsIndex'; +import type { QuerySuggestionsIndexParam } from '../model/querySuggestionsIndexParam'; +import type { QuerySuggestionsIndexWithIndexParam } from '../model/querySuggestionsIndexWithIndexParam'; +import type { Status } from '../model/status'; +import type { SucessResponse } from '../model/sucessResponse'; import { Transporter } from '../utils/Transporter'; import type { Requester } from '../utils/requester/Requester'; import type { Headers, Host, Request, RequestOptions } from '../utils/types'; @@ -39,6 +40,7 @@ export class QuerySuggestionsApi { constructor( appId: string, apiKey: string, + region: 'eu' | 'us', options?: { requester?: Requester; hosts?: Host[] } ) { this.setApiKey(QuerySuggestionsApiKeys.appId, appId); @@ -85,45 +87,28 @@ export class QuerySuggestionsApi { * Create a configuration of a Query Suggestions index. There\'s a limit of 100 configurations per application. * * @param createConfig - The createConfig parameters. - * @param createConfig.querySuggestionIndexParam - The querySuggestionIndexParam. + * @param createConfig.querySuggestionsIndexWithIndexParam - The querySuggestionsIndexWithIndexParam. */ createConfig({ - querySuggestionIndexParam, - }: CreateConfigProps): Promise { + querySuggestionsIndexWithIndexParam, + }: CreateConfigProps): Promise { const path = '/1/configs'; const headers: Headers = { Accept: 'application/json' }; const queryParameters: Record = {}; if ( - querySuggestionIndexParam === null || - querySuggestionIndexParam === undefined + querySuggestionsIndexWithIndexParam === null || + querySuggestionsIndexWithIndexParam === undefined ) { throw new Error( - 'Required parameter querySuggestionIndexParam was null or undefined when calling createConfig.' - ); - } - - if ( - querySuggestionIndexParam.indexName === null || - querySuggestionIndexParam.indexName === undefined - ) { - throw new Error( - 'Required parameter querySuggestionIndexParam.indexName was null or undefined when calling createConfig.' - ); - } - if ( - querySuggestionIndexParam.sourceIndices === null || - querySuggestionIndexParam.sourceIndices === undefined - ) { - throw new Error( - 'Required parameter querySuggestionIndexParam.sourceIndices was null or undefined when calling createConfig.' + 'Required parameter querySuggestionsIndexWithIndexParam was null or undefined when calling createConfig.' ); } const request: Request = { method: 'POST', path, - data: querySuggestionIndexParam, + data: querySuggestionsIndexWithIndexParam, }; const requestOptions: RequestOptions = { @@ -139,7 +124,7 @@ export class QuerySuggestionsApi { * @param deleteConfig - The deleteConfig parameters. * @param deleteConfig.indexName - The index in which to perform the request. */ - deleteConfig({ indexName }: DeleteConfigProps): Promise { + deleteConfig({ indexName }: DeleteConfigProps): Promise { const path = '/1/configs/{indexName}'.replace( '{indexName}', encodeURIComponent(String(indexName)) @@ -225,9 +210,7 @@ export class QuerySuggestionsApi { * @param getConfigStatus - The getConfigStatus parameters. * @param getConfigStatus.indexName - The index in which to perform the request. */ - getConfigStatus({ - indexName, - }: GetConfigStatusProps): Promise { + getConfigStatus({ indexName }: GetConfigStatusProps): Promise { const path = '/1/configs/{indexName}/status'.replace( '{indexName}', encodeURIComponent(String(indexName)) @@ -259,7 +242,7 @@ export class QuerySuggestionsApi { * @param getLogFile - The getLogFile parameters. * @param getLogFile.indexName - The index in which to perform the request. */ - getLogFile({ indexName }: GetLogFileProps): Promise { + getLogFile({ indexName }: GetLogFileProps): Promise { const path = '/1/logs/{indexName}'.replace( '{indexName}', encodeURIComponent(String(indexName)) @@ -289,45 +272,48 @@ export class QuerySuggestionsApi { * Update the configuration of a Query Suggestions index. * * @param updateConfig - The updateConfig parameters. - * @param updateConfig.querySuggestionIndexParam - The querySuggestionIndexParam. + * @param updateConfig.indexName - The index in which to perform the request. + * @param updateConfig.querySuggestionsIndexParam - The querySuggestionsIndexParam. */ updateConfig({ - querySuggestionIndexParam, - }: UpdateConfigProps): Promise { - const path = '/1/configs/{indexName}'; + indexName, + querySuggestionsIndexParam, + }: UpdateConfigProps): Promise { + const path = '/1/configs/{indexName}'.replace( + '{indexName}', + encodeURIComponent(String(indexName)) + ); const headers: Headers = { Accept: 'application/json' }; const queryParameters: Record = {}; - if ( - querySuggestionIndexParam === null || - querySuggestionIndexParam === undefined - ) { + if (indexName === null || indexName === undefined) { throw new Error( - 'Required parameter querySuggestionIndexParam was null or undefined when calling updateConfig.' + 'Required parameter indexName was null or undefined when calling updateConfig.' ); } if ( - querySuggestionIndexParam.indexName === null || - querySuggestionIndexParam.indexName === undefined + querySuggestionsIndexParam === null || + querySuggestionsIndexParam === undefined ) { throw new Error( - 'Required parameter querySuggestionIndexParam.indexName was null or undefined when calling updateConfig.' + 'Required parameter querySuggestionsIndexParam was null or undefined when calling updateConfig.' ); } + if ( - querySuggestionIndexParam.sourceIndices === null || - querySuggestionIndexParam.sourceIndices === undefined + querySuggestionsIndexParam.sourceIndices === null || + querySuggestionsIndexParam.sourceIndices === undefined ) { throw new Error( - 'Required parameter querySuggestionIndexParam.sourceIndices was null or undefined when calling updateConfig.' + 'Required parameter querySuggestionsIndexParam.sourceIndices was null or undefined when calling updateConfig.' ); } const request: Request = { method: 'PUT', path, - data: querySuggestionIndexParam, + data: querySuggestionsIndexParam, }; const requestOptions: RequestOptions = { @@ -341,9 +327,9 @@ export class QuerySuggestionsApi { export type CreateConfigProps = { /** - * The querySuggestionIndexParam. + * The querySuggestionsIndexWithIndexParam. */ - querySuggestionIndexParam: QuerySuggestionIndexParam; + querySuggestionsIndexWithIndexParam: QuerySuggestionsIndexWithIndexParam; }; export type DeleteConfigProps = { @@ -376,7 +362,11 @@ export type GetLogFileProps = { export type UpdateConfigProps = { /** - * The querySuggestionIndexParam. + * The index in which to perform the request. + */ + indexName: string; + /** + * The querySuggestionsIndexParam. */ - querySuggestionIndexParam: QuerySuggestionIndexParam; + querySuggestionsIndexParam: QuerySuggestionsIndexParam; }; diff --git a/specs/query-suggestions/common/parameters.yml b/specs/query-suggestions/common/parameters.yml index c7bd8abca6..dee975ab98 100644 --- a/specs/query-suggestions/common/parameters.yml +++ b/specs/query-suggestions/common/parameters.yml @@ -47,6 +47,7 @@ SourceIndex: type: string count: type: number + description: List of external indices to use to generate custom Query Suggestions. SourceIndices: type: array @@ -54,18 +55,14 @@ SourceIndices: $ref: '#/SourceIndex' description: List of source indices used to generate a Query Suggestions index. -QuerySuggestionIndexParam: +QuerySuggestionsIndexParam: type: object additionalProperties: false required: - - indexName - sourceIndices properties: - indexName: - type: string - description: Index name to target. sourceIndices: - $ref: ../common/parameters.yml#/SourceIndex + $ref: ../common/parameters.yml#/SourceIndices languages: type: array items: @@ -76,3 +73,15 @@ QuerySuggestionIndexParam: items: type: string description: List of words and patterns to exclude from the Query Suggestions index. + +QuerySuggestionsIndexWithIndexParam: + allOf: + - $ref: '#/QuerySuggestionsIndexParam' + - type: object + additionalProperties: false + required: + - indexName + properties: + indexName: + type: string + description: Index name to target. diff --git a/specs/query-suggestions/common/responses/Success.yml b/specs/query-suggestions/common/responses/Success.yml index b1ef663f41..1f2f7fac65 100644 --- a/specs/query-suggestions/common/responses/Success.yml +++ b/specs/query-suggestions/common/responses/Success.yml @@ -3,6 +3,7 @@ content: application/json: schema: type: object + title: SucessResponse additionalProperties: false required: - status diff --git a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml index 92b2479ec7..f936a80bf3 100644 --- a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml +++ b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml @@ -24,22 +24,67 @@ QuerySuggestionsIndex: description: List of words and patterns to exclude from the Query Suggestions index. SourceIndexWithReplicas: - allOf: - - $ref: '../parameters.yml#/SourceIndex' - - type: object - required: - - replicas - - analyticsTags - - facets - - minHits - - minLetters - - generate - - external - properties: - replicas: - type: boolean + type: object + additionalProperties: false + required: + - indexName + - replicas + - analyticsTags + - facets + - minHits + - minLetters + - generate + - external + properties: + indexName: + type: string + description: Source index name. + replicas: + type: boolean + analyticsTags: + type: array + items: + type: string + default: [] + description: List of analytics tags to filter the popular searches per tag. + facets: + type: array + items: + type: object + default: [] + description: List of facets to define as categories for the query suggestions. + minHits: + type: number + description: Minimum number of hits (e.g., matching records in the source index) to generate a suggestions. + minLetters: + type: number + description: Minimum number of required letters for a suggestion to remain. + generate: + type: array + items: + type: array + items: + type: string + default: [] + description: List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). + example: [['facetA', 'facetB'], ['facetC']] + external: + type: array + items: + type: object + additionalProperties: false + required: + - query + - count + properties: + query: + type: string + count: + type: number + description: List of external indices to use to generate custom Query Suggestions. SourceIndicesWithReplicas: type: array items: - type: '#/SourceIndexWithReplicas' + $ref: '#/SourceIndexWithReplicas' + description: List of source indices used to generate a Query Suggestions index. diff --git a/specs/query-suggestions/paths/getConfigurationStatus.yml b/specs/query-suggestions/paths/getConfigurationStatus.yml index 291156b99b..d4fe5a2d9b 100644 --- a/specs/query-suggestions/paths/getConfigurationStatus.yml +++ b/specs/query-suggestions/paths/getConfigurationStatus.yml @@ -15,6 +15,7 @@ get: application/json: schema: type: object + title: Status additionalProperties: false required: - indexName diff --git a/specs/query-suggestions/paths/getLogFile.yml b/specs/query-suggestions/paths/getLogFile.yml index 7df78b890d..d5337afa53 100644 --- a/specs/query-suggestions/paths/getLogFile.yml +++ b/specs/query-suggestions/paths/getLogFile.yml @@ -14,6 +14,7 @@ get: type: array items: type: object + title: LogFile additionalProperties: false required: - timestamp diff --git a/specs/query-suggestions/paths/qsConfig.yml b/specs/query-suggestions/paths/qsConfig.yml index 0f891ebce4..175f188925 100644 --- a/specs/query-suggestions/paths/qsConfig.yml +++ b/specs/query-suggestions/paths/qsConfig.yml @@ -3,12 +3,14 @@ put: - query-suggestions operationId: updateConfig description: Update the configuration of a Query Suggestions index. + parameters: + - $ref: ../../common/parameters.yml#/IndexName requestBody: required: true content: application/json: schema: - $ref: ../common/parameters.yml#/QuerySuggestionIndexParam + $ref: ../common/parameters.yml#/QuerySuggestionsIndexParam responses: '200': $ref: ../common/responses/Success.yml diff --git a/specs/query-suggestions/paths/qsConfigs.yml b/specs/query-suggestions/paths/qsConfigs.yml index 803e9c4c3d..ae2b5acddd 100644 --- a/specs/query-suggestions/paths/qsConfigs.yml +++ b/specs/query-suggestions/paths/qsConfigs.yml @@ -8,7 +8,7 @@ post: content: application/json: schema: - $ref: ../common/parameters.yml#/QuerySuggestionIndexParam + $ref: ../common/parameters.yml#/QuerySuggestionsIndexWithIndexParam responses: '200': $ref: ../common/responses/Success.yml diff --git a/templates/javascript/api-single.mustache b/templates/javascript/api-single.mustache index 0d44808439..8f16b0d348 100644 --- a/templates/javascript/api-single.mustache +++ b/templates/javascript/api-single.mustache @@ -60,6 +60,9 @@ export class {{classname}} { {{#isAnalyticsHost}} region: 'de' | 'us', {{/isAnalyticsHost}} + {{#isQuerySuggestionsHost}} + region: 'eu' | 'us', + {{/isQuerySuggestionsHost}} {{/hasRegionalHost}} options?: {requester?: Requester, hosts?: Host[]} ) { From 1aff1c514f9b004f9f67306080ac9e113d0340bc Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 10 Jan 2022 18:35:34 +0100 Subject: [PATCH 6/9] add CTS --- .../query-suggestions/createConfig.json | 67 +++++++++ .../query-suggestions/deleteConfig.json | 12 ++ .../query-suggestions/getAllConfigs.json | 10 ++ .../clients/query-suggestions/getConfig.json | 12 ++ .../query-suggestions/getConfigStatus.json | 12 ++ .../clients/query-suggestions/getLogFile.json | 12 ++ .../query-suggestions/updateConfig.json | 66 +++++++++ tests/CTS/templates/javascript.mustache | 2 +- tests/generateCTS.ts | 15 +- tests/output/javascript/analytics.test.ts | 2 +- .../output/javascript/personalization.test.ts | 2 +- .../javascript/query-suggestions.test.ts | 135 ++++++++++++++++++ 12 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 tests/CTS/clients/query-suggestions/createConfig.json create mode 100644 tests/CTS/clients/query-suggestions/deleteConfig.json create mode 100644 tests/CTS/clients/query-suggestions/getAllConfigs.json create mode 100644 tests/CTS/clients/query-suggestions/getConfig.json create mode 100644 tests/CTS/clients/query-suggestions/getConfigStatus.json create mode 100644 tests/CTS/clients/query-suggestions/getLogFile.json create mode 100644 tests/CTS/clients/query-suggestions/updateConfig.json create mode 100644 tests/output/javascript/query-suggestions.test.ts diff --git a/tests/CTS/clients/query-suggestions/createConfig.json b/tests/CTS/clients/query-suggestions/createConfig.json new file mode 100644 index 0000000000..eb4cae476b --- /dev/null +++ b/tests/CTS/clients/query-suggestions/createConfig.json @@ -0,0 +1,67 @@ +[ + { + "method": "createConfig", + "parameters": { + "querySuggestionsIndexWithIndexParam": { + "indexName": "theIndexName", + "sourceIndices": [ + { + "indexName": "testIndex", + "facets": [ + { + "attributes": "test" + } + ], + "generate": [ + [ + "facetA", + "facetB" + ], + [ + "facetC" + ] + ] + } + ], + "languages": [ + "french" + ], + "exclude": [ + "test" + ] + } + }, + "request": { + "path": "/1/configs", + "method": "POST", + "data": { + "indexName": "theIndexName", + "sourceIndices": [ + { + "indexName": "testIndex", + "facets": [ + { + "attributes": "test" + } + ], + "generate": [ + [ + "facetA", + "facetB" + ], + [ + "facetC" + ] + ] + } + ], + "languages": [ + "french" + ], + "exclude": [ + "test" + ] + } + } + } +] diff --git a/tests/CTS/clients/query-suggestions/deleteConfig.json b/tests/CTS/clients/query-suggestions/deleteConfig.json new file mode 100644 index 0000000000..4d07fd9d19 --- /dev/null +++ b/tests/CTS/clients/query-suggestions/deleteConfig.json @@ -0,0 +1,12 @@ +[ + { + "method": "deleteConfig", + "parameters": { + "indexName": "theIndexName" + }, + "request": { + "path": "/1/configs/theIndexName", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/clients/query-suggestions/getAllConfigs.json b/tests/CTS/clients/query-suggestions/getAllConfigs.json new file mode 100644 index 0000000000..13a2deb8c7 --- /dev/null +++ b/tests/CTS/clients/query-suggestions/getAllConfigs.json @@ -0,0 +1,10 @@ +[ + { + "method": "getAllConfigs", + "parameters": {}, + "request": { + "path": "/1/configs", + "method": "GET" + } + } +] diff --git a/tests/CTS/clients/query-suggestions/getConfig.json b/tests/CTS/clients/query-suggestions/getConfig.json new file mode 100644 index 0000000000..f7e308fb9a --- /dev/null +++ b/tests/CTS/clients/query-suggestions/getConfig.json @@ -0,0 +1,12 @@ +[ + { + "method": "getConfig", + "parameters": { + "indexName": "theIndexName" + }, + "request": { + "path": "/1/configs/theIndexName", + "method": "GET" + } + } +] diff --git a/tests/CTS/clients/query-suggestions/getConfigStatus.json b/tests/CTS/clients/query-suggestions/getConfigStatus.json new file mode 100644 index 0000000000..b9e0abc1e8 --- /dev/null +++ b/tests/CTS/clients/query-suggestions/getConfigStatus.json @@ -0,0 +1,12 @@ +[ + { + "method": "getConfigStatus", + "parameters": { + "indexName": "theIndexName" + }, + "request": { + "path": "/1/configs/theIndexName/status", + "method": "GET" + } + } +] diff --git a/tests/CTS/clients/query-suggestions/getLogFile.json b/tests/CTS/clients/query-suggestions/getLogFile.json new file mode 100644 index 0000000000..caea88b93a --- /dev/null +++ b/tests/CTS/clients/query-suggestions/getLogFile.json @@ -0,0 +1,12 @@ +[ + { + "method": "getLogFile", + "parameters": { + "indexName": "theIndexName" + }, + "request": { + "path": "/1/logs/theIndexName", + "method": "GET" + } + } +] diff --git a/tests/CTS/clients/query-suggestions/updateConfig.json b/tests/CTS/clients/query-suggestions/updateConfig.json new file mode 100644 index 0000000000..117e5810e8 --- /dev/null +++ b/tests/CTS/clients/query-suggestions/updateConfig.json @@ -0,0 +1,66 @@ +[ + { + "method": "updateConfig", + "parameters": { + "indexName": "theIndexName", + "querySuggestionsIndexParam": { + "sourceIndices": [ + { + "indexName": "testIndex", + "facets": [ + { + "attributes": "test" + } + ], + "generate": [ + [ + "facetA", + "facetB" + ], + [ + "facetC" + ] + ] + } + ], + "languages": [ + "french" + ], + "exclude": [ + "test" + ] + } + }, + "request": { + "path": "/1/configs/theIndexName", + "method": "PUT", + "data": { + "sourceIndices": [ + { + "indexName": "testIndex", + "facets": [ + { + "attributes": "test" + } + ], + "generate": [ + [ + "facetA", + "facetB" + ], + [ + "facetC" + ] + ] + } + ], + "languages": [ + "french" + ], + "exclude": [ + "test" + ] + } + } + } +] diff --git a/tests/CTS/templates/javascript.mustache b/tests/CTS/templates/javascript.mustache index 5b2541fb66..32de5195b0 100644 --- a/tests/CTS/templates/javascript.mustache +++ b/tests/CTS/templates/javascript.mustache @@ -3,7 +3,7 @@ import { {{client}}, EchoRequester } from '{{{import}}}'; const appId = process.env.ALGOLIA_APPLICATION_ID || 'test_app_id'; const apiKey = process.env.ALGOLIA_SEARCH_KEY || 'test_api_key'; -const client = new {{client}}(appId, apiKey, {{#hasRegionalHost}}'de', {{/hasRegionalHost}}{ requester: new EchoRequester() }); +const client = new {{client}}(appId, apiKey, {{#hasRegionalHost}}'us', {{/hasRegionalHost}}{ requester: new EchoRequester() }); {{#blocks}} describe('{{operationId}}', () => { diff --git a/tests/generateCTS.ts b/tests/generateCTS.ts index f493510217..0059de757f 100644 --- a/tests/generateCTS.ts +++ b/tests/generateCTS.ts @@ -85,6 +85,13 @@ function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1); } +function createClientName(client: string): string { + return `${client + .split('-') + .map((part) => capitalize(part)) + .join('')}Api`; +} + function removeObjectName(obj: Record): void { for (const prop in obj) { if (prop === '$objectName') { @@ -213,9 +220,13 @@ async function generateCode(language: Language): Promise { const code = Mustache.render(template, { import: packageNames[language][client], - client: `${capitalize(client)}Api`, + client: createClientName(client), blocks: cts[client], - hasRegionalHost: ['personalization', 'analytics'].includes(client), + hasRegionalHost: [ + 'personalization', + 'analytics', + 'query-suggestions', + ].includes(client), }); await fsp.writeFile( `output/${language}/${client}${extensionForLanguage[language]}`, diff --git a/tests/output/javascript/analytics.test.ts b/tests/output/javascript/analytics.test.ts index 699732c9ff..281c80fd03 100644 --- a/tests/output/javascript/analytics.test.ts +++ b/tests/output/javascript/analytics.test.ts @@ -3,7 +3,7 @@ import { AnalyticsApi, EchoRequester } from '@algolia/client-analytics'; const appId = process.env.ALGOLIA_APPLICATION_ID || 'test_app_id'; const apiKey = process.env.ALGOLIA_SEARCH_KEY || 'test_api_key'; -const client = new AnalyticsApi(appId, apiKey, 'de', { +const client = new AnalyticsApi(appId, apiKey, 'us', { requester: new EchoRequester(), }); diff --git a/tests/output/javascript/personalization.test.ts b/tests/output/javascript/personalization.test.ts index 5f0c629665..1b58e2056d 100644 --- a/tests/output/javascript/personalization.test.ts +++ b/tests/output/javascript/personalization.test.ts @@ -6,7 +6,7 @@ import { const appId = process.env.ALGOLIA_APPLICATION_ID || 'test_app_id'; const apiKey = process.env.ALGOLIA_SEARCH_KEY || 'test_api_key'; -const client = new PersonalizationApi(appId, apiKey, 'de', { +const client = new PersonalizationApi(appId, apiKey, 'us', { requester: new EchoRequester(), }); diff --git a/tests/output/javascript/query-suggestions.test.ts b/tests/output/javascript/query-suggestions.test.ts new file mode 100644 index 0000000000..95100564e1 --- /dev/null +++ b/tests/output/javascript/query-suggestions.test.ts @@ -0,0 +1,135 @@ +import { + QuerySuggestionsApi, + EchoRequester, +} from '@algolia/client-query-suggestions'; + +const appId = process.env.ALGOLIA_APPLICATION_ID || 'test_app_id'; +const apiKey = process.env.ALGOLIA_SEARCH_KEY || 'test_api_key'; + +const client = new QuerySuggestionsApi(appId, apiKey, 'us', { + requester: new EchoRequester(), +}); + +describe('createConfig', () => { + test('createConfig', async () => { + const req = await client.createConfig({ + querySuggestionsIndexWithIndexParam: { + indexName: 'theIndexName', + sourceIndices: [ + { + indexName: 'testIndex', + facets: [{ attributes: 'test' }], + generate: [['facetA', 'facetB'], ['facetC']], + }, + ], + languages: ['french'], + exclude: ['test'], + }, + }); + + expect((req as any).path).toEqual('/1/configs'); + expect((req as any).method).toEqual('POST'); + expect((req as any).data).toEqual({ + indexName: 'theIndexName', + sourceIndices: [ + { + indexName: 'testIndex', + facets: [{ attributes: 'test' }], + generate: [['facetA', 'facetB'], ['facetC']], + }, + ], + languages: ['french'], + exclude: ['test'], + }); + expect((req as any).searchParams).toEqual(undefined); + }); +}); + +describe('deleteConfig', () => { + test('deleteConfig', async () => { + const req = await client.deleteConfig({ indexName: 'theIndexName' }); + + expect((req as any).path).toEqual('/1/configs/theIndexName'); + expect((req as any).method).toEqual('DELETE'); + expect((req as any).data).toEqual(undefined); + expect((req as any).searchParams).toEqual(undefined); + }); +}); + +describe('getAllConfigs', () => { + test('getAllConfigs', async () => { + const req = await client.getAllConfigs(); + + expect((req as any).path).toEqual('/1/configs'); + expect((req as any).method).toEqual('GET'); + expect((req as any).data).toEqual(undefined); + expect((req as any).searchParams).toEqual(undefined); + }); +}); + +describe('getConfig', () => { + test('getConfig', async () => { + const req = await client.getConfig({ indexName: 'theIndexName' }); + + expect((req as any).path).toEqual('/1/configs/theIndexName'); + expect((req as any).method).toEqual('GET'); + expect((req as any).data).toEqual(undefined); + expect((req as any).searchParams).toEqual(undefined); + }); +}); + +describe('getConfigStatus', () => { + test('getConfigStatus', async () => { + const req = await client.getConfigStatus({ indexName: 'theIndexName' }); + + expect((req as any).path).toEqual('/1/configs/theIndexName/status'); + expect((req as any).method).toEqual('GET'); + expect((req as any).data).toEqual(undefined); + expect((req as any).searchParams).toEqual(undefined); + }); +}); + +describe('getLogFile', () => { + test('getLogFile', async () => { + const req = await client.getLogFile({ indexName: 'theIndexName' }); + + expect((req as any).path).toEqual('/1/logs/theIndexName'); + expect((req as any).method).toEqual('GET'); + expect((req as any).data).toEqual(undefined); + expect((req as any).searchParams).toEqual(undefined); + }); +}); + +describe('updateConfig', () => { + test('updateConfig', async () => { + const req = await client.updateConfig({ + indexName: 'theIndexName', + querySuggestionsIndexParam: { + sourceIndices: [ + { + indexName: 'testIndex', + facets: [{ attributes: 'test' }], + generate: [['facetA', 'facetB'], ['facetC']], + }, + ], + languages: ['french'], + exclude: ['test'], + }, + }); + + expect((req as any).path).toEqual('/1/configs/theIndexName'); + expect((req as any).method).toEqual('PUT'); + expect((req as any).data).toEqual({ + sourceIndices: [ + { + indexName: 'testIndex', + facets: [{ attributes: 'test' }], + generate: [['facetA', 'facetB'], ['facetC']], + }, + ], + languages: ['french'], + exclude: ['test'], + }); + expect((req as any).searchParams).toEqual(undefined); + }); +}); From e7aec05353837539952649fca37d180a8518b684 Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 10 Jan 2022 18:46:05 +0100 Subject: [PATCH 7/9] fix format spec --- specs/query-suggestions/common/parameters.yml | 2 +- .../common/schemas/QuerySuggestionsIndex.yml | 2 +- specs/query-suggestions/paths/getLogFile.yml | 2 +- specs/query-suggestions/spec.yml | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/query-suggestions/common/parameters.yml b/specs/query-suggestions/common/parameters.yml index dee975ab98..7996d0fc07 100644 --- a/specs/query-suggestions/common/parameters.yml +++ b/specs/query-suggestions/common/parameters.yml @@ -33,7 +33,7 @@ SourceIndex: type: string default: [] description: List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). - example: [['facetA', 'facetB'], ['facetC']] + example: [[facetA, facetB], [facetC]] external: type: array items: diff --git a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml index f936a80bf3..7262788b15 100644 --- a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml +++ b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml @@ -67,7 +67,7 @@ SourceIndexWithReplicas: type: string default: [] description: List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). - example: [['facetA', 'facetB'], ['facetC']] + example: [[facetA, facetB], [facetC]] external: type: array items: diff --git a/specs/query-suggestions/paths/getLogFile.yml b/specs/query-suggestions/paths/getLogFile.yml index d5337afa53..7f24d9f24b 100644 --- a/specs/query-suggestions/paths/getLogFile.yml +++ b/specs/query-suggestions/paths/getLogFile.yml @@ -28,7 +28,7 @@ get: description: date and time of creation of the record. level: type: string - enum: ['INFO', 'SKIP', 'ERROR'] + enum: [INFO, SKIP, ERROR] description: type of the record, can be one of three values (INFO, SKIP or ERROR). message: type: string diff --git a/specs/query-suggestions/spec.yml b/specs/query-suggestions/spec.yml index fc59726ab9..b6c7459b56 100644 --- a/specs/query-suggestions/spec.yml +++ b/specs/query-suggestions/spec.yml @@ -16,8 +16,8 @@ paths: /1/configs: $ref: paths/qsConfigs.yml /1/configs/{indexName}: - $ref: 'paths/qsConfig.yml' + $ref: paths/qsConfig.yml /1/configs/{indexName}/status: - $ref: 'paths/getConfigurationStatus.yml' + $ref: paths/getConfigurationStatus.yml /1/logs/{indexName}: - $ref: 'paths/getLogFile.yml' + $ref: paths/getLogFile.yml From cfab5d69a5d5ff805f2d5b6d7849c17bd5c230f8 Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Tue, 11 Jan 2022 18:18:37 +0100 Subject: [PATCH 8/9] review --- ...ggestionsIndexWithIndexParamAllOf.ts => indexName.ts} | 2 +- .../client-query-suggestions/model/models.ts | 2 +- .../model/querySuggestionsIndexWithIndexParam.ts | 6 +++--- .../model/sourceIndexExternal.ts | 6 ++++++ .../model/sourceIndexWithReplicas.ts | 6 ++++++ .../model/sourceIndexWithReplicasExternal.ts | 3 --- .../client-query-suggestions/model/status.ts | 9 +++++++++ .../client-query-suggestions/model/sucessResponse.ts | 6 ++++++ specs/query-suggestions/common/parameters.yml | 9 ++++++--- .../common/responses/StatusForbidden.yml | 5 ----- specs/query-suggestions/common/responses/Success.yml | 4 +++- .../common/schemas/QuerySuggestionsIndex.yml | 9 +++++---- specs/query-suggestions/paths/getConfigurationStatus.yml | 5 +++++ specs/query-suggestions/paths/getLogFile.yml | 4 +++- specs/query-suggestions/paths/qsConfig.yml | 4 ++++ specs/query-suggestions/paths/qsConfigs.yml | 4 +++- 16 files changed, 61 insertions(+), 23 deletions(-) rename clients/algoliasearch-client-javascript/client-query-suggestions/model/{querySuggestionsIndexWithIndexParamAllOf.ts => indexName.ts} (52%) delete mode 100644 specs/query-suggestions/common/responses/StatusForbidden.yml diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParamAllOf.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/indexName.ts similarity index 52% rename from clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParamAllOf.ts rename to clients/algoliasearch-client-javascript/client-query-suggestions/model/indexName.ts index 8edb131507..37c585d5b4 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParamAllOf.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/indexName.ts @@ -1,4 +1,4 @@ -export type QuerySuggestionsIndexWithIndexParamAllOf = { +export type IndexName = { /** * Index name to target. */ diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts index 812b07f3e0..782fed1944 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts @@ -2,11 +2,11 @@ import type { RequestOptions } from '../utils/types'; export * from './errorBase'; +export * from './indexName'; export * from './logFile'; export * from './querySuggestionsIndex'; export * from './querySuggestionsIndexParam'; export * from './querySuggestionsIndexWithIndexParam'; -export * from './querySuggestionsIndexWithIndexParamAllOf'; export * from './sourceIndex'; export * from './sourceIndexExternal'; export * from './sourceIndexWithReplicas'; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts index d5a7ddafb9..cd9ba12854 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/querySuggestionsIndexWithIndexParam.ts @@ -1,5 +1,5 @@ +import type { IndexName } from './indexName'; import type { QuerySuggestionsIndexParam } from './querySuggestionsIndexParam'; -import type { QuerySuggestionsIndexWithIndexParamAllOf } from './querySuggestionsIndexWithIndexParamAllOf'; -export type QuerySuggestionsIndexWithIndexParam = QuerySuggestionsIndexParam & - QuerySuggestionsIndexWithIndexParamAllOf; +export type QuerySuggestionsIndexWithIndexParam = IndexName & + QuerySuggestionsIndexParam; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts index e6ba5b9cec..d334668c52 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexExternal.ts @@ -1,4 +1,10 @@ export type SourceIndexExternal = { + /** + * The suggestion you would like to add. + */ query: string; + /** + * The measure of the suggestion relative popularity. + */ count: number; }; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts index 821f6da437..190e17c40a 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts @@ -5,6 +5,9 @@ export type SourceIndexWithReplicas = { * Source index name. */ indexName: string; + /** + * True if the Query Suggestions index is a replicas. + */ replicas: boolean; /** * List of analytics tags to filter the popular searches per tag. @@ -26,5 +29,8 @@ export type SourceIndexWithReplicas = { * List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). */ generate: string[][]; + /** + * List of external indices to use to generate custom Query Suggestions. + */ external: SourceIndexWithReplicasExternal[]; }; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts index 4931fca48a..4610457026 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts @@ -1,6 +1,3 @@ -/** - * List of external indices to use to generate custom Query Suggestions. - */ export type SourceIndexWithReplicasExternal = { query: string; count: number; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/status.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/status.ts index 6f5b4a9290..1e2048b9d0 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/status.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/status.ts @@ -1,5 +1,14 @@ export type Status = { + /** + * The targeted index name. + */ indexName: string; + /** + * True if the Query Suggestions index is running. + */ isRunning: boolean; + /** + * Date and time of the last build. + */ lastBuiltAt: string; }; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sucessResponse.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sucessResponse.ts index ff2b295060..841e28fdb3 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sucessResponse.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sucessResponse.ts @@ -1,4 +1,10 @@ export type SucessResponse = { + /** + * The status code. + */ status: number; + /** + * Message of the response. + */ message: string; }; diff --git a/specs/query-suggestions/common/parameters.yml b/specs/query-suggestions/common/parameters.yml index 7996d0fc07..d7eed71cdf 100644 --- a/specs/query-suggestions/common/parameters.yml +++ b/specs/query-suggestions/common/parameters.yml @@ -20,10 +20,10 @@ SourceIndex: default: [] description: List of facets to define as categories for the query suggestions. minHits: - type: number + type: integer description: Minimum number of hits (e.g., matching records in the source index) to generate a suggestions. minLetters: - type: number + type: integer description: Minimum number of required letters for a suggestion to remain. generate: type: array @@ -45,8 +45,10 @@ SourceIndex: properties: query: type: string + description: The suggestion you would like to add count: - type: number + type: integer + description: The measure of the suggestion relative popularity description: List of external indices to use to generate custom Query Suggestions. SourceIndices: @@ -78,6 +80,7 @@ QuerySuggestionsIndexWithIndexParam: allOf: - $ref: '#/QuerySuggestionsIndexParam' - type: object + title: IndexName additionalProperties: false required: - indexName diff --git a/specs/query-suggestions/common/responses/StatusForbidden.yml b/specs/query-suggestions/common/responses/StatusForbidden.yml deleted file mode 100644 index ec809c530e..0000000000 --- a/specs/query-suggestions/common/responses/StatusForbidden.yml +++ /dev/null @@ -1,5 +0,0 @@ -description: Status forbidden -content: - application/json: - schema: - $ref: ../../../common/schemas/ErrorBase.yml diff --git a/specs/query-suggestions/common/responses/Success.yml b/specs/query-suggestions/common/responses/Success.yml index 1f2f7fac65..06d5bbde78 100644 --- a/specs/query-suggestions/common/responses/Success.yml +++ b/specs/query-suggestions/common/responses/Success.yml @@ -10,8 +10,10 @@ content: - message properties: status: - type: number + type: integer example: 200 + description: The status code. message: type: string example: Success + description: Message of the response. diff --git a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml index 7262788b15..1c07909f51 100644 --- a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml +++ b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml @@ -41,6 +41,7 @@ SourceIndexWithReplicas: description: Source index name. replicas: type: boolean + description: true if the Query Suggestions index is a replicas. analyticsTags: type: array items: @@ -54,10 +55,10 @@ SourceIndexWithReplicas: default: [] description: List of facets to define as categories for the query suggestions. minHits: - type: number + type: integer description: Minimum number of hits (e.g., matching records in the source index) to generate a suggestions. minLetters: - type: number + type: integer description: Minimum number of required letters for a suggestion to remain. generate: type: array @@ -80,8 +81,8 @@ SourceIndexWithReplicas: query: type: string count: - type: number - description: List of external indices to use to generate custom Query Suggestions. + type: integer + description: List of external indices to use to generate custom Query Suggestions. SourceIndicesWithReplicas: type: array diff --git a/specs/query-suggestions/paths/getConfigurationStatus.yml b/specs/query-suggestions/paths/getConfigurationStatus.yml index d4fe5a2d9b..227613aec4 100644 --- a/specs/query-suggestions/paths/getConfigurationStatus.yml +++ b/specs/query-suggestions/paths/getConfigurationStatus.yml @@ -24,12 +24,17 @@ get: properties: indexName: type: string + description: The targeted index name. isRunning: type: boolean + description: true if the Query Suggestions index is running. lastBuiltAt: type: string format: data-time + description: Date and time of the last build. '401': $ref: ../../common/responses/Unauthorized.yml + '403': + $ref: ../../common/responses/MethodNotAllowed.yml '500': $ref: ../../common/responses/InternalError.yml diff --git a/specs/query-suggestions/paths/getLogFile.yml b/specs/query-suggestions/paths/getLogFile.yml index 7f24d9f24b..5c6f630417 100644 --- a/specs/query-suggestions/paths/getLogFile.yml +++ b/specs/query-suggestions/paths/getLogFile.yml @@ -34,10 +34,12 @@ get: type: string description: detailed description of what happened. contextLevel: - type: number + type: integer description: indicates the hierarchy of the records. For example, a record with contextLevel=1 belongs to a preceding record with contextLevel=0. '401': $ref: ../../common/responses/Unauthorized.yml + '403': + $ref: ../../common/responses/MethodNotAllowed.yml '404': $ref: ../../common/responses/IndexNotFound.yml '500': diff --git a/specs/query-suggestions/paths/qsConfig.yml b/specs/query-suggestions/paths/qsConfig.yml index 175f188925..51bbf50e1f 100644 --- a/specs/query-suggestions/paths/qsConfig.yml +++ b/specs/query-suggestions/paths/qsConfig.yml @@ -36,6 +36,8 @@ delete: $ref: ../common/responses/Success.yml '401': $ref: ../../common/responses/Unauthorized.yml + '403': + $ref: ../../common/responses/MethodNotAllowed.yml '500': $ref: ../../common/responses/InternalError.yml @@ -57,6 +59,8 @@ get: $ref: ../../common/responses/BadRequest.yml '401': $ref: ../../common/responses/Unauthorized.yml + '403': + $ref: ../../common/responses/MethodNotAllowed.yml '404': $ref: ../../common/responses/IndexNotFound.yml '500': diff --git a/specs/query-suggestions/paths/qsConfigs.yml b/specs/query-suggestions/paths/qsConfigs.yml index ae2b5acddd..4312c62298 100644 --- a/specs/query-suggestions/paths/qsConfigs.yml +++ b/specs/query-suggestions/paths/qsConfigs.yml @@ -17,7 +17,7 @@ post: '401': $ref: ../../common/responses/Unauthorized.yml '403': - $ref: ../common/responses/StatusForbidden.yml + $ref: ../../common/responses/MethodNotAllowed.yml '422': $ref: ../common/responses/StatusUnprocessableEntity.yml '500': @@ -42,6 +42,8 @@ get: $ref: ../common/schemas/QuerySuggestionsIndex.yml#/QuerySuggestionsIndex '401': $ref: ../../common/responses/Unauthorized.yml + '403': + $ref: ../../common/responses/MethodNotAllowed.yml '422': $ref: ../common/responses/StatusUnprocessableEntity.yml '500': From 9079dfff24ec74a4d1ad56a6e57f258ce6c5b0be Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Wed, 12 Jan 2022 11:00:38 +0100 Subject: [PATCH 9/9] review again --- .../client-query-suggestions/model/models.ts | 1 - .../model/sourceIndexWithReplicas.ts | 4 +- .../model/sourceIndexWithReplicasExternal.ts | 4 -- .../responses/StatusUnprocessableEntity.yml | 2 +- .../common/responses/Success.yml | 0 specs/query-suggestions/common/parameters.yml | 27 +++++++----- .../common/schemas/QuerySuggestionsIndex.yml | 44 +++---------------- specs/query-suggestions/paths/qsConfig.yml | 4 +- specs/query-suggestions/paths/qsConfigs.yml | 6 +-- 9 files changed, 30 insertions(+), 62 deletions(-) delete mode 100644 clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts rename specs/{query-suggestions => }/common/responses/StatusUnprocessableEntity.yml (62%) rename specs/{query-suggestions => }/common/responses/Success.yml (100%) diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts index 782fed1944..9ad1549d1d 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/models.ts @@ -10,7 +10,6 @@ export * from './querySuggestionsIndexWithIndexParam'; export * from './sourceIndex'; export * from './sourceIndexExternal'; export * from './sourceIndexWithReplicas'; -export * from './sourceIndexWithReplicasExternal'; export * from './status'; export * from './sucessResponse'; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts index 190e17c40a..fae244578d 100644 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts +++ b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicas.ts @@ -1,4 +1,4 @@ -import type { SourceIndexWithReplicasExternal } from './sourceIndexWithReplicasExternal'; +import type { SourceIndexExternal } from './sourceIndexExternal'; export type SourceIndexWithReplicas = { /** @@ -32,5 +32,5 @@ export type SourceIndexWithReplicas = { /** * List of external indices to use to generate custom Query Suggestions. */ - external: SourceIndexWithReplicasExternal[]; + external: SourceIndexExternal[]; }; diff --git a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts b/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts deleted file mode 100644 index 4610457026..0000000000 --- a/clients/algoliasearch-client-javascript/client-query-suggestions/model/sourceIndexWithReplicasExternal.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type SourceIndexWithReplicasExternal = { - query: string; - count: number; -}; diff --git a/specs/query-suggestions/common/responses/StatusUnprocessableEntity.yml b/specs/common/responses/StatusUnprocessableEntity.yml similarity index 62% rename from specs/query-suggestions/common/responses/StatusUnprocessableEntity.yml rename to specs/common/responses/StatusUnprocessableEntity.yml index 7aa14e7c4d..7025ba204d 100644 --- a/specs/query-suggestions/common/responses/StatusUnprocessableEntity.yml +++ b/specs/common/responses/StatusUnprocessableEntity.yml @@ -2,4 +2,4 @@ description: Status unprocessable entity content: application/json: schema: - $ref: ../../../common/schemas/ErrorBase.yml + $ref: ../schemas/ErrorBase.yml diff --git a/specs/query-suggestions/common/responses/Success.yml b/specs/common/responses/Success.yml similarity index 100% rename from specs/query-suggestions/common/responses/Success.yml rename to specs/common/responses/Success.yml diff --git a/specs/query-suggestions/common/parameters.yml b/specs/query-suggestions/common/parameters.yml index d7eed71cdf..eed470b9b3 100644 --- a/specs/query-suggestions/common/parameters.yml +++ b/specs/query-suggestions/common/parameters.yml @@ -37,20 +37,23 @@ SourceIndex: external: type: array items: - type: object - additionalProperties: false - required: - - query - - count - properties: - query: - type: string - description: The suggestion you would like to add - count: - type: integer - description: The measure of the suggestion relative popularity + $ref: '#/SourceIndexExternal' description: List of external indices to use to generate custom Query Suggestions. +SourceIndexExternal: + type: object + additionalProperties: false + required: + - query + - count + properties: + query: + type: string + description: The suggestion you would like to add + count: + type: integer + description: The measure of the suggestion relative popularity + SourceIndices: type: array items: diff --git a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml index 1c07909f51..07ca544d67 100644 --- a/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml +++ b/specs/query-suggestions/common/schemas/QuerySuggestionsIndex.yml @@ -37,52 +37,22 @@ SourceIndexWithReplicas: - external properties: indexName: - type: string - description: Source index name. + $ref: ../parameters.yml#/SourceIndex/properties/indexName replicas: type: boolean description: true if the Query Suggestions index is a replicas. analyticsTags: - type: array - items: - type: string - default: [] - description: List of analytics tags to filter the popular searches per tag. + $ref: ../parameters.yml#/SourceIndex/properties/analyticsTags facets: - type: array - items: - type: object - default: [] - description: List of facets to define as categories for the query suggestions. + $ref: ../parameters.yml#/SourceIndex/properties/facets minHits: - type: integer - description: Minimum number of hits (e.g., matching records in the source index) to generate a suggestions. + $ref: ../parameters.yml#/SourceIndex/properties/minHits minLetters: - type: integer - description: Minimum number of required letters for a suggestion to remain. + $ref: ../parameters.yml#/SourceIndex/properties/minLetters generate: - type: array - items: - type: array - items: - type: string - default: [] - description: List of facet attributes used to generate Query Suggestions. The resulting suggestions are every combination of the facets in the nested list (e.g., (facetA and facetB) and facetC). - example: [[facetA, facetB], [facetC]] + $ref: ../parameters.yml#/SourceIndex/properties/generate external: - type: array - items: - type: object - additionalProperties: false - required: - - query - - count - properties: - query: - type: string - count: - type: integer - description: List of external indices to use to generate custom Query Suggestions. + $ref: ../parameters.yml#/SourceIndex/properties/external SourceIndicesWithReplicas: type: array diff --git a/specs/query-suggestions/paths/qsConfig.yml b/specs/query-suggestions/paths/qsConfig.yml index 51bbf50e1f..dafde87126 100644 --- a/specs/query-suggestions/paths/qsConfig.yml +++ b/specs/query-suggestions/paths/qsConfig.yml @@ -13,7 +13,7 @@ put: $ref: ../common/parameters.yml#/QuerySuggestionsIndexParam responses: '200': - $ref: ../common/responses/Success.yml + $ref: ../../common/responses/Success.yml '401': $ref: ../../common/responses/Unauthorized.yml '500': @@ -33,7 +33,7 @@ delete: - $ref: ../../common/parameters.yml#/IndexName responses: '200': - $ref: ../common/responses/Success.yml + $ref: ../../common/responses/Success.yml '401': $ref: ../../common/responses/Unauthorized.yml '403': diff --git a/specs/query-suggestions/paths/qsConfigs.yml b/specs/query-suggestions/paths/qsConfigs.yml index 4312c62298..be2a55d40c 100644 --- a/specs/query-suggestions/paths/qsConfigs.yml +++ b/specs/query-suggestions/paths/qsConfigs.yml @@ -11,7 +11,7 @@ post: $ref: ../common/parameters.yml#/QuerySuggestionsIndexWithIndexParam responses: '200': - $ref: ../common/responses/Success.yml + $ref: ../../common/responses/Success.yml '400': $ref: ../../common/responses/BadRequest.yml '401': @@ -19,7 +19,7 @@ post: '403': $ref: ../../common/responses/MethodNotAllowed.yml '422': - $ref: ../common/responses/StatusUnprocessableEntity.yml + $ref: ../../common/responses/StatusUnprocessableEntity.yml '500': $ref: ../../common/responses/InternalError.yml @@ -45,6 +45,6 @@ get: '403': $ref: ../../common/responses/MethodNotAllowed.yml '422': - $ref: ../common/responses/StatusUnprocessableEntity.yml + $ref: ../../common/responses/StatusUnprocessableEntity.yml '500': $ref: ../../common/responses/InternalError.yml