From b34adb284ce6af4ebeee265831af027fe4bc4196 Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Wed, 5 Jan 2022 15:13:16 +0100 Subject: [PATCH 01/10] feat: add Insight client specs + insights client for javascript --- .../client-insights/.gitignore | 4 + .../client-insights/.openapi-generator-ignore | 23 ++ .../client-insights/api.ts | 3 + .../client-insights/git_push.sh | 57 ++++ .../client-insights/model/errorBase.ts | 6 + .../client-insights/model/insightEvent.ts | 49 ++++ .../client-insights/model/insightEvents.ts | 11 + .../client-insights/model/models.ts | 46 ++++ .../model/pushEventsResponse.ts | 6 + .../client-insights/package.json | 24 ++ .../client-insights/src/apis.ts | 7 + .../client-insights/src/insightsApi.ts | 118 +++++++++ .../client-insights/tsconfig.json | 22 ++ .../client-insights/utils/Response.ts | 23 ++ .../client-insights/utils/StatefulHost.ts | 34 +++ .../client-insights/utils/Transporter.ts | 243 ++++++++++++++++++ .../client-insights/utils/cache/Cache.ts | 27 ++ .../utils/cache/MemoryCache.ts | 39 +++ .../client-insights/utils/errors.ts | 38 +++ .../client-insights/utils/helpers.ts | 117 +++++++++ .../utils/requester/EchoRequester.ts | 17 ++ .../utils/requester/HttpRequester.ts | 94 +++++++ .../utils/requester/Requester.ts | 8 + .../client-insights/utils/stackTrace.ts | 30 +++ .../client-insights/utils/types.ts | 65 +++++ openapitools.json | 22 ++ specs/insights/paths/pushEvents.yml | 90 ++++++- specs/insights/spec.yml | 34 +-- templates/javascript/api-single.mustache | 8 +- yarn.lock | 9 + 30 files changed, 1255 insertions(+), 19 deletions(-) create mode 100644 clients/algoliasearch-client-javascript/client-insights/.gitignore create mode 100644 clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore create mode 100644 clients/algoliasearch-client-javascript/client-insights/api.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/git_push.sh create mode 100644 clients/algoliasearch-client-javascript/client-insights/model/errorBase.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/model/insightEvent.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/model/models.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/model/pushEventsResponse.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/package.json create mode 100644 clients/algoliasearch-client-javascript/client-insights/src/apis.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/tsconfig.json create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/Response.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/StatefulHost.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/Transporter.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/cache/Cache.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/cache/MemoryCache.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/errors.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/helpers.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/requester/EchoRequester.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/requester/HttpRequester.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/requester/Requester.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/stackTrace.ts create mode 100644 clients/algoliasearch-client-javascript/client-insights/utils/types.ts diff --git a/clients/algoliasearch-client-javascript/client-insights/.gitignore b/clients/algoliasearch-client-javascript/client-insights/.gitignore new file mode 100644 index 0000000000..8aafcdc3fd --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.openapi-generator +.env diff --git a/clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore b/clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/clients/algoliasearch-client-javascript/client-insights/api.ts b/clients/algoliasearch-client-javascript/client-insights/api.ts new file mode 100644 index 0000000000..59b02c4607 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/git_push.sh b/clients/algoliasearch-client-javascript/client-insights/git_push.sh new file mode 100644 index 0000000000..b151edf823 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="algolia" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="algolia" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="algoliasearch-client-javascript" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/clients/algoliasearch-client-javascript/client-insights/model/errorBase.ts b/clients/algoliasearch-client-javascript/client-insights/model/errorBase.ts new file mode 100644 index 0000000000..a533aa7a15 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/model/errorBase.ts @@ -0,0 +1,6 @@ +/** + * Error. + */ +export type ErrorBase = { + message?: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-insights/model/insightEvent.ts b/clients/algoliasearch-client-javascript/client-insights/model/insightEvent.ts new file mode 100644 index 0000000000..fde9c5580c --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/model/insightEvent.ts @@ -0,0 +1,49 @@ +/** + * Insights event. + */ +export type InsightEvent = { + /** + * An eventType can be a click, a conversion, or a view. + */ + eventType: InsightEvent.EventTypeEnum; + /** + * A user-defined string used to categorize events. + */ + eventName: string; + /** + * Name of the targeted index. + */ + index: string; + /** + * A user identifier. Depending if the user is logged-in or not, several strategies can be used from a sessionId to a technical identifier. + */ + userToken: string; + /** + * Time of the event expressed in milliseconds since the Unix epoch. + */ + timestamp?: number; + /** + * Algolia queryID. This is required when an event is tied to a search. + */ + queryID?: string; + /** + * An array of index objectID. Limited to 20 objects. An event can’t have both objectIDs and filters at the same time. + */ + objectIDs?: string[]; + /** + * An array of filters. Limited to 10 filters. An event can’t have both objectIDs and filters at the same time. + */ + filters?: string[]; + /** + * Position of the click in the list of Algolia search results. This field is required if a queryID is provided. One position must be provided for each objectID. + */ + positions?: number[]; +}; + +export namespace InsightEvent { + export enum EventTypeEnum { + Click = 'click', + Conversion = 'conversion', + View = 'view', + } +} diff --git a/clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts b/clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts new file mode 100644 index 0000000000..0431c466c7 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts @@ -0,0 +1,11 @@ +import type { InsightEvent } from './insightEvent'; + +/** + * Object containing the events sent to the Insights API. + */ +export type InsightEvents = { + /** + * Array of events sent to the Insights API. + */ + events: InsightEvent[]; +}; diff --git a/clients/algoliasearch-client-javascript/client-insights/model/models.ts b/clients/algoliasearch-client-javascript/client-insights/model/models.ts new file mode 100644 index 0000000000..fa4726b58b --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/model/models.ts @@ -0,0 +1,46 @@ +/* eslint-disable no-param-reassign */ +import type { RequestOptions } from '../utils/types'; + +export * from './errorBase'; +export * from './insightEvent'; +export * from './insightEvents'; +export * from './pushEventsResponse'; + +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-insights/model/pushEventsResponse.ts b/clients/algoliasearch-client-javascript/client-insights/model/pushEventsResponse.ts new file mode 100644 index 0000000000..ed9b5dd044 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/model/pushEventsResponse.ts @@ -0,0 +1,6 @@ +export type PushEventsResponse = { + /** + * A message confirming the event push. + */ + message: string; +}; diff --git a/clients/algoliasearch-client-javascript/client-insights/package.json b/clients/algoliasearch-client-javascript/client-insights/package.json new file mode 100644 index 0000000000..5da4f673fe --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/package.json @@ -0,0 +1,24 @@ +{ + "name": "@algolia/client-insights", + "version": "5.0.0", + "description": "JavaScript client for @algolia/client-insights", + "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.2" + } +} diff --git a/clients/algoliasearch-client-javascript/client-insights/src/apis.ts b/clients/algoliasearch-client-javascript/client-insights/src/apis.ts new file mode 100644 index 0000000000..9dea0a8916 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/src/apis.ts @@ -0,0 +1,7 @@ +import { InsightsApi } from './insightsApi'; + +export * from './insightsApi'; +export * from '../utils/errors'; +export { EchoRequester } from '../utils/requester/EchoRequester'; + +export const APIS = [InsightsApi]; diff --git a/clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts b/clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts new file mode 100644 index 0000000000..0268c08f27 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts @@ -0,0 +1,118 @@ +import type { InsightEvents } from '../model/insightEvents'; +import { ApiKeyAuth } from '../model/models'; +import type { PushEventsResponse } from '../model/pushEventsResponse'; +import { Transporter } from '../utils/Transporter'; +import type { Requester } from '../utils/requester/Requester'; +import type { Headers, Host, Request, RequestOptions } from '../utils/types'; + +export enum InsightsApiKeys { + apiKey, + appId, +} + +export class InsightsApi { + 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(InsightsApiKeys.appId, appId); + this.setApiKey(InsightsApiKeys.apiKey, apiKey); + + this.transporter = new Transporter({ + hosts: options?.hosts ?? this.getDefaultHosts(), + baseHeaders: { + 'content-type': 'application/x-www-form-urlencoded', + }, + userAgent: 'Algolia for Javascript', + timeouts: { + connect: 2, + read: 5, + write: 30, + }, + requester: options?.requester, + }); + } + + getDefaultHosts(): Host[] { + return [ + { url: `insights.algolia.io`, accept: 'readWrite', protocol: 'https' }, + ]; + } + + setRequest(requester: Requester): void { + this.transporter.setRequester(requester); + } + + setHosts(hosts: Host[]): void { + this.transporter.setHosts(hosts); + } + + setApiKey(key: InsightsApiKeys, value: string): void { + this.authentications[InsightsApiKeys[key]].apiKey = value; + } + + /** + * This command pushes an array of events to the Insights API. + * + * @summary This command pushes an array of events to the Insights API. + * @param pushEvents - The pushEvents parameters. + * @param pushEvents.insightEvents - The insightEvents. + */ + pushEvents({ insightEvents }: PushEventsProps): Promise { + const path = '/1/events'; + const headers: Headers = { Accept: 'application/json' }; + const queryParameters: Record = {}; + + if (insightEvents === null || insightEvents === undefined) { + throw new Error( + 'Required parameter insightEvents was null or undefined when calling pushEvents.' + ); + } + + if (insightEvents.events === null || insightEvents.events === undefined) { + throw new Error( + 'Required parameter insightEvents.events was null or undefined when calling pushEvents.' + ); + } + + const request: Request = { + method: 'POST', + path, + data: insightEvents, + }; + + const requestOptions: RequestOptions = { + headers, + queryParameters, + }; + + return this.sendRequest(request, requestOptions); + } +} + +export type PushEventsProps = { + insightEvents: InsightEvents; +}; diff --git a/clients/algoliasearch-client-javascript/client-insights/tsconfig.json b/clients/algoliasearch-client-javascript/client-insights/tsconfig.json new file mode 100644 index 0000000000..2f72c93ccb --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/Response.ts b/clients/algoliasearch-client-javascript/client-insights/utils/Response.ts new file mode 100644 index 0000000000..bd22de7df9 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/StatefulHost.ts b/clients/algoliasearch-client-javascript/client-insights/utils/StatefulHost.ts new file mode 100644 index 0000000000..162c4ed1c6 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/Transporter.ts b/clients/algoliasearch-client-javascript/client-insights/utils/Transporter.ts new file mode 100644 index 0000000000..48b4edebfd --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/cache/Cache.ts b/clients/algoliasearch-client-javascript/client-insights/utils/cache/Cache.ts new file mode 100644 index 0000000000..625862660c --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/cache/MemoryCache.ts b/clients/algoliasearch-client-javascript/client-insights/utils/cache/MemoryCache.ts new file mode 100644 index 0000000000..f7853f39bc --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/errors.ts b/clients/algoliasearch-client-javascript/client-insights/utils/errors.ts new file mode 100644 index 0000000000..a02f3004ad --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/helpers.ts b/clients/algoliasearch-client-javascript/client-insights/utils/helpers.ts new file mode 100644 index 0000000000..5d64ac8868 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/requester/EchoRequester.ts b/clients/algoliasearch-client-javascript/client-insights/utils/requester/EchoRequester.ts new file mode 100644 index 0000000000..41a3dd9041 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/utils/requester/EchoRequester.ts @@ -0,0 +1,17 @@ +import type { EndRequest, Request, Response } from '../types'; + +import { Requester } from './Requester'; + +export class EchoRequester extends Requester { + constructor(private status = 200) { + super(); + } + + send(_request: EndRequest, originalRequest: Request): Promise { + return Promise.resolve({ + content: JSON.stringify(originalRequest), + isTimedOut: false, + status: this.status, + }); + } +} diff --git a/clients/algoliasearch-client-javascript/client-insights/utils/requester/HttpRequester.ts b/clients/algoliasearch-client-javascript/client-insights/utils/requester/HttpRequester.ts new file mode 100644 index 0000000000..3697d290fb --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/requester/Requester.ts b/clients/algoliasearch-client-javascript/client-insights/utils/requester/Requester.ts new file mode 100644 index 0000000000..41c0606575 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/stackTrace.ts b/clients/algoliasearch-client-javascript/client-insights/utils/stackTrace.ts new file mode 100644 index 0000000000..14750a54f2 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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-insights/utils/types.ts b/clients/algoliasearch-client-javascript/client-insights/utils/types.ts new file mode 100644 index 0000000000..d2a478c1a0 --- /dev/null +++ b/clients/algoliasearch-client-javascript/client-insights/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; +}; diff --git a/openapitools.json b/openapitools.json index 68b579e23b..185bb9a734 100644 --- a/openapitools.json +++ b/openapitools.json @@ -21,6 +21,7 @@ "npmVersion": "5.0.0", "packageName": "@algolia/client-search", + "useAppIdInHost": true, "isSearchHost": true } }, @@ -42,6 +43,7 @@ "npmVersion": "5.0.0", "packageName": "@algolia/recommend", + "useAppIdInHost": true, "isSearchHost": true } }, @@ -87,6 +89,26 @@ "isAnalyticsHost": true } }, + "javascript-insights": { + "generatorName": "typescript-node", + "templateDir": "#{cwd}/templates/javascript/", + "config": "#{cwd}/openapitools.json", + "apiPackage": "src", + "output": "#{cwd}/clients/algoliasearch-client-javascript/client-insights", + "glob": "specs/insights/spec.yml", + "gitHost": "algolia", + "gitUserId": "algolia", + "gitRepoId": "algoliasearch-client-javascript", + "additionalProperties": { + "modelPropertyNaming": "original", + "supportsES6": true, + "npmName": "@algolia/client-insights", + "npmVersion": "5.0.0", + + "packageName": "@algolia/client-insights", + "isInsightsHost": true + } + }, "java-search": { "generatorName": "java", "templateDir": "#{cwd}/templates/java/", diff --git a/specs/insights/paths/pushEvents.yml b/specs/insights/paths/pushEvents.yml index bf62725b88..632f51372d 100644 --- a/specs/insights/paths/pushEvents.yml +++ b/specs/insights/paths/pushEvents.yml @@ -1 +1,89 @@ -# post: +post: + tags: + - insights + operationId: pushEvents + description: This command pushes an array of events to the Insights API. + summary: This command pushes an array of events to the Insights API. + requestBody: + required: true + content: + application/json: + schema: + title: InsightEvents + type: object + description: Object containing the events sent to the Insights API. + additionalProperties: false + required: + - events + properties: + events: + type: array + description: Array of events sent to the Insights API. + items: + title: InsightEvent + type: object + description: Insights event. + additionalProperties: false + properties: + eventType: + type: string + description: An eventType can be a click, a conversion, or a view. + enum: [click, conversion, view] + eventName: + type: string + description: A user-defined string used to categorize events. + index: + type: string + description: Name of the targeted index. + userToken: + type: string + description: A user identifier. Depending if the user is logged-in or not, several strategies can be used from a sessionId to a technical identifier. + timestamp: + type: integer + description: Time of the event expressed in milliseconds since the Unix epoch. + queryID: + type: string + description: Algolia queryID. This is required when an event is tied to a search. + objectIDs: + type: array + description: An array of index objectID. Limited to 20 objects. An event can’t have both objectIDs and filters at the same time. + items: + type: string + filters: + type: array + description: An array of filters. Limited to 10 filters. An event can’t have both objectIDs and filters at the same time. + items: + type: string + positions: + type: array + description: Position of the click in the list of Algolia search results. This field is required if a queryID is provided. One position must be provided for each objectID. + items: + type: integer + required: + - eventType + - eventName + - index + - userToken + responses: + '200': + description: OK + content: + application/json: + schema: + title: pushEventsResponse + type: object + additionalProperties: false + required: + - message + properties: + message: + type: string + description: A message confirming the event push. + '400': + $ref: ../../common/responses/BadRequest.yml + '402': + $ref: ../../common/responses/FeatureNotEnabled.yml + '403': + $ref: ../../common/responses/MethodNotAllowed.yml + '404': + $ref: ../../common/responses/IndexNotFound.yml diff --git a/specs/insights/spec.yml b/specs/insights/spec.yml index 4b362a7eb1..4cb05bc906 100644 --- a/specs/insights/spec.yml +++ b/specs/insights/spec.yml @@ -1,17 +1,17 @@ -# openapi: 3.0.2 -# info: -# title: Insights API -# description: API powering the Insights 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/events: -# $ref: paths/pushEvents.yml +openapi: 3.0.2 +info: + title: Insights API + description: API powering the Insights 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/events: + $ref: paths/pushEvents.yml diff --git a/templates/javascript/api-single.mustache b/templates/javascript/api-single.mustache index 2c64259f5b..a69563311b 100644 --- a/templates/javascript/api-single.mustache +++ b/templates/javascript/api-single.mustache @@ -68,7 +68,7 @@ export class {{classname}} { this.transporter = new Transporter({ hosts: options?.hosts ?? this.getDefaultHosts( - {{^hasRegionalHost}}appId{{/hasRegionalHost}} + {{#useAppIdInHost}}appId{{/useAppIdInHost}} {{#hasRegionalHost}}region{{/hasRegionalHost}} ), baseHeaders: { @@ -113,6 +113,12 @@ export class {{classname}} { } {{/isAnalyticsHost}} + {{#isInsightsHost}} + public getDefaultHosts(): Host[] { + return [{ url: `insights.algolia.io`, accept: 'readWrite', protocol: 'https' }]; + } + {{/isInsightsHost}} + public setRequest(requester: Requester): void { this.transporter.setRequester(requester); } diff --git a/yarn.lock b/yarn.lock index 1266d96ded..67507e24ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,6 +40,15 @@ __metadata: languageName: unknown linkType: soft +"@algolia/client-insights@workspace:clients/algoliasearch-client-javascript/client-insights": + version: 0.0.0-use.local + resolution: "@algolia/client-insights@workspace:clients/algoliasearch-client-javascript/client-insights" + dependencies: + "@types/node": 16.11.11 + typescript: 4.5.2 + languageName: unknown + linkType: soft + "@algolia/client-personalization@5.0.0, @algolia/client-personalization@workspace:clients/algoliasearch-client-javascript/client-personalization": version: 0.0.0-use.local resolution: "@algolia/client-personalization@workspace:clients/algoliasearch-client-javascript/client-personalization" From bb064eefe9ee5b36b085511118ebc4a11ac1ba01 Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Wed, 5 Jan 2022 15:54:26 +0100 Subject: [PATCH 02/10] feat: add test for insights client --- tests/CTS/clients/insights/pushEvents.json | 76 ++++++++++++++++++++ tests/output/javascript/insights.test.ts | 81 ++++++++++++++++++++++ tests/package.json | 1 + 3 files changed, 158 insertions(+) create mode 100644 tests/CTS/clients/insights/pushEvents.json create mode 100644 tests/output/javascript/insights.test.ts diff --git a/tests/CTS/clients/insights/pushEvents.json b/tests/CTS/clients/insights/pushEvents.json new file mode 100644 index 0000000000..fcc367001b --- /dev/null +++ b/tests/CTS/clients/insights/pushEvents.json @@ -0,0 +1,76 @@ +[ + { + "method": "pushEvents", + "parameters": [ + { + "insightEvents": { + "events": [ + { + "eventType": "click", + "eventName": "Product Clicked", + "index": "products", + "userToken": "user-123456", + "timestamp": 1641290601962, + "objectIDs": ["9780545139700", "9780439784542"], + "queryID": "43b15df305339e827f0ac0bdc5ebcaa7", + "positions": [7, 6] + }, + { + "eventType": "view", + "eventName":"Product Detail Page Viewed", + "index": "products", + "userToken": "user-123456", + "timestamp": 1641290601962, + "objectIDs": ["9780545139700", "9780439784542"] + }, + { + "eventType": "conversion", + "eventName": "Product Purchased", + "index": "products", + "userToken": "user-123456", + "timestamp": 1641290601962, + "objectIDs": ["9780545139700", "9780439784542"], + "queryID": "43b15df305339e827f0ac0bdc5ebcaa7" + } + ] + } + } + ], + "request": { + "path": "/1/events", + "method": "POST", + "data": { + "events": [ + { + "eventType": "click", + "eventName": "Product Clicked", + "index": "products", + "userToken": "user-123456", + "timestamp": 1641290601962, + "objectIDs": ["9780545139700", "9780439784542"], + "queryID": "43b15df305339e827f0ac0bdc5ebcaa7", + "positions": [7, 6] + }, + { + "eventType": "view", + "eventName":"Product Detail Page Viewed", + "index": "products", + "userToken": "user-123456", + "timestamp": 1641290601962, + "objectIDs": ["9780545139700", "9780439784542"] + }, + { + "eventType": "conversion", + "eventName": "Product Purchased", + "index": "products", + "userToken": "user-123456", + "timestamp": 1641290601962, + "objectIDs": ["9780545139700", "9780439784542"], + "queryID": "43b15df305339e827f0ac0bdc5ebcaa7" + } + ] + } + } + } +] + \ No newline at end of file diff --git a/tests/output/javascript/insights.test.ts b/tests/output/javascript/insights.test.ts new file mode 100644 index 0000000000..36fe80cdec --- /dev/null +++ b/tests/output/javascript/insights.test.ts @@ -0,0 +1,81 @@ +// @ts-nocheck +import { InsightsApi, EchoRequester } from '@algolia/client-insights'; + +const client = new InsightsApi( + process.env.ALGOLIA_APPLICATION_ID, + process.env.ALGOLIA_SEARCH_KEY, + { requester: new EchoRequester() } +); + +describe('pushEvents', () => { + test('pushEvents', async () => { + const req = await client.pushEvents({ + insightEvents: { + events: [ + { + eventType: 'click', + eventName: 'Product Clicked', + index: 'products', + userToken: 'user-123456', + timestamp: 1641290601962, + objectIDs: ['9780545139700', '9780439784542'], + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + positions: [7, 6], + }, + { + eventType: 'view', + eventName: 'Product Detail Page Viewed', + index: 'products', + userToken: 'user-123456', + timestamp: 1641290601962, + objectIDs: ['9780545139700', '9780439784542'], + }, + { + eventType: 'conversion', + eventName: 'Product Purchased', + index: 'products', + userToken: 'user-123456', + timestamp: 1641290601962, + objectIDs: ['9780545139700', '9780439784542'], + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + ], + }, + }); + expect(req).toMatchObject({ + path: '/1/events', + method: 'POST', + data: { + events: [ + { + eventType: 'click', + eventName: 'Product Clicked', + index: 'products', + userToken: 'user-123456', + timestamp: 1641290601962, + objectIDs: ['9780545139700', '9780439784542'], + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + positions: [7, 6], + }, + { + eventType: 'view', + eventName: 'Product Detail Page Viewed', + index: 'products', + userToken: 'user-123456', + timestamp: 1641290601962, + objectIDs: ['9780545139700', '9780439784542'], + }, + { + eventType: 'conversion', + eventName: 'Product Purchased', + index: 'products', + userToken: 'user-123456', + timestamp: 1641290601962, + objectIDs: ['9780545139700', '9780439784542'], + queryID: '43b15df305339e827f0ac0bdc5ebcaa7', + }, + ], + }, + }); + }); +}); diff --git a/tests/package.json b/tests/package.json index c8f88d2b1e..629701842e 100644 --- a/tests/package.json +++ b/tests/package.json @@ -9,6 +9,7 @@ }, "devDependencies": { "@algolia/client-search": "5.0.0", + "@algolia/client-insights" : "5.0.0", "@algolia/recommend": "5.0.0", "@apidevtools/swagger-parser": "10.0.3", "@types/jest": "27.0.3", From 42a0dfd895104fae96f312ce3ae3fcd3de8e154d Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Wed, 5 Jan 2022 16:10:00 +0100 Subject: [PATCH 03/10] fix: regenerate --- tests/output/javascript/search.test.ts | 714 ++++++++++++------------- tests/package.json | 2 +- yarn.lock | 23 +- 3 files changed, 380 insertions(+), 359 deletions(-) diff --git a/tests/output/javascript/search.test.ts b/tests/output/javascript/search.test.ts index b0d5668e59..31af67f315 100644 --- a/tests/output/javascript/search.test.ts +++ b/tests/output/javascript/search.test.ts @@ -7,329 +7,309 @@ const client = new SearchApi( { requester: new EchoRequester() } ); -describe('getDictionarySettings', () => { - test('get getDictionarySettings results', async () => { - const req = await client.getDictionarySettings(); +describe('addApiKey', () => { + test('addApiKey', async () => { + const req = await client.addApiKey({ + apiKey: { + acl: ['search', 'addObject'], + description: 'my new api key', + validity: 300, + maxQueriesPerIPPerHour: 100, + maxHitsPerQuery: 20, + }, + }); expect(req).toMatchObject({ - path: '/1/dictionaries/*/settings', - method: 'GET', + path: '/1/keys', + method: 'POST', + data: { + acl: ['search', 'addObject'], + description: 'my new api key', + validity: 300, + maxQueriesPerIPPerHour: 100, + maxHitsPerQuery: 20, + }, }); }); }); -describe('searchSynonyms', () => { - test('searchSynonyms', async () => { - const req = await client.searchSynonyms({ - indexName: 'indexName', - query: 'queryString', - type: 'onewaysynonym', +describe('batchDictionaryEntries', () => { + test('get batchDictionaryEntries results with minimal parameters', async () => { + const req = await client.batchDictionaryEntries({ + dictionaryName: 'dictionaryName', + batchDictionaryEntries: { + requests: [ + { action: 'addEntry', body: { objectID: '1', language: 'en' } }, + { action: 'deleteEntry', body: { objectID: '2', language: 'fr' } }, + ], + }, }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/synonyms/search', + path: '/1/dictionaries/dictionaryName/batch', method: 'POST', + data: { + requests: [ + { action: 'addEntry', body: { objectID: '1', language: 'en' } }, + { action: 'deleteEntry', body: { objectID: '2', language: 'fr' } }, + ], + }, + }); + }); + + test('get batchDictionaryEntries results with all parameters', async () => { + const req = await client.batchDictionaryEntries({ + dictionaryName: 'dictionaryName', + batchDictionaryEntries: { + clearExistingDictionaryEntries: false, + requests: [ + { + action: 'addEntry', + body: { + objectID: '1', + language: 'en', + word: 'yo', + words: ['yo', 'algolia'], + decomposition: ['yo', 'algolia'], + state: 'enabled', + }, + }, + { + action: 'deleteEntry', + body: { + objectID: '2', + language: 'fr', + word: 'salut', + words: ['salut', 'algolia'], + decomposition: ['salut', 'algolia'], + state: 'enabled', + }, + }, + ], + }, + }); + expect(req).toMatchObject({ + path: '/1/dictionaries/dictionaryName/batch', + method: 'POST', + data: { + clearExistingDictionaryEntries: false, + requests: [ + { + action: 'addEntry', + body: { + objectID: '1', + language: 'en', + word: 'yo', + words: ['yo', 'algolia'], + decomposition: ['yo', 'algolia'], + state: 'enabled', + }, + }, + { + action: 'deleteEntry', + body: { + objectID: '2', + language: 'fr', + word: 'salut', + words: ['salut', 'algolia'], + decomposition: ['salut', 'algolia'], + state: 'enabled', + }, + }, + ], + }, }); }); }); -describe('saveSynonyms', () => { - test('saveSynonyms', async () => { - const req = await client.saveSynonyms({ +describe('batchRules', () => { + test('batchRules', async () => { + const req = await client.batchRules({ indexName: 'indexName', - synonymHit: [ + rule: [ { - objectID: 'id1', - type: 'synonym', - synonyms: ['car', 'vehicule', 'auto'], + objectID: 'a-rule-id', + conditions: [{ pattern: 'smartphone', anchoring: 'contains' }], + consequence: { params: { filters: 'category:smartphone' } }, }, { - objectID: 'id2', - type: 'onewaysynonym', - input: 'iphone', - synonyms: ['ephone', 'aphone', 'yphone'], + objectID: 'a-second-rule-id', + conditions: [{ pattern: 'apple', anchoring: 'contains' }], + consequence: { params: { filters: 'brand:apple' } }, }, ], forwardToReplicas: true, - replaceExistingSynonyms: false, + clearExistingRules: true, }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/synonyms/batch', + path: '/1/indexes/indexName/rules/batch', method: 'POST', data: [ { - objectID: 'id1', - type: 'synonym', - synonyms: ['car', 'vehicule', 'auto'], + objectID: 'a-rule-id', + conditions: [{ pattern: 'smartphone', anchoring: 'contains' }], + consequence: { params: { filters: 'category:smartphone' } }, }, { - objectID: 'id2', - type: 'onewaysynonym', - input: 'iphone', - synonyms: ['ephone', 'aphone', 'yphone'], + objectID: 'a-second-rule-id', + conditions: [{ pattern: 'apple', anchoring: 'contains' }], + consequence: { params: { filters: 'brand:apple' } }, }, ], }); }); }); -describe('deleteRule', () => { - test('deleteRule', async () => { - const req = await client.deleteRule({ - indexName: 'indexName', - objectID: 'id1', - }); - expect(req).toMatchObject({ - path: '/1/indexes/indexName/rules/id1', - method: 'DELETE', - }); - }); -}); - -describe('searchForFacetValues', () => { - test('get searchForFacetValues results with minimal parameters', async () => { - const req = await client.searchForFacetValues({ - indexName: 'indexName', - facetName: 'facetName', - }); +describe('browse', () => { + test('get browse results with minimal parameters', async () => { + const req = await client.browse({ indexName: 'indexName' }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/facets/facetName/query', + path: '/1/indexes/indexName/browse', method: 'POST', }); }); - test('get searchForFacetValues results with all parameters', async () => { - const req = await client.searchForFacetValues({ + test('get browse results with all parameters', async () => { + const req = await client.browse({ indexName: 'indexName', - facetName: 'facetName', - searchForFacetValuesRequest: { + browseRequest: { params: "query=foo&facetFilters=['bar']", - facetQuery: 'foo', - maxFacetHits: 42, + cursor: 'cts', }, }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/facets/facetName/query', + path: '/1/indexes/indexName/browse', method: 'POST', - data: { - params: "query=foo&facetFilters=['bar']", - facetQuery: 'foo', - maxFacetHits: 42, - }, + data: { params: "query=foo&facetFilters=['bar']", cursor: 'cts' }, }); }); }); -describe('getSynonym', () => { - test('getSynonym', async () => { - const req = await client.getSynonym({ - indexName: 'indexName', - objectID: 'id1', - }); +describe('clearAllSynonyms', () => { + test('clearAllSynonyms', async () => { + const req = await client.clearAllSynonyms({ indexName: 'indexName' }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/synonyms/id1', - method: 'GET', + path: '/1/indexes/indexName/synonyms/clear', + method: 'POST', }); }); }); -describe('search', () => { - test('search', async () => { - const req = await client.search({ - indexName: 'indexName', - searchParams: { $objectName: 'Query', query: 'queryString' }, - }); +describe('clearRules', () => { + test('clearRules', async () => { + const req = await client.clearRules({ indexName: 'indexName' }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/query', + path: '/1/indexes/indexName/rules/clear', method: 'POST', - data: { query: 'queryString' }, }); }); }); -describe('setDictionarySettings', () => { - test('get setDictionarySettings results with minimal parameters', async () => { - const req = await client.setDictionarySettings({ - dictionarySettingsRequest: { - disableStandardEntries: { plurals: { fr: false, en: false, ru: true } }, - }, - }); - expect(req).toMatchObject({ - path: '/1/dictionaries/*/settings', - method: 'PUT', - data: { - disableStandardEntries: { plurals: { fr: false, en: false, ru: true } }, - }, - }); - }); - - test('get setDictionarySettings results with all parameters', async () => { - const req = await client.setDictionarySettings({ - dictionarySettingsRequest: { - disableStandardEntries: { - plurals: { fr: false, en: false, ru: true }, - stopwords: { fr: false }, - compounds: { ru: true }, - }, - }, - }); +describe('deleteApiKey', () => { + test('deleteApiKey', async () => { + const req = await client.deleteApiKey({ key: 'myTestApiKey' }); expect(req).toMatchObject({ - path: '/1/dictionaries/*/settings', - method: 'PUT', - data: { - disableStandardEntries: { - plurals: { fr: false, en: false, ru: true }, - stopwords: { fr: false }, - compounds: { ru: true }, - }, - }, + path: '/1/keys/myTestApiKey', + method: 'DELETE', }); }); }); -describe('getRule', () => { - test('getRule', async () => { - const req = await client.getRule({ +describe('deleteRule', () => { + test('deleteRule', async () => { + const req = await client.deleteRule({ indexName: 'indexName', objectID: 'id1', }); expect(req).toMatchObject({ path: '/1/indexes/indexName/rules/id1', - method: 'GET', + method: 'DELETE', }); }); }); -describe('searchDictionaryEntries', () => { - test('get searchDictionaryEntries results with minimal parameters', async () => { - const req = await client.searchDictionaryEntries({ - dictionaryName: 'dictionaryName', - searchDictionaryEntries: { query: 'foo' }, +describe('deleteSynonym', () => { + test('deleteSynonym', async () => { + const req = await client.deleteSynonym({ + indexName: 'indexName', + objectID: 'id1', }); expect(req).toMatchObject({ - path: '/1/dictionaries/dictionaryName/search', - method: 'POST', - data: { query: 'foo' }, + path: '/1/indexes/indexName/synonyms/id1', + method: 'DELETE', }); }); +}); - test('get searchDictionaryEntries results with all parameters', async () => { - const req = await client.searchDictionaryEntries({ - dictionaryName: 'dictionaryName', - searchDictionaryEntries: { - query: 'foo', - page: 4, - hitsPerPage: 2, - language: 'fr', - }, - }); +describe('getApiKey', () => { + test('getApiKey', async () => { + const req = await client.getApiKey({ key: 'myTestApiKey' }); expect(req).toMatchObject({ - path: '/1/dictionaries/dictionaryName/search', - method: 'POST', - data: { query: 'foo', page: 4, hitsPerPage: 2, language: 'fr' }, + path: '/1/keys/myTestApiKey', + method: 'GET', }); }); }); -describe('batchRules', () => { - test('batchRules', async () => { - const req = await client.batchRules({ - indexName: 'indexName', - rule: [ - { - objectID: 'a-rule-id', - conditions: [{ pattern: 'smartphone', anchoring: 'contains' }], - consequence: { params: { filters: 'category:smartphone' } }, - }, - { - objectID: 'a-second-rule-id', - conditions: [{ pattern: 'apple', anchoring: 'contains' }], - consequence: { params: { filters: 'brand:apple' } }, - }, - ], - forwardToReplicas: true, - clearExistingRules: true, - }); +describe('getDictionaryLanguages', () => { + test('get getDictionaryLanguages', async () => { + const req = await client.getDictionaryLanguages(); expect(req).toMatchObject({ - path: '/1/indexes/indexName/rules/batch', - method: 'POST', - data: [ - { - objectID: 'a-rule-id', - conditions: [{ pattern: 'smartphone', anchoring: 'contains' }], - consequence: { params: { filters: 'category:smartphone' } }, - }, - { - objectID: 'a-second-rule-id', - conditions: [{ pattern: 'apple', anchoring: 'contains' }], - consequence: { params: { filters: 'brand:apple' } }, - }, - ], + path: '/1/dictionaries/*/languages', + method: 'GET', }); }); }); -describe('updateApiKey', () => { - test('updateApiKey', async () => { - const req = await client.updateApiKey({ - key: 'myApiKey', - apiKey: { - acl: ['search', 'addObject'], - validity: 300, - maxQueriesPerIPPerHour: 100, - maxHitsPerQuery: 20, - }, - }); +describe('getDictionarySettings', () => { + test('get getDictionarySettings results', async () => { + const req = await client.getDictionarySettings(); expect(req).toMatchObject({ - path: '/1/keys/myApiKey', - method: 'PUT', - data: { - acl: ['search', 'addObject'], - validity: 300, - maxQueriesPerIPPerHour: 100, - maxHitsPerQuery: 20, - }, + path: '/1/dictionaries/*/settings', + method: 'GET', }); }); }); -describe('getDictionaryLanguages', () => { - test('get getDictionaryLanguages', async () => { - const req = await client.getDictionaryLanguages(); +describe('getRule', () => { + test('getRule', async () => { + const req = await client.getRule({ + indexName: 'indexName', + objectID: 'id1', + }); expect(req).toMatchObject({ - path: '/1/dictionaries/*/languages', + path: '/1/indexes/indexName/rules/id1', method: 'GET', }); }); }); -describe('deleteApiKey', () => { - test('deleteApiKey', async () => { - const req = await client.deleteApiKey({ key: 'myTestApiKey' }); +describe('getSynonym', () => { + test('getSynonym', async () => { + const req = await client.getSynonym({ + indexName: 'indexName', + objectID: 'id1', + }); expect(req).toMatchObject({ - path: '/1/keys/myTestApiKey', - method: 'DELETE', + path: '/1/indexes/indexName/synonyms/id1', + method: 'GET', }); }); }); -describe('searchRules', () => { - test('searchRules', async () => { - const req = await client.searchRules({ - indexName: 'indexName', - searchRulesParams: { query: 'something' }, - }); +describe('listApiKeys', () => { + test('listApiKeys', async () => { + const req = await client.listApiKeys(); expect(req).toMatchObject({ - path: '/1/indexes/indexName/rules/search', - method: 'POST', - data: { query: 'something' }, + path: '/1/keys', + method: 'GET', }); }); }); -describe('clearAllSynonyms', () => { - test('clearAllSynonyms', async () => { - const req = await client.clearAllSynonyms({ indexName: 'indexName' }); +describe('restoreApiKey', () => { + test('restoreApiKey', async () => { + const req = await client.restoreApiKey({ key: 'myApiKey' }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/synonyms/clear', + path: '/1/keys/myApiKey/restore', method: 'POST', }); }); @@ -359,216 +339,236 @@ describe('saveRule', () => { }); }); -describe('addApiKey', () => { - test('addApiKey', async () => { - const req = await client.addApiKey({ - apiKey: { - acl: ['search', 'addObject'], - description: 'my new api key', - validity: 300, - maxQueriesPerIPPerHour: 100, - maxHitsPerQuery: 20, - }, +describe('saveSynonym', () => { + test('saveSynonym', async () => { + const req = await client.saveSynonym({ + indexName: 'indexName', + objectID: 'id1', + synonymHit: { + objectID: 'id1', + type: 'synonym', + synonyms: ['car', 'vehicule', 'auto'], + }, + forwardToReplicas: true, + }); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/synonyms/id1', + method: 'PUT', + data: { + objectID: 'id1', + type: 'synonym', + synonyms: ['car', 'vehicule', 'auto'], + }, + }); + }); +}); + +describe('saveSynonyms', () => { + test('saveSynonyms', async () => { + const req = await client.saveSynonyms({ + indexName: 'indexName', + synonymHit: [ + { + objectID: 'id1', + type: 'synonym', + synonyms: ['car', 'vehicule', 'auto'], + }, + { + objectID: 'id2', + type: 'onewaysynonym', + input: 'iphone', + synonyms: ['ephone', 'aphone', 'yphone'], + }, + ], + forwardToReplicas: true, + replaceExistingSynonyms: false, + }); + expect(req).toMatchObject({ + path: '/1/indexes/indexName/synonyms/batch', + method: 'POST', + data: [ + { + objectID: 'id1', + type: 'synonym', + synonyms: ['car', 'vehicule', 'auto'], + }, + { + objectID: 'id2', + type: 'onewaysynonym', + input: 'iphone', + synonyms: ['ephone', 'aphone', 'yphone'], + }, + ], + }); + }); +}); + +describe('search', () => { + test('search', async () => { + const req = await client.search({ + indexName: 'indexName', + searchParams: { $objectName: 'Query', query: 'queryString' }, }); expect(req).toMatchObject({ - path: '/1/keys', + path: '/1/indexes/indexName/query', method: 'POST', - data: { - acl: ['search', 'addObject'], - description: 'my new api key', - validity: 300, - maxQueriesPerIPPerHour: 100, - maxHitsPerQuery: 20, - }, + data: { query: 'queryString' }, }); }); }); -describe('restoreApiKey', () => { - test('restoreApiKey', async () => { - const req = await client.restoreApiKey({ key: 'myApiKey' }); +describe('searchDictionaryEntries', () => { + test('get searchDictionaryEntries results with minimal parameters', async () => { + const req = await client.searchDictionaryEntries({ + dictionaryName: 'dictionaryName', + searchDictionaryEntries: { query: 'foo' }, + }); expect(req).toMatchObject({ - path: '/1/keys/myApiKey/restore', + path: '/1/dictionaries/dictionaryName/search', method: 'POST', + data: { query: 'foo' }, }); }); -}); -describe('getApiKey', () => { - test('getApiKey', async () => { - const req = await client.getApiKey({ key: 'myTestApiKey' }); + test('get searchDictionaryEntries results with all parameters', async () => { + const req = await client.searchDictionaryEntries({ + dictionaryName: 'dictionaryName', + searchDictionaryEntries: { + query: 'foo', + page: 4, + hitsPerPage: 2, + language: 'fr', + }, + }); expect(req).toMatchObject({ - path: '/1/keys/myTestApiKey', - method: 'GET', + path: '/1/dictionaries/dictionaryName/search', + method: 'POST', + data: { query: 'foo', page: 4, hitsPerPage: 2, language: 'fr' }, }); }); }); -describe('browse', () => { - test('get browse results with minimal parameters', async () => { - const req = await client.browse({ indexName: 'indexName' }); +describe('searchForFacetValues', () => { + test('get searchForFacetValues results with minimal parameters', async () => { + const req = await client.searchForFacetValues({ + indexName: 'indexName', + facetName: 'facetName', + }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/browse', + path: '/1/indexes/indexName/facets/facetName/query', method: 'POST', }); }); - test('get browse results with all parameters', async () => { - const req = await client.browse({ + test('get searchForFacetValues results with all parameters', async () => { + const req = await client.searchForFacetValues({ indexName: 'indexName', - browseRequest: { + facetName: 'facetName', + searchForFacetValuesRequest: { params: "query=foo&facetFilters=['bar']", - cursor: 'cts', + facetQuery: 'foo', + maxFacetHits: 42, }, }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/browse', + path: '/1/indexes/indexName/facets/facetName/query', method: 'POST', - data: { params: "query=foo&facetFilters=['bar']", cursor: 'cts' }, + data: { + params: "query=foo&facetFilters=['bar']", + facetQuery: 'foo', + maxFacetHits: 42, + }, }); }); }); -describe('deleteSynonym', () => { - test('deleteSynonym', async () => { - const req = await client.deleteSynonym({ +describe('searchRules', () => { + test('searchRules', async () => { + const req = await client.searchRules({ indexName: 'indexName', - objectID: 'id1', + searchRulesParams: { query: 'something' }, }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/synonyms/id1', - method: 'DELETE', + path: '/1/indexes/indexName/rules/search', + method: 'POST', + data: { query: 'something' }, }); }); }); -describe('clearRules', () => { - test('clearRules', async () => { - const req = await client.clearRules({ indexName: 'indexName' }); +describe('searchSynonyms', () => { + test('searchSynonyms', async () => { + const req = await client.searchSynonyms({ + indexName: 'indexName', + query: 'queryString', + type: 'onewaysynonym', + }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/rules/clear', + path: '/1/indexes/indexName/synonyms/search', method: 'POST', }); }); }); -describe('batchDictionaryEntries', () => { - test('get batchDictionaryEntries results with minimal parameters', async () => { - const req = await client.batchDictionaryEntries({ - dictionaryName: 'dictionaryName', - batchDictionaryEntries: { - requests: [ - { action: 'addEntry', body: { objectID: '1', language: 'en' } }, - { action: 'deleteEntry', body: { objectID: '2', language: 'fr' } }, - ], +describe('setDictionarySettings', () => { + test('get setDictionarySettings results with minimal parameters', async () => { + const req = await client.setDictionarySettings({ + dictionarySettingsRequest: { + disableStandardEntries: { plurals: { fr: false, en: false, ru: true } }, }, }); expect(req).toMatchObject({ - path: '/1/dictionaries/dictionaryName/batch', - method: 'POST', + path: '/1/dictionaries/*/settings', + method: 'PUT', data: { - requests: [ - { action: 'addEntry', body: { objectID: '1', language: 'en' } }, - { action: 'deleteEntry', body: { objectID: '2', language: 'fr' } }, - ], + disableStandardEntries: { plurals: { fr: false, en: false, ru: true } }, }, }); }); - test('get batchDictionaryEntries results with all parameters', async () => { - const req = await client.batchDictionaryEntries({ - dictionaryName: 'dictionaryName', - batchDictionaryEntries: { - clearExistingDictionaryEntries: false, - requests: [ - { - action: 'addEntry', - body: { - objectID: '1', - language: 'en', - word: 'yo', - words: ['yo', 'algolia'], - decomposition: ['yo', 'algolia'], - state: 'enabled', - }, - }, - { - action: 'deleteEntry', - body: { - objectID: '2', - language: 'fr', - word: 'salut', - words: ['salut', 'algolia'], - decomposition: ['salut', 'algolia'], - state: 'enabled', - }, - }, - ], + test('get setDictionarySettings results with all parameters', async () => { + const req = await client.setDictionarySettings({ + dictionarySettingsRequest: { + disableStandardEntries: { + plurals: { fr: false, en: false, ru: true }, + stopwords: { fr: false }, + compounds: { ru: true }, + }, }, }); expect(req).toMatchObject({ - path: '/1/dictionaries/dictionaryName/batch', - method: 'POST', + path: '/1/dictionaries/*/settings', + method: 'PUT', data: { - clearExistingDictionaryEntries: false, - requests: [ - { - action: 'addEntry', - body: { - objectID: '1', - language: 'en', - word: 'yo', - words: ['yo', 'algolia'], - decomposition: ['yo', 'algolia'], - state: 'enabled', - }, - }, - { - action: 'deleteEntry', - body: { - objectID: '2', - language: 'fr', - word: 'salut', - words: ['salut', 'algolia'], - decomposition: ['salut', 'algolia'], - state: 'enabled', - }, - }, - ], + disableStandardEntries: { + plurals: { fr: false, en: false, ru: true }, + stopwords: { fr: false }, + compounds: { ru: true }, + }, }, }); }); }); -describe('listApiKeys', () => { - test('listApiKeys', async () => { - const req = await client.listApiKeys(); - expect(req).toMatchObject({ - path: '/1/keys', - method: 'GET', - }); - }); -}); - -describe('saveSynonym', () => { - test('saveSynonym', async () => { - const req = await client.saveSynonym({ - indexName: 'indexName', - objectID: 'id1', - synonymHit: { - objectID: 'id1', - type: 'synonym', - synonyms: ['car', 'vehicule', 'auto'], +describe('updateApiKey', () => { + test('updateApiKey', async () => { + const req = await client.updateApiKey({ + key: 'myApiKey', + apiKey: { + acl: ['search', 'addObject'], + validity: 300, + maxQueriesPerIPPerHour: 100, + maxHitsPerQuery: 20, }, - forwardToReplicas: true, }); expect(req).toMatchObject({ - path: '/1/indexes/indexName/synonyms/id1', + path: '/1/keys/myApiKey', method: 'PUT', data: { - objectID: 'id1', - type: 'synonym', - synonyms: ['car', 'vehicule', 'auto'], + acl: ['search', 'addObject'], + validity: 300, + maxQueriesPerIPPerHour: 100, + maxHitsPerQuery: 20, }, }); }); diff --git a/tests/package.json b/tests/package.json index 2540c0b13f..c9539970b8 100644 --- a/tests/package.json +++ b/tests/package.json @@ -8,8 +8,8 @@ "start": "yarn build && node dist/tests/generateCTS.js ${0:-all} && yarn lint:fix" }, "devDependencies": { + "@algolia/client-insights": "5.0.0", "@algolia/client-search": "5.0.0", - "@algolia/client-insights" : "5.0.0", "@algolia/recommend": "5.0.0", "@apidevtools/swagger-parser": "10.0.3", "@types/jest": "27.0.3", diff --git a/yarn.lock b/yarn.lock index 1c793e2854..3d1b3d7bfe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,7 +40,7 @@ __metadata: languageName: unknown linkType: soft -"@algolia/client-insights@workspace:clients/algoliasearch-client-javascript/client-insights": +"@algolia/client-insights@5.0.0, @algolia/client-insights@workspace:clients/algoliasearch-client-javascript/client-insights": version: 0.0.0-use.local resolution: "@algolia/client-insights@workspace:clients/algoliasearch-client-javascript/client-insights" dependencies: @@ -5692,6 +5692,7 @@ fsevents@^2.3.2: version: 0.0.0-use.local resolution: "tests@workspace:tests" dependencies: + "@algolia/client-insights": 5.0.0 "@algolia/client-search": 5.0.0 "@algolia/recommend": 5.0.0 "@apidevtools/swagger-parser": 10.0.3 @@ -5941,6 +5942,26 @@ typescript@4.5.4: languageName: node linkType: hard +"typescript@npm:4.5.2": + version: 4.5.2 + resolution: "typescript@npm:4.5.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 74f9ce65d532bdf5d0214b3f60cf37992180023388c87a11ee6f838a803067ef0b63c600fa501b0deb07f989257dce1e244c9635ed79feca40bbccf6e0aa1ebc + languageName: node + linkType: hard + +"typescript@patch:typescript@4.5.2#~builtin": + version: 4.5.2 + resolution: "typescript@patch:typescript@npm%3A4.5.2#~builtin::version=4.5.2&hash=493e53" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 53838d56aba6fcc947d63aa0771e5d966b1b648fddafed6e221d7f38c71219c4e036ece8cfe9e35ed80cf5a35ff4eb958934c993f99c3233773ec4f9ccd53f69 + languageName: node + linkType: hard + "typescript@patch:typescript@4.5.4#~builtin": version: 4.5.4 resolution: "typescript@patch:typescript@npm%3A4.5.4#~builtin::version=4.5.4&hash=493e53" From 9240fb6f93ba332602308a16c92d9ec5f00c65b8 Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Wed, 5 Jan 2022 16:33:01 +0100 Subject: [PATCH 04/10] fix: update typescript version --- .../client-insights/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/algoliasearch-client-javascript/client-insights/package.json b/clients/algoliasearch-client-javascript/client-insights/package.json index 5da4f673fe..987293db90 100644 --- a/clients/algoliasearch-client-javascript/client-insights/package.json +++ b/clients/algoliasearch-client-javascript/client-insights/package.json @@ -19,6 +19,6 @@ }, "devDependencies": { "@types/node": "16.11.11", - "typescript": "4.5.2" + "typescript": "4.5.4" } } From 4692fbf9b4069529d53325fd629f6396ba91c187 Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Wed, 5 Jan 2022 16:35:11 +0100 Subject: [PATCH 05/10] fix: yarn.lock --- yarn.lock | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3d1b3d7bfe..5017abf761 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45,7 +45,7 @@ __metadata: resolution: "@algolia/client-insights@workspace:clients/algoliasearch-client-javascript/client-insights" dependencies: "@types/node": 16.11.11 - typescript: 4.5.2 + typescript: 4.5.4 languageName: unknown linkType: soft @@ -5942,26 +5942,6 @@ typescript@4.5.4: languageName: node linkType: hard -"typescript@npm:4.5.2": - version: 4.5.2 - resolution: "typescript@npm:4.5.2" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 74f9ce65d532bdf5d0214b3f60cf37992180023388c87a11ee6f838a803067ef0b63c600fa501b0deb07f989257dce1e244c9635ed79feca40bbccf6e0aa1ebc - languageName: node - linkType: hard - -"typescript@patch:typescript@4.5.2#~builtin": - version: 4.5.2 - resolution: "typescript@patch:typescript@npm%3A4.5.2#~builtin::version=4.5.2&hash=493e53" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 53838d56aba6fcc947d63aa0771e5d966b1b648fddafed6e221d7f38c71219c4e036ece8cfe9e35ed80cf5a35ff4eb958934c993f99c3233773ec4f9ccd53f69 - languageName: node - linkType: hard - "typescript@patch:typescript@4.5.4#~builtin": version: 4.5.4 resolution: "typescript@patch:typescript@npm%3A4.5.4#~builtin::version=4.5.4&hash=493e53" From a06227d36f2a982d7e9b3fb699263d92e270904e Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Wed, 5 Jan 2022 16:52:36 +0100 Subject: [PATCH 06/10] fix: update CI --- .github/workflows/check.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a3cc084a2c..49d20ed667 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -36,6 +36,9 @@ jobs: - name: Checking analytics specs run: yarn build:specs analytics + - name: Checking insights specs + run: yarn build:specs insights + - name: Lint run: yarn specs:lint @@ -72,6 +75,9 @@ jobs: - name: Build analytics client run: yarn build:clients javascript analytics + - name: Build insights client + run: yarn build:clients javascript insights + - name: Lint run: yarn lint From e79023cc037a017255db849bf50fa89ce4f449ac Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Wed, 5 Jan 2022 16:55:45 +0100 Subject: [PATCH 07/10] fix: CI --- .github/workflows/check.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 49d20ed667..d826d7d3a8 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -75,6 +75,9 @@ jobs: - name: Build analytics client run: yarn build:clients javascript analytics + - name: Generate insights client + run: yarn generate javascript insights + - name: Build insights client run: yarn build:clients javascript insights From 4c783945d1b5e8da1156490d702cecb723a9c09a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vannicatte?= Date: Thu, 6 Jan 2022 10:17:42 +0100 Subject: [PATCH 08/10] add insights jobs --- .github/actions/setup/action.yml | 2 +- .github/workflows/check.yml | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 5a98ff3c89..cd3cf309e8 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -91,7 +91,7 @@ outputs: value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.ANALYTICS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_ANALYTICS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} RUN_JS_CLIENT_INSIGHTS: description: 'Determine if the `client_javascript_insights` 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.ANALYTICS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_INSIGHTS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} + value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.INSIGHTS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_INSIGHTS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} # java client variables RUN_JAVA_CLIENT: diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 4b4cad513a..54aa39cc8f 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -92,12 +92,19 @@ jobs: - name: Checking analytics specs run: yarn build:specs analytics + specs_insights: + runs-on: ubuntu-20.04 + needs: setup + if: ${{ always() && needs.setup.outputs.RUN_SPECS_INSIGHTS == 'true' }} + steps: + - uses: actions/checkout@v2 + + - name: Restore cache + uses: ./.github/actions/cache + - name: Checking insights specs run: yarn build:specs insights - - name: Lint - run: yarn specs:lint - client_javascript_search: runs-on: ubuntu-20.04 needs: [specs_search] @@ -258,6 +265,7 @@ jobs: client_javascript_recommend, client_javascript_perso, client_javascript_analytics, + client_javascript_insights, client_java_search, ] if: ${{ always() && needs.setup.outputs.RUN_CTS == 'true' }} From 9e757b00a14727f2704f8527616fe54d43fa8874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vannicatte?= Date: Thu, 6 Jan 2022 10:38:44 +0100 Subject: [PATCH 09/10] retrieve outputs variable --- .github/workflows/check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 54aa39cc8f..7b5c6cc142 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -35,6 +35,7 @@ jobs: RUN_JS_CLIENT_RECOMMEND: ${{ steps.setup.outputs.RUN_JS_CLIENT_RECOMMEND }} 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 }} RUN_JAVA_CLIENT: ${{ steps.setup.outputs.RUN_JAVA_CLIENT }} From 39eebf2cb0db08d45df3b59ea34e3feee41d1ff1 Mon Sep 17 00:00:00 2001 From: Damien Couchez Date: Thu, 6 Jan 2022 11:44:42 +0100 Subject: [PATCH 10/10] fix: last comments --- .github/actions/cache/action.yml | 2 +- .github/actions/setup/action.yml | 26 ++++----- .github/workflows/check.yml | 15 +++-- .../client-insights/.openapi-generator-ignore | 18 +----- .../client-insights/git_push.sh | 57 ------------------- .../client-insights/model/insightEvents.ts | 4 +- .../client-insights/src/insightsApi.ts | 4 +- openapitools.json | 2 - specs/insights/paths/pushEvents.yml | 8 +-- templates/javascript/api-single.mustache | 2 +- tests/CTS/clients/insights/pushEvents.json | 1 - 11 files changed, 31 insertions(+), 108 deletions(-) delete mode 100644 clients/algoliasearch-client-javascript/client-insights/git_push.sh diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml index 2769c04744..8672628a1e 100644 --- a/.github/actions/cache/action.yml +++ b/.github/actions/cache/action.yml @@ -4,7 +4,7 @@ description: Restore cached dependencies. inputs: job: - description: 'The job that requires this composite' + description: The job that requires this composite required: true runs: diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index cd3cf309e8..c560a53b3c 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -55,42 +55,42 @@ runs: outputs: # specs variables RUN_SPECS_SEARCH: - description: 'Determine if the `specs_search` job should run' + description: Determine if the `specs_search` 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.SEARCH_SPECS_CHANGED > 0 }} RUN_SPECS_RECOMMEND: - description: 'Determine if the `specs_recommend` job should run' + 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' + 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_PERSO: - description: 'Determine if the `specs_perso` job should run' + 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 }} RUN_SPECS_INSIGHTS: - description: 'Determine if the `specs_insights` job should run' + description: Determine if the `specs_insights` 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.INSIGHTS_SPECS_CHANGED > 0 }} RUN_SPECS_ANALYTICS: - description: 'Determine if the `specs_analytics` job should run' + description: Determine if the `specs_analytics` 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.ANALYTICS_SPECS_CHANGED > 0 }} RUN_SPECS_AB: - description: 'Determine if the `specs_ab` job should run' + description: Determine if the `specs_ab` 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.AB_SPECS_CHANGED > 0 }} # js client variables RUN_JS_CLIENT_SEARCH: - description: 'Determine if the `client_javascript_search` job should run' + description: Determine if the `client_javascript_search` 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.SEARCH_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_SEARCH_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} RUN_JS_CLIENT_RECOMMEND: - description: 'Determine if the `client_javascript_recommend` job should run' + 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_PERSO: - description: 'Determine if the `client_javascript_perso` job should run' + 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 }} RUN_JS_CLIENT_ANALYTICS: - description: 'Determine if the `client_javascript_analytics` job should run' + description: Determine if the `client_javascript_analytics` 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.ANALYTICS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_ANALYTICS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} RUN_JS_CLIENT_INSIGHTS: - description: 'Determine if the `client_javascript_insights` job should run' + description: Determine if the `client_javascript_insights` 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.INSIGHTS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_INSIGHTS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }} # java client variables @@ -100,5 +100,5 @@ outputs: # cts variables RUN_CTS: - description: 'Determine if the `cts` job should run' + description: Determine if the `cts` job should run value: ${{ github.ref_name == 'main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.SPECS_CHANGED > 0 || steps.diff.outputs.TESTS_CHANGED > 0 || steps.diff.outputs.JS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 || steps.diff.outputs.JAVA_CLIENT_CHANGED > 0 || steps.diff.outputs.JAVA_TEMPLATE_CHANGED > 0 }} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7b5c6cc142..bb1992bad5 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -261,14 +261,13 @@ jobs: cts: runs-on: ubuntu-20.04 needs: - [ - client_javascript_search, - client_javascript_recommend, - client_javascript_perso, - client_javascript_analytics, - client_javascript_insights, - client_java_search, - ] + - client_javascript_search + - client_javascript_recommend + - client_javascript_perso + - client_javascript_analytics + - client_javascript_insights + - client_java_search + if: ${{ always() && needs.setup.outputs.RUN_CTS == 'true' }} steps: - uses: actions/checkout@v2 diff --git a/clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore b/clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore index 7484ee590a..abfb5c9516 100644 --- a/clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore +++ b/clients/algoliasearch-client-javascript/client-insights/.openapi-generator-ignore @@ -4,20 +4,4 @@ # Use this file to prevent files from being overwritten by the generator. # The patterns follow closely to .gitignore or .dockerignore. -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md +git_push.sh diff --git a/clients/algoliasearch-client-javascript/client-insights/git_push.sh b/clients/algoliasearch-client-javascript/client-insights/git_push.sh deleted file mode 100644 index b151edf823..0000000000 --- a/clients/algoliasearch-client-javascript/client-insights/git_push.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ -# -# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" - -git_user_id=$1 -git_repo_id=$2 -release_note=$3 -git_host=$4 - -if [ "$git_host" = "" ]; then - git_host="algolia" - echo "[INFO] No command line input provided. Set \$git_host to $git_host" -fi - -if [ "$git_user_id" = "" ]; then - git_user_id="algolia" - echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" -fi - -if [ "$git_repo_id" = "" ]; then - git_repo_id="algoliasearch-client-javascript" - echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" -fi - -if [ "$release_note" = "" ]; then - release_note="Minor update" - echo "[INFO] No command line input provided. Set \$release_note to $release_note" -fi - -# Initialize the local directory as a Git repository -git init - -# Adds the files in the local repository and stages them for commit. -git add . - -# Commits the tracked changes and prepares them to be pushed to a remote repository. -git commit -m "$release_note" - -# Sets the new remote -git_remote=$(git remote) -if [ "$git_remote" = "" ]; then # git remote not defined - - if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." - git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git - else - git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git - fi - -fi - -git pull origin master - -# Pushes (Forces) the changes in the local repository up to the remote repository -echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" -git push origin master 2>&1 | grep -v 'To https' diff --git a/clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts b/clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts index 0431c466c7..69728810b2 100644 --- a/clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts +++ b/clients/algoliasearch-client-javascript/client-insights/model/insightEvents.ts @@ -1,11 +1,11 @@ import type { InsightEvent } from './insightEvent'; /** - * Object containing the events sent to the Insights API. + * Object containing the events sent. */ export type InsightEvents = { /** - * Array of events sent to the Insights API. + * Array of events sent. */ events: InsightEvent[]; }; diff --git a/clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts b/clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts index 0268c08f27..9f0ad84cdd 100644 --- a/clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts +++ b/clients/algoliasearch-client-javascript/client-insights/src/insightsApi.ts @@ -75,9 +75,9 @@ export class InsightsApi { } /** - * This command pushes an array of events to the Insights API. + * This command pushes an array of events. * - * @summary This command pushes an array of events to the Insights API. + * @summary Pushes an array of events. * @param pushEvents - The pushEvents parameters. * @param pushEvents.insightEvents - The insightEvents. */ diff --git a/openapitools.json b/openapitools.json index 185bb9a734..8cf8fe32b6 100644 --- a/openapitools.json +++ b/openapitools.json @@ -21,7 +21,6 @@ "npmVersion": "5.0.0", "packageName": "@algolia/client-search", - "useAppIdInHost": true, "isSearchHost": true } }, @@ -43,7 +42,6 @@ "npmVersion": "5.0.0", "packageName": "@algolia/recommend", - "useAppIdInHost": true, "isSearchHost": true } }, diff --git a/specs/insights/paths/pushEvents.yml b/specs/insights/paths/pushEvents.yml index 632f51372d..945370372d 100644 --- a/specs/insights/paths/pushEvents.yml +++ b/specs/insights/paths/pushEvents.yml @@ -2,8 +2,8 @@ post: tags: - insights operationId: pushEvents - description: This command pushes an array of events to the Insights API. - summary: This command pushes an array of events to the Insights API. + description: This command pushes an array of events. + summary: Pushes an array of events. requestBody: required: true content: @@ -11,14 +11,14 @@ post: schema: title: InsightEvents type: object - description: Object containing the events sent to the Insights API. + description: Object containing the events sent. additionalProperties: false required: - events properties: events: type: array - description: Array of events sent to the Insights API. + description: Array of events sent. items: title: InsightEvent type: object diff --git a/templates/javascript/api-single.mustache b/templates/javascript/api-single.mustache index a69563311b..5a0416ee55 100644 --- a/templates/javascript/api-single.mustache +++ b/templates/javascript/api-single.mustache @@ -68,7 +68,7 @@ export class {{classname}} { this.transporter = new Transporter({ hosts: options?.hosts ?? this.getDefaultHosts( - {{#useAppIdInHost}}appId{{/useAppIdInHost}} + {{#isSearchHost}}appId{{/isSearchHost}} {{#hasRegionalHost}}region{{/hasRegionalHost}} ), baseHeaders: { diff --git a/tests/CTS/clients/insights/pushEvents.json b/tests/CTS/clients/insights/pushEvents.json index fcc367001b..27ebfaf751 100644 --- a/tests/CTS/clients/insights/pushEvents.json +++ b/tests/CTS/clients/insights/pushEvents.json @@ -73,4 +73,3 @@ } } ] - \ No newline at end of file