From 44c355a82645c23999781a00ce75aa7ed9a26a92 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Fri, 25 Apr 2025 09:50:27 +0300 Subject: [PATCH 1/2] Add experimental features methods --- src/meilisearch.ts | 22 ++++++++++++++++++ src/types/experimental-features.ts | 14 ++++++++++++ src/types/index.ts | 1 + tests/experimental-features.test.ts | 35 +++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 src/types/experimental-features.ts create mode 100644 tests/experimental-features.test.ts diff --git a/src/meilisearch.ts b/src/meilisearch.ts index 25f1c4e8d..018e02a0f 100644 --- a/src/meilisearch.ts +++ b/src/meilisearch.ts @@ -28,6 +28,7 @@ import type { ExtraRequestInit, Network, RecordAny, + RuntimeTogglableFeatures, } from "./types/index.js"; import { ErrorStatusCode } from "./types/index.js"; import { HttpRequests } from "./http-requests.js"; @@ -454,4 +455,25 @@ export class MeiliSearch { path: "snapshots", }); } + + /// + /// EXPERIMENTAL-FEATURES + /// + + /** {@link https://www.meilisearch.com/docs/reference/api/experimental_features#get-all-experimental-features} */ + async getExperimentalFeatures(): Promise { + return await this.httpRequest.get({ + path: "experimental-features", + }); + } + + /** {@link https://www.meilisearch.com/docs/reference/api/experimental_features#configure-experimental-features} */ + async updateExperimentalFeatures( + runtimeTogglableFeatures: RuntimeTogglableFeatures, + ): Promise { + return await this.httpRequest.patch({ + path: "experimental-features", + body: runtimeTogglableFeatures, + }); + } } diff --git a/src/types/experimental-features.ts b/src/types/experimental-features.ts new file mode 100644 index 000000000..c81720d16 --- /dev/null +++ b/src/types/experimental-features.ts @@ -0,0 +1,14 @@ +/** + * {@link https://www.meilisearch.com/docs/reference/api/experimental_features#experimental-features-object} + * + * @see `meilisearch::routes::features::RuntimeTogglableFeatures` + */ +export type RuntimeTogglableFeatures = { + metrics?: boolean | null; + logsRoute?: boolean | null; + editDocumentsByFunction?: boolean | null; + containsFilter?: boolean | null; + network?: boolean | null; + getTaskDocumentsRoute?: boolean | null; + compositeEmbedders?: boolean | null; +}; diff --git a/src/types/index.ts b/src/types/index.ts index 2a13bc669..8aaa23dba 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,4 @@ +export * from "./experimental-features.js"; export * from "./task_and_batch.js"; export * from "./token.js"; export * from "./types.js"; diff --git a/tests/experimental-features.test.ts b/tests/experimental-features.test.ts new file mode 100644 index 000000000..0e4b22e15 --- /dev/null +++ b/tests/experimental-features.test.ts @@ -0,0 +1,35 @@ +import { afterAll, test } from "vitest"; +import { assert, getClient } from "./utils/meilisearch-test-utils.js"; +import type { RuntimeTogglableFeatures } from "../src/index.js"; + +const ms = await getClient("Master"); + +afterAll(async () => { + await ms.updateExperimentalFeatures({ + metrics: false, + logsRoute: false, + editDocumentsByFunction: false, + containsFilter: false, + network: false, + getTaskDocumentsRoute: false, + compositeEmbedders: false, + } satisfies { [TKey in keyof RuntimeTogglableFeatures]-?: false }); +}); + +test(`${ms.updateExperimentalFeatures.name} and ${ms.getExperimentalFeatures.name} methods`, async () => { + const features: { [TKey in keyof RuntimeTogglableFeatures]-?: true } = { + metrics: true, + logsRoute: true, + editDocumentsByFunction: true, + containsFilter: true, + network: true, + getTaskDocumentsRoute: true, + compositeEmbedders: true, + }; + + const updateFeatures = await ms.updateExperimentalFeatures(features); + assert.deepEqual(updateFeatures, features); + + const getFeatures = await ms.getExperimentalFeatures(); + assert.deepEqual(getFeatures, features); +}); From 19bfec36331f7fc3959797735c6406e48fa1e533 Mon Sep 17 00:00:00 2001 From: "F. Levi" <55688616+flevi29@users.noreply.github.com> Date: Fri, 25 Apr 2025 09:58:12 +0300 Subject: [PATCH 2/2] Use new methods instead of raw fetches in some tests --- tests/documents.test.ts | 50 ++++++++++++++--------------------------- tests/embedders.test.ts | 14 +++--------- tests/search.test.ts | 13 +++-------- 3 files changed, 23 insertions(+), 54 deletions(-) diff --git a/tests/documents.test.ts b/tests/documents.test.ts index 40aa86d14..e9c958761 100644 --- a/tests/documents.test.ts +++ b/tests/documents.test.ts @@ -680,17 +680,13 @@ describe("Documents tests", () => { test(`${permission} key: test updateDocumentsByFunction`, async () => { const client = await getClient(permission); const index = client.index<(typeof dataset)[number]>(indexPk.uid); - const adminKey = await getKey("Admin"); await index.updateFilterableAttributes(["id"]).waitTask(); - await fetch(`${HOST}/experimental-features`, { - body: JSON.stringify({ editDocumentsByFunction: true }), - headers: { - Authorization: `Bearer ${adminKey}`, - "Content-Type": "application/json", - }, - method: "PATCH", + await ( + await getClient("Master") + ).updateExperimentalFeatures({ + editDocumentsByFunction: true, }); await index.addDocuments(dataset).waitTask(); @@ -761,15 +757,11 @@ describe("Documents tests", () => { test(`${permission} key: Try updateDocumentsByFunction and be denied`, async () => { const client = await getClient(permission); - const adminKey = await getKey("Admin"); - await fetch(`${HOST}/experimental-features`, { - body: JSON.stringify({ editDocumentsByFunction: true }), - headers: { - Authorization: `Bearer ${adminKey}`, - "Content-Type": "application/json", - }, - method: "PATCH", + await ( + await getClient("Master") + ).updateExperimentalFeatures({ + editDocumentsByFunction: true, }); await expect( @@ -848,15 +840,11 @@ describe("Documents tests", () => { test(`${permission} key: Try updateDocumentsByFunction and be denied`, async () => { const client = await getClient(permission); - const adminKey = await getKey("Admin"); - await fetch(`${HOST}/experimental-features`, { - body: JSON.stringify({ editDocumentsByFunction: true }), - headers: { - Authorization: `Bearer ${adminKey}`, - "Content-Type": "application/json", - }, - method: "PATCH", + await ( + await getClient("Master") + ).updateExperimentalFeatures({ + editDocumentsByFunction: true, }); await expect( @@ -962,15 +950,11 @@ describe("Documents tests", () => { const route = `indexes/${indexPk.uid}/documents/edit`; const client = new MeiliSearch({ host }); const strippedHost = trailing ? host.slice(0, -1) : host; - const adminKey = await getKey("Admin"); - - await fetch(`${HOST}/experimental-features`, { - body: JSON.stringify({ editDocumentsByFunction: true }), - headers: { - Authorization: `Bearer ${adminKey}`, - "Content-Type": "application/json", - }, - method: "PATCH", + + await ( + await getClient("Master") + ).updateExperimentalFeatures({ + editDocumentsByFunction: true, }); await expect( diff --git a/tests/embedders.test.ts b/tests/embedders.test.ts index bfdddc506..63d6f09ad 100644 --- a/tests/embedders.test.ts +++ b/tests/embedders.test.ts @@ -6,8 +6,7 @@ import { BAD_HOST, MeiliSearch, getClient, - getKey, - HOST, + masterClient, } from "./utils/meilisearch-test-utils.js"; const index = { @@ -239,16 +238,9 @@ describe.each([{ permission: "Master" }, { permission: "Admin" }])( }); test(`${permission} key: Update embedders with composite embedder`, async () => { - const adminKey = await getKey("Admin"); - // first enable the network endpoint. - await fetch(`${HOST}/experimental-features`, { - body: JSON.stringify({ compositeEmbedders: true }), - headers: { - Authorization: `Bearer ${adminKey}`, - "Content-Type": "application/json", - }, - method: "PATCH", + await masterClient.updateExperimentalFeatures({ + compositeEmbedders: true, }); const client = await getClient(permission); diff --git a/tests/search.test.ts b/tests/search.test.ts index 76611c04a..8cacb0e6b 100644 --- a/tests/search.test.ts +++ b/tests/search.test.ts @@ -234,20 +234,13 @@ describe.each([ }); test(`${permission} key: Multi index search with federation and remote`, async () => { - const adminKey = await getKey("Admin"); + const masterClient = await getClient("Master"); // first enable the network endpoint. - await fetch(`${HOST}/experimental-features`, { - body: JSON.stringify({ network: true }), - headers: { - Authorization: `Bearer ${adminKey}`, - "Content-Type": "application/json", - }, - method: "PATCH", + await masterClient.updateExperimentalFeatures({ + network: true, }); - const masterClient = await getClient("Master"); - const searchKey = await getKey("Search"); // set the remote name and instances