resolve main merge conflicts for alert context and beads state
This commit is contained in:
commit
dc932cf18e
11 changed files with 843 additions and 153 deletions
21
services/api/src/alert-context.ts
Normal file
21
services/api/src/alert-context.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const alertContextTraceIdSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(256)
|
||||
.regex(/^[A-Za-z0-9][A-Za-z0-9:_./-]*$/);
|
||||
|
||||
export const isAlertContextPath = (pathname: string): boolean => {
|
||||
return /^\/flow\/alerts\/[^/]+\/context$/.test(pathname);
|
||||
};
|
||||
|
||||
export const parseAlertContextTraceIdPath = (pathname: string): string | null => {
|
||||
if (!isAlertContextPath(pathname)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const encodedTraceId = pathname.slice("/flow/alerts/".length, -"/context".length);
|
||||
return alertContextTraceIdSchema.parse(decodeURIComponent(encodedTraceId));
|
||||
};
|
||||
|
|
@ -47,6 +47,7 @@ import {
|
|||
ensureOptionPrintsTable,
|
||||
fetchAlertsAfter,
|
||||
fetchAlertsBefore,
|
||||
fetchAlertContextByTraceId,
|
||||
fetchClassifierHitsAfter,
|
||||
fetchClassifierHitsBefore,
|
||||
fetchSmartMoneyEventsAfter,
|
||||
|
|
@ -119,6 +120,7 @@ import {
|
|||
resolveLiveStateConfig,
|
||||
shouldFanoutLiveEvent
|
||||
} from "./live";
|
||||
import { isAlertContextPath, parseAlertContextTraceIdPath } from "./alert-context";
|
||||
import { parseOptionPrintQuery } from "./option-queries";
|
||||
import {
|
||||
buildSyntheticDerivedStatus,
|
||||
|
|
@ -1488,6 +1490,25 @@ const run = async () => {
|
|||
return jsonResponse({ data });
|
||||
}
|
||||
|
||||
if (req.method === "GET" && isAlertContextPath(url.pathname)) {
|
||||
try {
|
||||
const traceId = parseAlertContextTraceIdPath(url.pathname);
|
||||
if (traceId === null) {
|
||||
return jsonResponse({ error: "not found" }, 404);
|
||||
}
|
||||
const data = await fetchAlertContextByTraceId(clickhouse, traceId);
|
||||
return jsonResponse(data);
|
||||
} catch (error) {
|
||||
return jsonResponse(
|
||||
{
|
||||
error: "invalid alert context query",
|
||||
detail: error instanceof Error ? error.message : String(error)
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/history/options") {
|
||||
try {
|
||||
const { beforeTs, beforeSeq, limit } = parseBeforeParams(url);
|
||||
|
|
|
|||
18
services/api/tests/alert-context.test.ts
Normal file
18
services/api/tests/alert-context.test.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { describe, expect, it } from "bun:test";
|
||||
import { isAlertContextPath, parseAlertContextTraceIdPath } from "../src/alert-context";
|
||||
|
||||
describe("alert context route helpers", () => {
|
||||
it("extracts a valid alert trace id from the context endpoint path", () => {
|
||||
expect(parseAlertContextTraceIdPath("/flow/alerts/alert%3Actx%2Fone/context")).toBe("alert:ctx/one");
|
||||
});
|
||||
|
||||
it("returns null for unrelated alert paths", () => {
|
||||
expect(isAlertContextPath("/flow/alerts")).toBe(false);
|
||||
expect(parseAlertContextTraceIdPath("/flow/alerts/alert:ctx")).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects malformed trace ids safely", () => {
|
||||
expect(() => parseAlertContextTraceIdPath("/flow/alerts/%20/context")).toThrow();
|
||||
expect(() => parseAlertContextTraceIdPath("/flow/alerts/%24bad/context")).toThrow();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue