diff --git a/server/src/service/infrastructure/network/HttpProvider.ts b/server/src/service/infrastructure/network/HttpProvider.ts index 73cdd6faf..500f93771 100644 --- a/server/src/service/infrastructure/network/HttpProvider.ts +++ b/server/src/service/infrastructure/network/HttpProvider.ts @@ -33,17 +33,65 @@ export class HttpProvider implements IStatusProvider { if (error instanceof HTTPError || error instanceof RequestError) { const statusCode = error.response?.statusCode; const statusUp = isStatusUp(statusCode, monitor.customUpCodes); + const responseTime = error.timings?.phases?.total ?? 0; + + if (!statusUp) { + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: statusCode ?? NETWORK_ERROR, + message: error.message, + responseTime, + timings: error.timings, + payload: null as T, + }; + } + + const { jsonPath } = monitor; + const body = error.response?.body ?? ""; + const contentType = error.response?.headers?.["content-type"] || ""; + const isJson = contentType.includes("application/json"); + + if (jsonPath && !isJson) { + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: statusCode ?? NETWORK_ERROR, + message: "Response is not JSON", + responseTime, + timings: error.timings, + payload: body as unknown as T, + }; + } + + let payload: T; + if (isJson) { + try { + payload = JSON.parse(body) as T; + } catch { + payload = body as unknown as T; + } + } else { + payload = body as unknown as T; + } + + const matchResult = this.advancedMatcher.validate(payload, monitor); return { monitorId: monitor.id, teamId: monitor.teamId, type: monitor.type, - status: statusUp, + status: statusUp && matchResult.ok, code: statusCode ?? NETWORK_ERROR, - message: error.message, - responseTime: error.timings?.phases?.total ?? 0, + message: matchResult.ok ? error.message : matchResult.message, + responseTime, timings: error.timings, - payload: null as T, + payload, + extracted: matchResult.extracted, }; } diff --git a/server/test/unit/providers/network/httpProvider.test.ts b/server/test/unit/providers/network/httpProvider.test.ts index ea24b9872..bd6d05b43 100644 --- a/server/test/unit/providers/network/httpProvider.test.ts +++ b/server/test/unit/providers/network/httpProvider.test.ts @@ -408,5 +408,29 @@ describe("HttpProvider", () => { expect(result.status).toBe(false); expect(result.message).toBe("Body mismatch"); }); + + it("does not override matcher failure on HTTPError when status code is in customUpCodes", async () => { + const err = new HTTPError("Unauthorized"); + (err as any).response = { + statusCode: 401, + body: '{"status":"down"}', + headers: { "content-type": "application/json" }, + }; + (err as any).timings = { phases: { total: 30 } }; + mockGot.mockRejectedValue(err); + const matcher = createMockMatcher({ + ok: false, + message: "Body mismatch", + extracted: "down", + }); + const { provider } = createProvider(matcher); + + const result = await provider.handle(makeMonitor({ customUpCodes: [401], useAdvancedMatching: true })); + + expect(result.status).toBe(false); + expect(result.message).toBe("Body mismatch"); + expect(result.payload).toEqual({ status: "down" }); + expect(result.extracted).toBe("down"); + }); }); });