'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var clientCommon = require('@algolia/client-common'); var transporter = require('@algolia/transporter'); var requesterCommon = require('@algolia/requester-common'); var crypto = require('crypto'); function createBrowsablePromise(options) { const browse = (data) => { return options.request(data).then(response => { /** * First we send to the developer the * batch retrieved from the API. */ if (options.batch !== undefined) { options.batch(response.hits); } /** * Then, we ask to the browse concrete implementation * if we should stop browsing. As example, the `browseObjects` * method will stop if the cursor is not present on the response. */ if (options.shouldStop(response)) { return undefined; } /** * Finally, if the response contains a cursor, we browse to the next * batch using that same cursor. Otherwise, we just use the traditional * browsing using the page element. */ if (response.cursor) { return browse({ cursor: response.cursor, }); } return browse({ page: (data.page || 0) + 1, }); }); }; return browse({}); } const createSearchClient = options => { const appId = options.appId; const auth = clientCommon.createAuth(options.authMode !== undefined ? options.authMode : clientCommon.AuthMode.WithinHeaders, appId, options.apiKey); const transporter$1 = transporter.createTransporter({ hosts: [ { url: `${appId}-dsn.algolia.net`, accept: transporter.CallEnum.Read }, { url: `${appId}.algolia.net`, accept: transporter.CallEnum.Write }, ].concat(clientCommon.shuffle([ { url: `${appId}-1.algolianet.com` }, { url: `${appId}-2.algolianet.com` }, { url: `${appId}-3.algolianet.com` }, ])), ...options, headers: { ...auth.headers(), ...{ 'content-type': 'application/x-www-form-urlencoded' }, ...options.headers, }, queryParameters: { ...auth.queryParameters(), ...options.queryParameters, }, }); const base = { transporter: transporter$1, appId, addAlgoliaAgent(segment, version) { transporter$1.userAgent.add({ segment, version }); }, clearCache() { return Promise.all([ transporter$1.requestsCache.clear(), transporter$1.responsesCache.clear(), ]).then(() => undefined); }, }; return clientCommon.addMethods(base, options.methods); }; function createMissingObjectIDError() { return { name: 'MissingObjectIDError', message: 'All objects must have an unique objectID ' + '(like a primary key) to be valid. ' + 'Algolia is also able to generate objectIDs ' + "automatically but *it's not recommended*. " + "To do it, use the `{'autoGenerateObjectIDIfNotExist': true}` option.", }; } function createObjectNotFoundError() { return { name: 'ObjectNotFoundError', message: 'Object not found.', }; } function createValidUntilNotFoundError() { return { name: 'ValidUntilNotFoundError', message: 'ValidUntil not found in given secured api key.', }; } const addApiKey = (base) => { return (acl, requestOptions) => { const { queryParameters, ...options } = requestOptions || {}; const data = { acl, ...(queryParameters !== undefined ? { queryParameters } : {}), }; const wait = (response, waitRequestOptions) => { return clientCommon.createRetryablePromise(retry => { return getApiKey(base)(response.key, waitRequestOptions).catch((apiError) => { if (apiError.status !== 404) { throw apiError; } return retry(); }); }); }; return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: '1/keys', data, }, options), wait); }; }; const assignUserID = (base) => { return (userID, clusterName, requestOptions) => { const mappedRequestOptions = transporter.createMappedRequestOptions(requestOptions); // eslint-disable-next-line functional/immutable-data mappedRequestOptions.queryParameters['X-Algolia-User-ID'] = userID; return base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: '1/clusters/mapping', data: { cluster: clusterName }, }, mappedRequestOptions); }; }; const assignUserIDs = (base) => { return (userIDs, clusterName, requestOptions) => { return base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: '1/clusters/mapping/batch', data: { users: userIDs, cluster: clusterName, }, }, requestOptions); }; }; const clearDictionaryEntries = (base) => { return (dictionary, requestOptions) => { return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('/1/dictionaries/%s/batch', dictionary), data: { clearExistingDictionaryEntries: true, requests: { action: 'addEntry', body: [] }, }, }, requestOptions), (response, waitRequestOptions) => waitAppTask(base)(response.taskID, waitRequestOptions)); }; }; const copyIndex = (base) => { return (from, to, requestOptions) => { const wait = (response, waitRequestOptions) => { return initIndex(base)(from, { methods: { waitTask }, }).waitTask(response.taskID, waitRequestOptions); }; return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/operation', from), data: { operation: 'copy', destination: to, }, }, requestOptions), wait); }; }; const copyRules = (base) => { return (from, to, requestOptions) => { return copyIndex(base)(from, to, { ...requestOptions, scope: [ScopeEnum.Rules], }); }; }; const copySettings = (base) => { return (from, to, requestOptions) => { return copyIndex(base)(from, to, { ...requestOptions, scope: [ScopeEnum.Settings], }); }; }; const copySynonyms = (base) => { return (from, to, requestOptions) => { return copyIndex(base)(from, to, { ...requestOptions, scope: [ScopeEnum.Synonyms], }); }; }; const customRequest = (base) => { return (request, requestOptions) => { if (request.method === requesterCommon.MethodEnum.Get) { return base.transporter.read(request, requestOptions); } return base.transporter.write(request, requestOptions); }; }; const deleteApiKey = (base) => { return (apiKey, requestOptions) => { const wait = (_, waitRequestOptions) => { return clientCommon.createRetryablePromise(retry => { return getApiKey(base)(apiKey, waitRequestOptions) .then(retry) .catch((apiError) => { if (apiError.status !== 404) { throw apiError; } }); }); }; return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Delete, path: clientCommon.encode('1/keys/%s', apiKey), }, requestOptions), wait); }; }; const deleteDictionaryEntries = (base) => { return (dictionary, objectIDs, requestOptions) => { const requests = objectIDs.map(objectID => ({ action: 'deleteEntry', body: { objectID }, })); return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('/1/dictionaries/%s/batch', dictionary), data: { clearExistingDictionaryEntries: false, requests }, }, requestOptions), (response, waitRequestOptions) => waitAppTask(base)(response.taskID, waitRequestOptions)); }; }; const generateSecuredApiKey = () => { return (parentApiKey, restrictions) => { const queryParameters = transporter.serializeQueryParameters(restrictions); const securedKey = crypto.createHmac('sha256', parentApiKey) .update(queryParameters) .digest('hex'); return Buffer.from(securedKey + queryParameters).toString('base64'); }; }; const getApiKey = (base) => { return (apiKey, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode('1/keys/%s', apiKey), }, requestOptions); }; }; const getAppTask = (base) => { return (taskID, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode('1/task/%s', taskID.toString()), }, requestOptions); }; }; const getDictionarySettings = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '/1/dictionaries/*/settings', }, requestOptions); }; }; const getLogs = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '1/logs', }, requestOptions); }; }; const getSecuredApiKeyRemainingValidity = () => { return (securedApiKey) => { const decodedString = Buffer.from(securedApiKey, 'base64').toString('ascii'); const regex = /validUntil=(\d+)/; const match = decodedString.match(regex); if (match === null) { throw createValidUntilNotFoundError(); } return parseInt(match[1], 10) - Math.round(new Date().getTime() / 1000); }; }; const getTopUserIDs = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '1/clusters/mapping/top', }, requestOptions); }; }; const getUserID = (base) => { return (userID, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode('1/clusters/mapping/%s', userID), }, requestOptions); }; }; const hasPendingMappings = (base) => { return (requestOptions) => { const { retrieveMappings, ...options } = requestOptions || {}; if (retrieveMappings === true) { // eslint-disable-next-line functional/immutable-data options.getClusters = true; } return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '1/clusters/mapping/pending', }, options); }; }; const initIndex = (base) => { return (indexName, options = {}) => { const searchIndex = { transporter: base.transporter, appId: base.appId, indexName, }; return clientCommon.addMethods(searchIndex, options.methods); }; }; const listApiKeys = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '1/keys', }, requestOptions); }; }; const listClusters = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '1/clusters', }, requestOptions); }; }; const listIndices = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '1/indexes', }, requestOptions); }; }; const listUserIDs = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: '1/clusters/mapping', }, requestOptions); }; }; const moveIndex = (base) => { return (from, to, requestOptions) => { const wait = (response, waitRequestOptions) => { return initIndex(base)(from, { methods: { waitTask }, }).waitTask(response.taskID, waitRequestOptions); }; return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/operation', from), data: { operation: 'move', destination: to, }, }, requestOptions), wait); }; }; const multipleBatch = (base) => { return (requests, requestOptions) => { const wait = (response, waitRequestOptions) => { return Promise.all(Object.keys(response.taskID).map(indexName => { return initIndex(base)(indexName, { methods: { waitTask }, }).waitTask(response.taskID[indexName], waitRequestOptions); })); }; return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: '1/indexes/*/batch', data: { requests, }, }, requestOptions), wait); }; }; const multipleGetObjects = (base) => { return (requests, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: '1/indexes/*/objects', data: { requests, }, }, requestOptions); }; }; const multipleQueries = (base) => { return (queries, requestOptions) => { const requests = queries.map(query => { return { ...query, params: transporter.serializeQueryParameters(query.params || {}), }; }); return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: '1/indexes/*/queries', data: { requests, }, cacheable: true, }, requestOptions); }; }; const multipleSearchForFacetValues = (base) => { return (queries, requestOptions) => { return Promise.all(queries.map(query => { const { facetName, facetQuery, ...params } = query.params; return initIndex(base)(query.indexName, { methods: { searchForFacetValues }, }).searchForFacetValues(facetName, facetQuery, { ...requestOptions, ...params, }); })); }; }; const removeUserID = (base) => { return (userID, requestOptions) => { const mappedRequestOptions = transporter.createMappedRequestOptions(requestOptions); // eslint-disable-next-line functional/immutable-data mappedRequestOptions.queryParameters['X-Algolia-User-ID'] = userID; return base.transporter.write({ method: requesterCommon.MethodEnum.Delete, path: '1/clusters/mapping', }, mappedRequestOptions); }; }; const replaceDictionaryEntries = (base) => { return (dictionary, entries, requestOptions) => { const requests = entries.map(entry => ({ action: 'addEntry', body: entry, })); return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('/1/dictionaries/%s/batch', dictionary), data: { clearExistingDictionaryEntries: true, requests }, }, requestOptions), (response, waitRequestOptions) => waitAppTask(base)(response.taskID, waitRequestOptions)); }; }; const restoreApiKey = (base) => { return (apiKey, requestOptions) => { const wait = (_, waitRequestOptions) => { return clientCommon.createRetryablePromise(retry => { return getApiKey(base)(apiKey, waitRequestOptions).catch((apiError) => { if (apiError.status !== 404) { throw apiError; } return retry(); }); }); }; return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/keys/%s/restore', apiKey), }, requestOptions), wait); }; }; const saveDictionaryEntries = (base) => { return (dictionary, entries, requestOptions) => { const requests = entries.map(entry => ({ action: 'addEntry', body: entry, })); return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('/1/dictionaries/%s/batch', dictionary), data: { clearExistingDictionaryEntries: false, requests }, }, requestOptions), (response, waitRequestOptions) => waitAppTask(base)(response.taskID, waitRequestOptions)); }; }; const searchDictionaryEntries = (base) => { return (dictionary, query, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('/1/dictionaries/%s/search', dictionary), data: { query, }, cacheable: true, }, requestOptions); }; }; const searchUserIDs = (base) => { return (query, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: '1/clusters/mapping/search', data: { query, }, }, requestOptions); }; }; const setDictionarySettings = (base) => { return (settings, requestOptions) => { return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Put, path: '/1/dictionaries/*/settings', data: settings, }, requestOptions), (response, waitRequestOptions) => waitAppTask(base)(response.taskID, waitRequestOptions)); }; }; const updateApiKey = (base) => { return (apiKey, requestOptions) => { const updatedFields = Object.assign({}, requestOptions); const { queryParameters, ...options } = requestOptions || {}; const data = queryParameters ? { queryParameters } : {}; const apiKeyFields = [ 'acl', 'indexes', 'referers', 'restrictSources', 'queryParameters', 'description', 'maxQueriesPerIPPerHour', 'maxHitsPerQuery', ]; // Check that all the fields retrieved through getApiKey are the same as the ones we wanted to update const hasChanged = (getApiKeyResponse) => { return Object.keys(updatedFields) .filter((updatedField) => apiKeyFields.indexOf(updatedField) !== -1) .every(updatedField => { // If the field is an array, we need to check that they are the same length and that all the values are the same if (Array.isArray(getApiKeyResponse[updatedField]) && Array.isArray(updatedFields[updatedField])) { const getApiKeyResponseArray = getApiKeyResponse[updatedField]; return (getApiKeyResponseArray.length === updatedFields[updatedField].length && getApiKeyResponseArray.every((value, index) => value === updatedFields[updatedField][index])); } else { return getApiKeyResponse[updatedField] === updatedFields[updatedField]; } }); }; const wait = (_, waitRequestOptions) => clientCommon.createRetryablePromise(retry => { return getApiKey(base)(apiKey, waitRequestOptions).then(getApiKeyResponse => { return hasChanged(getApiKeyResponse) ? Promise.resolve() : retry(); }); }); return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Put, path: clientCommon.encode('1/keys/%s', apiKey), data, }, options), wait); }; }; const waitAppTask = (base) => { return (taskID, requestOptions) => { return clientCommon.createRetryablePromise(retry => { return getAppTask(base)(taskID, requestOptions).then(response => { return response.status !== 'published' ? retry() : undefined; }); }); }; }; const batch = (base) => { return (requests, requestOptions) => { const wait = (response, waitRequestOptions) => { return waitTask(base)(response.taskID, waitRequestOptions); }; return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/batch', base.indexName), data: { requests, }, }, requestOptions), wait); }; }; const browseObjects = (base) => { return (requestOptions) => { return createBrowsablePromise({ shouldStop: response => response.cursor === undefined, ...requestOptions, request: (data) => base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/browse', base.indexName), data, }, requestOptions), }); }; }; const browseRules = (base) => { return (requestOptions) => { const options = { hitsPerPage: 1000, ...requestOptions, }; return createBrowsablePromise({ shouldStop: response => response.hits.length < options.hitsPerPage, ...options, request(data) { return searchRules(base)('', { ...options, ...data }).then((response) => { return { ...response, hits: response.hits.map(rule => { // eslint-disable-next-line functional/immutable-data,no-param-reassign delete rule._highlightResult; return rule; }), }; }); }, }); }; }; const browseSynonyms = (base) => { return (requestOptions) => { const options = { hitsPerPage: 1000, ...requestOptions, }; return createBrowsablePromise({ shouldStop: response => response.hits.length < options.hitsPerPage, ...options, request(data) { return searchSynonyms(base)('', { ...options, ...data }).then((response) => { return { ...response, hits: response.hits.map(synonym => { // eslint-disable-next-line functional/immutable-data,no-param-reassign delete synonym._highlightResult; return synonym; }), }; }); }, }); }; }; const chunkedBatch = (base) => { return (bodies, action, requestOptions) => { const { batchSize, ...options } = requestOptions || {}; const response = { taskIDs: [], objectIDs: [], }; const forEachBatch = (lastIndex = 0) => { // eslint-disable-next-line functional/prefer-readonly-type const bodiesChunk = []; // eslint-disable-next-line functional/no-let let index; /* eslint-disable-next-line functional/no-loop-statement */ for (index = lastIndex; index < bodies.length; index++) { // eslint-disable-next-line functional/immutable-data bodiesChunk.push(bodies[index]); if (bodiesChunk.length === (batchSize || 1000)) { break; } } if (bodiesChunk.length === 0) { return Promise.resolve(response); } return batch(base)(bodiesChunk.map(body => { return { action, body, }; }), options).then(res => { response.objectIDs = response.objectIDs.concat(res.objectIDs); // eslint-disable-line functional/immutable-data response.taskIDs.push(res.taskID); // eslint-disable-line functional/immutable-data index++; return forEachBatch(index); }); }; return clientCommon.createWaitablePromise(forEachBatch(), (chunkedBatchResponse, waitRequestOptions) => { return Promise.all(chunkedBatchResponse.taskIDs.map(taskID => { return waitTask(base)(taskID, waitRequestOptions); })); }); }; }; const clearObjects = (base) => { return (requestOptions) => { return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/clear', base.indexName), }, requestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const clearRules = (base) => { return (requestOptions) => { const { forwardToReplicas, ...options } = requestOptions || {}; const mappedRequestOptions = transporter.createMappedRequestOptions(options); if (forwardToReplicas) { mappedRequestOptions.queryParameters.forwardToReplicas = 1; // eslint-disable-line functional/immutable-data } return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/rules/clear', base.indexName), }, mappedRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const clearSynonyms = (base) => { return (requestOptions) => { const { forwardToReplicas, ...options } = requestOptions || {}; const mappedRequestOptions = transporter.createMappedRequestOptions(options); if (forwardToReplicas) { mappedRequestOptions.queryParameters.forwardToReplicas = 1; // eslint-disable-line functional/immutable-data } return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/synonyms/clear', base.indexName), }, mappedRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const deleteBy = (base) => { return (filters, requestOptions) => { return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/deleteByQuery', base.indexName), data: filters, }, requestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const deleteIndex = (base) => { return (requestOptions) => { return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Delete, path: clientCommon.encode('1/indexes/%s', base.indexName), }, requestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const deleteObject = (base) => { return (objectID, requestOptions) => { return clientCommon.createWaitablePromise(deleteObjects(base)([objectID], requestOptions).then(response => { return { taskID: response.taskIDs[0] }; }), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const deleteObjects = (base) => { return (objectIDs, requestOptions) => { const objects = objectIDs.map(objectID => { return { objectID }; }); return chunkedBatch(base)(objects, BatchActionEnum.DeleteObject, requestOptions); }; }; const deleteRule = (base) => { return (objectID, requestOptions) => { const { forwardToReplicas, ...options } = requestOptions || {}; const mappedRequestOptions = transporter.createMappedRequestOptions(options); if (forwardToReplicas) { mappedRequestOptions.queryParameters.forwardToReplicas = 1; // eslint-disable-line functional/immutable-data } return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Delete, path: clientCommon.encode('1/indexes/%s/rules/%s', base.indexName, objectID), }, mappedRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const deleteSynonym = (base) => { return (objectID, requestOptions) => { const { forwardToReplicas, ...options } = requestOptions || {}; const mappedRequestOptions = transporter.createMappedRequestOptions(options); if (forwardToReplicas) { mappedRequestOptions.queryParameters.forwardToReplicas = 1; // eslint-disable-line functional/immutable-data } return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Delete, path: clientCommon.encode('1/indexes/%s/synonyms/%s', base.indexName, objectID), }, mappedRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const exists = (base) => { return (requestOptions) => { return getSettings(base)(requestOptions) .then(() => true) .catch(error => { if (error.status !== 404) { throw error; } return false; }); }; }; const findAnswers = (base) => { return (query, queryLanguages, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/answers/%s/prediction', base.indexName), data: { query, queryLanguages, }, cacheable: true, }, requestOptions); }; }; const findObject = (base) => { return (callback, requestOptions) => { const { query, paginate, ...options } = requestOptions || {}; // eslint-disable-next-line functional/no-let let page = 0; const forEachPage = () => { return search(base)(query || '', { ...options, page }).then(result => { // eslint-disable-next-line functional/no-loop-statement for (const [position, hit] of Object.entries(result.hits)) { // eslint-disable-next-line promise/no-callback-in-promise if (callback(hit)) { return { object: hit, position: parseInt(position, 10), page, }; } } page++; // paginate if option was set and has next page if (paginate === false || page >= result.nbPages) { throw createObjectNotFoundError(); } return forEachPage(); }); }; return forEachPage(); }; }; const getObject = (base) => { return (objectID, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode('1/indexes/%s/%s', base.indexName, objectID), }, requestOptions); }; }; const getObjectPosition = () => { return (searchResponse, objectID) => { // eslint-disable-next-line functional/no-loop-statement for (const [position, hit] of Object.entries(searchResponse.hits)) { if (hit.objectID === objectID) { return parseInt(position, 10); } } return -1; }; }; const getObjects = (base) => { return (objectIDs, requestOptions) => { const { attributesToRetrieve, ...options } = requestOptions || {}; const requests = objectIDs.map(objectID => { return { indexName: base.indexName, objectID, ...(attributesToRetrieve ? { attributesToRetrieve } : {}), }; }); return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: '1/indexes/*/objects', data: { requests, }, }, options); }; }; const getRule = (base) => { return (objectID, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode('1/indexes/%s/rules/%s', base.indexName, objectID), }, requestOptions); }; }; const getSettings = (base) => { return (requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode('1/indexes/%s/settings', base.indexName), data: { getVersion: 2, }, }, requestOptions); }; }; const getSynonym = (base) => { return (objectID, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode(`1/indexes/%s/synonyms/%s`, base.indexName, objectID), }, requestOptions); }; }; const getTask = (base) => { return (taskID, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Get, path: clientCommon.encode('1/indexes/%s/task/%s', base.indexName, taskID.toString()), }, requestOptions); }; }; const partialUpdateObject = (base) => { return (object, requestOptions) => { return clientCommon.createWaitablePromise(partialUpdateObjects(base)([object], requestOptions).then(response => { return { objectID: response.objectIDs[0], taskID: response.taskIDs[0], }; }), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const partialUpdateObjects = (base) => { return (objects, requestOptions) => { const { createIfNotExists, ...options } = requestOptions || {}; const action = createIfNotExists ? BatchActionEnum.PartialUpdateObject : BatchActionEnum.PartialUpdateObjectNoCreate; return chunkedBatch(base)(objects, action, options); }; }; const replaceAllObjects = (base) => { return (objects, requestOptions) => { const { safe, autoGenerateObjectIDIfNotExist, batchSize, ...options } = requestOptions || {}; const operation = (from, to, type, operationRequestOptions) => { return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/operation', from), data: { operation: type, destination: to, }, }, operationRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; const randomSuffix = Math.random() .toString(36) .substring(7); const temporaryIndexName = `${base.indexName}_tmp_${randomSuffix}`; const saveObjectsInTemporary = saveObjects({ appId: base.appId, transporter: base.transporter, indexName: temporaryIndexName, }); // @ts-ignore // eslint-disable-next-line prefer-const, functional/no-let, functional/prefer-readonly-type let responses = []; const copyWaitablePromise = operation(base.indexName, temporaryIndexName, 'copy', { ...options, scope: ['settings', 'synonyms', 'rules'], }); // eslint-disable-next-line functional/immutable-data responses.push(copyWaitablePromise); const result = (safe ? copyWaitablePromise.wait(options) : copyWaitablePromise) .then(() => { const saveObjectsWaitablePromise = saveObjectsInTemporary(objects, { ...options, autoGenerateObjectIDIfNotExist, batchSize, }); // eslint-disable-next-line functional/immutable-data responses.push(saveObjectsWaitablePromise); return safe ? saveObjectsWaitablePromise.wait(options) : saveObjectsWaitablePromise; }) .then(() => { const moveWaitablePromise = operation(temporaryIndexName, base.indexName, 'move', options); // eslint-disable-next-line functional/immutable-data responses.push(moveWaitablePromise); return safe ? moveWaitablePromise.wait(options) : moveWaitablePromise; }) .then(() => Promise.all(responses)) .then(([copyResponse, saveObjectsResponse, moveResponse]) => { return { objectIDs: saveObjectsResponse.objectIDs, taskIDs: [copyResponse.taskID, ...saveObjectsResponse.taskIDs, moveResponse.taskID], }; }); return clientCommon.createWaitablePromise(result, (_, waitRequestOptions) => { return Promise.all(responses.map(response => response.wait(waitRequestOptions))); }); }; }; const replaceAllRules = (base) => { return (rules, requestOptions) => { return saveRules(base)(rules, { ...requestOptions, clearExistingRules: true, }); }; }; const replaceAllSynonyms = (base) => { return (synonyms, requestOptions) => { return saveSynonyms(base)(synonyms, { ...requestOptions, clearExistingSynonyms: true, }); }; }; const saveObject = (base) => { return (object, requestOptions) => { return clientCommon.createWaitablePromise(saveObjects(base)([object], requestOptions).then(response => { return { objectID: response.objectIDs[0], taskID: response.taskIDs[0], }; }), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const saveObjects = (base) => { return (objects, requestOptions) => { const { autoGenerateObjectIDIfNotExist, ...options } = requestOptions || {}; const action = autoGenerateObjectIDIfNotExist ? BatchActionEnum.AddObject : BatchActionEnum.UpdateObject; if (action === BatchActionEnum.UpdateObject) { // eslint-disable-next-line functional/no-loop-statement for (const object of objects) { if (object.objectID === undefined) { return clientCommon.createWaitablePromise(Promise.reject(createMissingObjectIDError())); } } } return chunkedBatch(base)(objects, action, options); }; }; const saveRule = (base) => { return (rule, requestOptions) => { return saveRules(base)([rule], requestOptions); }; }; const saveRules = (base) => { return (rules, requestOptions) => { const { forwardToReplicas, clearExistingRules, ...options } = requestOptions || {}; const mappedRequestOptions = transporter.createMappedRequestOptions(options); if (forwardToReplicas) { mappedRequestOptions.queryParameters.forwardToReplicas = 1; // eslint-disable-line functional/immutable-data } if (clearExistingRules) { mappedRequestOptions.queryParameters.clearExistingRules = 1; // eslint-disable-line functional/immutable-data } return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/rules/batch', base.indexName), data: rules, }, mappedRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const saveSynonym = (base) => { return (synonym, requestOptions) => { return saveSynonyms(base)([synonym], requestOptions); }; }; const saveSynonyms = (base) => { return (synonyms, requestOptions) => { const { forwardToReplicas, clearExistingSynonyms, replaceExistingSynonyms, ...options } = requestOptions || {}; const mappedRequestOptions = transporter.createMappedRequestOptions(options); if (forwardToReplicas) { mappedRequestOptions.queryParameters.forwardToReplicas = 1; // eslint-disable-line functional/immutable-data } if (replaceExistingSynonyms || clearExistingSynonyms) { mappedRequestOptions.queryParameters.replaceExistingSynonyms = 1; // eslint-disable-line functional/immutable-data } return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/synonyms/batch', base.indexName), data: synonyms, }, mappedRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const search = (base) => { return (query, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/query', base.indexName), data: { query, }, cacheable: true, }, requestOptions); }; }; const searchForFacetValues = (base) => { return (facetName, facetQuery, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/facets/%s/query', base.indexName, facetName), data: { facetQuery, }, cacheable: true, }, requestOptions); }; }; const searchRules = (base) => { return (query, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/rules/search', base.indexName), data: { query, }, }, requestOptions); }; }; const searchSynonyms = (base) => { return (query, requestOptions) => { return base.transporter.read({ method: requesterCommon.MethodEnum.Post, path: clientCommon.encode('1/indexes/%s/synonyms/search', base.indexName), data: { query, }, }, requestOptions); }; }; const setSettings = (base) => { return (settings, requestOptions) => { const { forwardToReplicas, ...options } = requestOptions || {}; const mappedRequestOptions = transporter.createMappedRequestOptions(options); if (forwardToReplicas) { mappedRequestOptions.queryParameters.forwardToReplicas = 1; // eslint-disable-line functional/immutable-data } return clientCommon.createWaitablePromise(base.transporter.write({ method: requesterCommon.MethodEnum.Put, path: clientCommon.encode('1/indexes/%s/settings', base.indexName), data: settings, }, mappedRequestOptions), (response, waitRequestOptions) => waitTask(base)(response.taskID, waitRequestOptions)); }; }; const waitTask = (base) => { return (taskID, requestOptions) => { return clientCommon.createRetryablePromise(retry => { return getTask(base)(taskID, requestOptions).then(response => { return response.status !== 'published' ? retry() : undefined; }); }); }; }; const ApiKeyACLEnum = { AddObject: 'addObject', Analytics: 'analytics', Browser: 'browse', DeleteIndex: 'deleteIndex', DeleteObject: 'deleteObject', EditSettings: 'editSettings', Inference: 'inference', ListIndexes: 'listIndexes', Logs: 'logs', Personalization: 'personalization', Recommendation: 'recommendation', Search: 'search', SeeUnretrievableAttributes: 'seeUnretrievableAttributes', Settings: 'settings', Usage: 'usage', }; const BatchActionEnum = { AddObject: 'addObject', UpdateObject: 'updateObject', PartialUpdateObject: 'partialUpdateObject', PartialUpdateObjectNoCreate: 'partialUpdateObjectNoCreate', DeleteObject: 'deleteObject', DeleteIndex: 'delete', ClearIndex: 'clear', }; const ScopeEnum = { Settings: 'settings', Synonyms: 'synonyms', Rules: 'rules', }; const StrategyEnum = { None: 'none', StopIfEnoughMatches: 'stopIfEnoughMatches', }; const SynonymEnum = { Synonym: 'synonym', OneWaySynonym: 'oneWaySynonym', AltCorrection1: 'altCorrection1', AltCorrection2: 'altCorrection2', Placeholder: 'placeholder', }; exports.ApiKeyACLEnum = ApiKeyACLEnum; exports.BatchActionEnum = BatchActionEnum; exports.ScopeEnum = ScopeEnum; exports.StrategyEnum = StrategyEnum; exports.SynonymEnum = SynonymEnum; exports.addApiKey = addApiKey; exports.assignUserID = assignUserID; exports.assignUserIDs = assignUserIDs; exports.batch = batch; exports.browseObjects = browseObjects; exports.browseRules = browseRules; exports.browseSynonyms = browseSynonyms; exports.chunkedBatch = chunkedBatch; exports.clearDictionaryEntries = clearDictionaryEntries; exports.clearObjects = clearObjects; exports.clearRules = clearRules; exports.clearSynonyms = clearSynonyms; exports.copyIndex = copyIndex; exports.copyRules = copyRules; exports.copySettings = copySettings; exports.copySynonyms = copySynonyms; exports.createBrowsablePromise = createBrowsablePromise; exports.createMissingObjectIDError = createMissingObjectIDError; exports.createObjectNotFoundError = createObjectNotFoundError; exports.createSearchClient = createSearchClient; exports.createValidUntilNotFoundError = createValidUntilNotFoundError; exports.customRequest = customRequest; exports.deleteApiKey = deleteApiKey; exports.deleteBy = deleteBy; exports.deleteDictionaryEntries = deleteDictionaryEntries; exports.deleteIndex = deleteIndex; exports.deleteObject = deleteObject; exports.deleteObjects = deleteObjects; exports.deleteRule = deleteRule; exports.deleteSynonym = deleteSynonym; exports.exists = exists; exports.findAnswers = findAnswers; exports.findObject = findObject; exports.generateSecuredApiKey = generateSecuredApiKey; exports.getApiKey = getApiKey; exports.getAppTask = getAppTask; exports.getDictionarySettings = getDictionarySettings; exports.getLogs = getLogs; exports.getObject = getObject; exports.getObjectPosition = getObjectPosition; exports.getObjects = getObjects; exports.getRule = getRule; exports.getSecuredApiKeyRemainingValidity = getSecuredApiKeyRemainingValidity; exports.getSettings = getSettings; exports.getSynonym = getSynonym; exports.getTask = getTask; exports.getTopUserIDs = getTopUserIDs; exports.getUserID = getUserID; exports.hasPendingMappings = hasPendingMappings; exports.initIndex = initIndex; exports.listApiKeys = listApiKeys; exports.listClusters = listClusters; exports.listIndices = listIndices; exports.listUserIDs = listUserIDs; exports.moveIndex = moveIndex; exports.multipleBatch = multipleBatch; exports.multipleGetObjects = multipleGetObjects; exports.multipleQueries = multipleQueries; exports.multipleSearchForFacetValues = multipleSearchForFacetValues; exports.partialUpdateObject = partialUpdateObject; exports.partialUpdateObjects = partialUpdateObjects; exports.removeUserID = removeUserID; exports.replaceAllObjects = replaceAllObjects; exports.replaceAllRules = replaceAllRules; exports.replaceAllSynonyms = replaceAllSynonyms; exports.replaceDictionaryEntries = replaceDictionaryEntries; exports.restoreApiKey = restoreApiKey; exports.saveDictionaryEntries = saveDictionaryEntries; exports.saveObject = saveObject; exports.saveObjects = saveObjects; exports.saveRule = saveRule; exports.saveRules = saveRules; exports.saveSynonym = saveSynonym; exports.saveSynonyms = saveSynonyms; exports.search = search; exports.searchDictionaryEntries = searchDictionaryEntries; exports.searchForFacetValues = searchForFacetValues; exports.searchRules = searchRules; exports.searchSynonyms = searchSynonyms; exports.searchUserIDs = searchUserIDs; exports.setDictionarySettings = setDictionarySettings; exports.setSettings = setSettings; exports.updateApiKey = updateApiKey; exports.waitAppTask = waitAppTask; exports.waitTask = waitTask;