From 606c1ed989b29d1480268db1bbc85c20f62d6223 Mon Sep 17 00:00:00 2001 From: Johnny Chadda Date: Thu, 21 Nov 2024 13:39:45 +0100 Subject: [PATCH 1/2] Get and delete indexes by name --- src/__tests__/opperai-indexes.test.ts | 110 ++++++++++++++++++++++++++ src/indexes.ts | 42 +++++++--- 2 files changed, 141 insertions(+), 11 deletions(-) diff --git a/src/__tests__/opperai-indexes.test.ts b/src/__tests__/opperai-indexes.test.ts index a5411d2..d01596d 100644 --- a/src/__tests__/opperai-indexes.test.ts +++ b/src/__tests__/opperai-indexes.test.ts @@ -87,6 +87,116 @@ describe("OpperAIIndexes", () => { ); }); }); + + describe("get", () => { + it("should retrieve an index by name", async () => { + const mockIndex = mockIndexes[0]; + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve(mockIndex), + }); + + const index = await opperAIIndexes.get("Test Index 1"); + + expect(index).toBeInstanceOf(Index); + expect(index?.uuid).toBe(mockIndex.uuid); + expect(index?._index).toEqual(mockIndex); + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenCalledWith("https://api.opper.ai/v1/indexes/by-name/Test%20Index%201", { + method: "GET", + headers: { + "X-OPPER-API-KEY": "test-api-key", + "User-Agent": "opper-node/0.0.0", + "Content-Type": "application/json", + }, + }); + }); + + it("should return null when index is not found", async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + status: 404, + statusText: "Not Found", + }); + + const index = await opperAIIndexes.get("NonExistentIndex"); + + expect(index).toBeNull(); + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenCalledWith("https://api.opper.ai/v1/indexes/by-name/NonExistentIndex", { + method: "GET", + headers: { + "X-OPPER-API-KEY": "test-api-key", + "User-Agent": "opper-node/0.0.0", + "Content-Type": "application/json", + }, + }); + }); + + it("should throw an error for non-404 errors", async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + status: 500, + statusText: "Internal Server Error", + }); + + await expect(opperAIIndexes.get("Test Index 1")).rejects.toThrow( + "500 Failed to fetch request https://api.opper.ai/v1/indexes/by-name/Test%20Index%201: Internal Server Error" + ); + }); + }); + + describe("deleteByName", () => { + it("should delete an index by name", async () => { + const mockIndex = mockIndexes[0]; + // Mock the get request + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve(mockIndex), + }); + // Mock the delete request + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve(true), + }); + + const result = await opperAIIndexes.deleteByName("Test Index 1"); + + expect(result).toBe(true); + expect(fetch).toHaveBeenCalledTimes(2); + // Verify get request + expect(fetch).toHaveBeenNthCalledWith(1, "https://api.opper.ai/v1/indexes/by-name/Test%20Index%201", { + method: "GET", + headers: { + "X-OPPER-API-KEY": "test-api-key", + "User-Agent": "opper-node/0.0.0", + "Content-Type": "application/json", + }, + }); + // Verify delete request + expect(fetch).toHaveBeenNthCalledWith(2, "https://api.opper.ai/v1/indexes/1", { + method: "DELETE", + headers: { + "X-OPPER-API-KEY": "test-api-key", + "User-Agent": "opper-node/0.0.0", + "Content-Type": "application/json", + }, + }); + }); + + it("should return false when index is not found", async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + status: 404, + statusText: "Not Found", + }); + + const result = await opperAIIndexes.deleteByName("NonExistentIndex"); + + expect(result).toBe(false); + expect(fetch).toHaveBeenCalledTimes(1); + }); + }); }); describe("OpperAIIndex", () => { diff --git a/src/indexes.ts b/src/indexes.ts index c91c636..34058aa 100644 --- a/src/indexes.ts +++ b/src/indexes.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import { OpperIndexDocument, OpperIndexQuery, OpperIndex, APIClientContext } from "./types"; -import { OpperError } from "./errors"; +import { OpperError, APIError } from "./errors"; import APIResource from "./api-resource"; import { URLBuilder, BASE_PATHS } from "./utils"; @@ -144,6 +144,9 @@ class Indexes extends APIResource { protected calcURLIndexByUUID = (uuid: string) => { return `${this.baseURL}/v1/indexes/${uuid}`; }; + protected calcURLIndexByName = (name: string) => { + return `${this.baseURL}/v1/indexes/by-name/${encodeURIComponent(name)}`; + }; constructor({ baseURL, apiKey, isUsingAuthorization }: APIClientContext) { super({ baseURL, apiKey, isUsingAuthorization }); @@ -180,30 +183,47 @@ class Indexes extends APIResource { } /** - * Deletes an index + * Deletes an index by UUID * @param uuid The uuid of the index to delete. * @returns A promise that resolves to a boolean indicating success. */ public async delete(uuid: string): Promise { const url = this.calcURLIndexByUUID(uuid); const deleted = await this.doDelete(url); - return deleted; } + /** + * Deletes an index by name + * @param name The name of the index to delete. + * @returns A promise that resolves to a boolean indicating success, or false if index not found. + * @throws {APIError} If there's an API error other than 404 Not Found + */ + public async deleteByName(name: string): Promise { + const index = await this.get(name); + if (!index) { + return false; + } + return this.delete(index.uuid); + } + /** * Retrieves an index by name. * @param name The name of the index to retrieve. - * @returns A promise that resolves to the index. + * @returns A promise that resolves to the index or null if not found. + * @throws {APIError} If there's an API error other than 404 Not Found */ - public async get(name: string) { - const list = await this.list(); - const index = list.find((index) => index.name === name); - - if (!index) { - return null; + public async get(name: string): Promise { + try { + const url = this.calcURLIndexByName(name); + const index = await this.doGet(url); + return new Index(index, this); + } catch (error: unknown) { + if (error instanceof APIError && error.status === 404) { + return null; + } + throw error; } - return new Index(index, this); } } From 1fd7e31a79f3ac79c6c05a0e4cc92cd3c798ec43 Mon Sep 17 00:00:00 2001 From: Johnny Chadda Date: Thu, 21 Nov 2024 13:57:18 +0100 Subject: [PATCH 2/2] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 956fcfb..aeb7246 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opperai", - "version": "2.2.0", + "version": "2.3.0", "description": "Typescript SDK for the Opper API", "main": "dist/index.js", "module": "dist/index.mjs",