diff --git a/CHANGELOG.md b/CHANGELOG.md index 01e60df15..f895e03db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixed - Fix escaping regex values in when editing silences #3936. +- Fix UI handling of 401 errors #3942. ## v0.96 diff --git a/ui/src/Hooks/useFetchAny.test.tsx b/ui/src/Hooks/useFetchAny.test.tsx index cb0174155..34a15318d 100644 --- a/ui/src/Hooks/useFetchAny.test.tsx +++ b/ui/src/Hooks/useFetchAny.test.tsx @@ -19,6 +19,7 @@ describe("useFetchAny", () => { status: 500, body: "fake error", }); + fetchMock.mock("http://localhost/401", 401); fetchMock.mock("http://localhost/error", { throws: new TypeError("failed to fetch"), }); @@ -133,6 +134,25 @@ describe("useFetchAny", () => { expect(result.current.responseURI).toBe("http://localhost/ok/json"); }); + it("error is using status code if body is empty", async () => { + const upstreams = [{ uri: "http://localhost/401", options: {} }]; + const { result, waitForNextUpdate } = renderHook(() => + useFetchAny(upstreams) + ); + + expect(result.current.response).toBe(null); + expect(result.current.error).toBe(null); + expect(result.current.inProgress).toBe(true); + expect(result.current.responseURI).toBe(null); + + await waitForNextUpdate(); + + expect(result.current.response).toBe(null); + expect(result.current.error).toBe("401 Unauthorized"); + expect(result.current.inProgress).toBe(false); + expect(result.current.responseURI).toBe(null); + }); + it("error is updated after 500 error", async () => { const upstreams = [{ uri: "http://localhost/500", options: {} }]; const { result, waitForNextUpdate } = renderHook(() => diff --git a/ui/src/Hooks/useFetchAny.ts b/ui/src/Hooks/useFetchAny.ts index 8288da1af..5372e65fd 100644 --- a/ui/src/Hooks/useFetchAny.ts +++ b/ui/src/Hooks/useFetchAny.ts @@ -92,7 +92,7 @@ const useFetchAny = ( } else { setResponse({ response: null, - error: body, + error: body ? body : `${res.status} ${res.statusText}`, responseURI: null, inProgress: false, }); diff --git a/ui/src/Hooks/useFetchDelete.test.tsx b/ui/src/Hooks/useFetchDelete.test.tsx index 10aacbe17..29a7b1ace 100644 --- a/ui/src/Hooks/useFetchDelete.test.tsx +++ b/ui/src/Hooks/useFetchDelete.test.tsx @@ -11,6 +11,7 @@ import { useFetchDelete } from "./useFetchDelete"; describe("useFetchDelete", () => { beforeAll(() => { fetchMock.mock("http://localhost/ok", "body ok"); + fetchMock.mock("http://localhost/401", 401); fetchMock.mock("http://localhost/500", { status: 500, body: "fake error", @@ -79,6 +80,22 @@ describe("useFetchDelete", () => { expect(result.current.isDeleting).toBe(false); }); + it("error is updated after 401 error", async () => { + const { result, waitForNextUpdate } = renderHook(() => + useFetchDelete("http://localhost/401", EmptyOptions) + ); + + expect(result.current.response).toBe(null); + expect(result.current.error).toBe(null); + expect(result.current.isDeleting).toBe(true); + + await waitForNextUpdate(); + + expect(result.current.response).toBe(null); + expect(result.current.error).toBe("401 Unauthorized"); + expect(result.current.isDeleting).toBe(false); + }); + it("error is updated after 500 error", async () => { const { result, waitForNextUpdate } = renderHook(() => useFetchDelete("http://localhost/500", EmptyOptions) diff --git a/ui/src/Hooks/useFetchDelete.ts b/ui/src/Hooks/useFetchDelete.ts index 57c284cb3..be1b83379 100644 --- a/ui/src/Hooks/useFetchDelete.ts +++ b/ui/src/Hooks/useFetchDelete.ts @@ -39,7 +39,7 @@ const useFetchDelete = ( setResponse(text); setIsDeleting(false); } else { - setError(text); + setError(text ? text : `${res.status} ${res.statusText}`); setIsDeleting(false); } } diff --git a/ui/src/Hooks/useFetchGet.test.tsx b/ui/src/Hooks/useFetchGet.test.tsx index 82d2fe28e..c4394c5ca 100644 --- a/ui/src/Hooks/useFetchGet.test.tsx +++ b/ui/src/Hooks/useFetchGet.test.tsx @@ -17,6 +17,7 @@ describe("useFetchGet", () => { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: "ok" }), }); + fetchMock.mock("http://localhost/401", 401); fetchMock.mock("http://localhost/error", { throws: new TypeError("failed to fetch"), }); @@ -189,6 +190,25 @@ describe("useFetchGet", () => { expect(result.current.isRetrying).toBe(false); }); + it("error is updated after 401 error", async () => { + const { result, waitForNextUpdate } = renderHook(() => + useFetchGet("http://localhost/401") + ); + + expect(result.current.response).toBe(null); + expect(result.current.error).toBe(null); + expect(result.current.isLoading).toBe(true); + expect(result.current.isRetrying).toBe(false); + + jest.runOnlyPendingTimers(); + await waitForNextUpdate(); + + expect(result.current.response).toBe(null); + expect(result.current.error).toBe("401 Unauthorized"); + expect(result.current.isLoading).toBe(false); + expect(result.current.isRetrying).toBe(false); + }); + it("error is updated after failed fetch", async () => { const { result, waitForNextUpdate } = renderHook(() => useFetchGet("http://localhost/error") diff --git a/ui/src/Hooks/useFetchGet.ts b/ui/src/Hooks/useFetchGet.ts index 3c3136141..d06bd4e58 100644 --- a/ui/src/Hooks/useFetchGet.ts +++ b/ui/src/Hooks/useFetchGet.ts @@ -108,7 +108,7 @@ const useFetchGet = ( } else { setResponse({ response: null, - error: body, + error: body ? body : `${res.status} ${res.statusText}`, isLoading: false, isRetrying: false, retryCount: 0,