From 770af6b1c0202f63b90beaf19cdf3efaff63b469 Mon Sep 17 00:00:00 2001 From: badgerbees <93577481+badgerbees@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:11:32 +0700 Subject: [PATCH] Vault: handle empty delete responses (#913) Co-authored-by: Viet Tran --- ui/web/src/api/http-client.test.ts | 48 ++++++++++++++++++++++++++++++ ui/web/src/api/http-client.ts | 17 +++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 ui/web/src/api/http-client.test.ts diff --git a/ui/web/src/api/http-client.test.ts b/ui/web/src/api/http-client.test.ts new file mode 100644 index 00000000..88f2fd98 --- /dev/null +++ b/ui/web/src/api/http-client.test.ts @@ -0,0 +1,48 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { HttpClient } from "./http-client"; + +function createClient() { + return new HttpClient("https://example.com", () => "", () => ""); +} + +afterEach(() => { + vi.unstubAllGlobals(); + localStorage.clear(); +}); + +describe("HttpClient", () => { + it("returns undefined for 204 no-content responses", async () => { + const fetchMock = vi.fn().mockResolvedValue(new Response(null, { status: 204, statusText: "No Content" })); + vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch); + + const client = createClient(); + const result = await client.delete("/v1/vault/documents/123"); + + expect(result).toBeUndefined(); + expect(fetchMock).toHaveBeenCalledWith( + "https://example.com/v1/vault/documents/123", + expect.objectContaining({ + method: "DELETE", + headers: expect.objectContaining({ + "Content-Type": "application/json", + }), + }), + ); + }); + + it("parses JSON responses from successful requests", async () => { + const payload = { deleted: true }; + const fetchMock = vi.fn().mockResolvedValue( + new Response(JSON.stringify(payload), { + status: 200, + headers: { "content-type": "application/json" }, + }), + ); + vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch); + + const client = createClient(); + const result = await client.post<{ deleted: boolean }>("/v1/test", { id: "123" }); + + expect(result).toEqual(payload); + }); +}); \ No newline at end of file diff --git a/ui/web/src/api/http-client.ts b/ui/web/src/api/http-client.ts index 0e9a3866..41f057e2 100644 --- a/ui/web/src/api/http-client.ts +++ b/ui/web/src/api/http-client.ts @@ -90,7 +90,7 @@ export class HttpClient { throw new ApiError(code, message); } - return res.json() as Promise; + return this.readJson(res); } private buildUrl(path: string, params?: Record): string { @@ -150,6 +150,19 @@ export class HttpClient { throw new ApiError(code, message); } - return res.json() as Promise; + return this.readJson(res); + } + + private async readJson(res: Response): Promise { + if (res.status === 204 || res.headers.get("content-length") === "0") { + return undefined as T; + } + + const text = await res.text(); + if (text.trim().length === 0) { + return undefined as T; + } + + return JSON.parse(text) as T; } }