feat(kafka): filter loaded messages

This commit is contained in:
Guoyu Su authored and GitHub committed 2026-08-24 20:27:26 +08:00
1 parent ae3b991bd5
commit 697a8efe6e
12 files changed
+259 -1

No files matched your search

@@ -0,0 +1,45 @@
import { describe, expect, it } from "vitest";
import type { PeekedMessage } from "@/types/mq";
import { buildKafkaMessageSearchText, kafkaMessageSearchTextMatches, normalizeKafkaMessageSearchQuery } from "@/lib/mq/kafkaMessageSearch";
function message(overrides: Partial<PeekedMessage> = {}): PeekedMessage {
return {
position: 7,
messageId: "42",
key: "Order-ABC",
publishTime: "1724457600000",
properties: { partition: "3" },
headers: { TraceId: "REQUEST-99", source: "billing" },
payloadBase64: "",
payloadText: '{"customer":"Alice"}',
...overrides,
};
}
describe("Kafka loaded message search", () => {
it("builds a case-insensitive index from every visible message field", () => {
const searchText = buildKafkaMessageSearchText(message(), "8/24/2024, 8:00:00 AM");
for (const query of ["7", "42", "order-abc", "3", "1724457600000", "8:00:00 am", "traceid", "request-99", "billing", "alice"]) {
expect(kafkaMessageSearchTextMatches(searchText, normalizeKafkaMessageSearchQuery(query)), query).toBe(true);
}
});
it("uses the displayed Base64 payload when text is unavailable", () => {
const searchText = buildKafkaMessageSearchText(
message({
payloadText: undefined,
payloadBase64: "AAECAw==",
}),
);
expect(kafkaMessageSearchTextMatches(searchText, normalizeKafkaMessageSearchQuery("aecaw=="))).toBe(true);
});
it("treats whitespace-only queries as empty and rejects missing text", () => {
const searchText = buildKafkaMessageSearchText(message());
expect(kafkaMessageSearchTextMatches(searchText, normalizeKafkaMessageSearchQuery(" "))).toBe(true);
expect(kafkaMessageSearchTextMatches(searchText, normalizeKafkaMessageSearchQuery("not-present"))).toBe(false);
});
});
@@ -0,0 +1,30 @@
import type { PeekedMessage } from "@/types/mq";
function appendSearchValue(values: string[], value: unknown) {
if (value == null || value === "") return;
values.push(String(value));
}
export function normalizeKafkaMessageSearchQuery(query: string): string {
return query.trim().toLowerCase();
}
export function buildKafkaMessageSearchText(message: PeekedMessage, formattedPublishTime?: string): string {
const values: string[] = [];
appendSearchValue(values, message.position);
appendSearchValue(values, message.messageId);
appendSearchValue(values, message.key);
appendSearchValue(values, message.properties?.partition);
appendSearchValue(values, message.publishTime);
appendSearchValue(values, formattedPublishTime);
for (const [key, value] of Object.entries(message.headers || {})) {
appendSearchValue(values, key);
appendSearchValue(values, value);
}
appendSearchValue(values, message.payloadText ?? message.payloadBase64);
return values.join("\n").toLowerCase();
}
export function kafkaMessageSearchTextMatches(searchText: string, normalizedQuery: string): boolean {
return normalizedQuery === "" || searchText.includes(normalizedQuery);
}