eslint
Some checks failed
CI / Rustfmt (push) Successful in 22s
CI / Cargo Audit & Deny (push) Failing after 1m2s
CI / Web Blocking Checks (push) Failing after 35s
CI / Security Blocking Checks (push) Successful in 8s
CI / Clippy (push) Successful in 2m43s
CI / Web Advisory Checks (push) Successful in 35s
CI / Security Advisory Checks (push) Successful in 37s
CI / Tests (push) Failing after 9m28s

This commit is contained in:
2026-03-05 06:52:55 -06:00
parent f54eef3a14
commit 179180d604
102 changed files with 1031 additions and 532 deletions

View File

@@ -1,6 +1,7 @@
import { useCallback } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { useEntityNotifications } from "@/contexts/WebSocketContext";
import type { EnforcementSummary } from "@/api";
interface UseEnforcementStreamOptions {
/**
@@ -16,11 +17,47 @@ interface UseEnforcementStreamOptions {
enabled?: boolean;
}
/** Shape of data coming from WebSocket notifications for enforcements */
interface EnforcementNotification {
entity_id: number;
entity_type: string;
notification_type: string;
payload: Partial<EnforcementSummary> & Record<string, unknown>;
timestamp: string;
}
/** Query params shape used in enforcement list query keys */
interface EnforcementQueryParams {
status?: string;
event?: number;
rule?: number;
triggerRef?: string;
ruleRef?: string;
}
/** Shape of the paginated API response stored in React Query cache */
interface EnforcementListCache {
data: EnforcementSummary[];
pagination?: {
total_items?: number;
page?: number;
page_size?: number;
};
}
/** Shape of a single enforcement detail response stored in React Query cache */
interface EnforcementDetailCache {
data: EnforcementSummary;
}
/**
* Check if an enforcement matches the given query parameters
* Only checks fields that are reliably present in WebSocket payloads
*/
function enforcementMatchesParams(enforcement: any, params: any): boolean {
function enforcementMatchesParams(
enforcement: Partial<EnforcementSummary>,
params: EnforcementQueryParams | undefined,
): boolean {
if (!params) return true;
// Check status filter
@@ -53,8 +90,10 @@ function enforcementMatchesParams(enforcement: any, params: any): boolean {
/**
* Check if query params include filters not present in WebSocket payloads
*/
function hasUnsupportedFilters(params: any): boolean {
if (!params) return false;
function hasUnsupportedFilters(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_params: EnforcementQueryParams | undefined,
): boolean {
// Currently all enforcement filters are supported in WebSocket payloads
return false;
}
@@ -81,19 +120,20 @@ export function useEnforcementStream(
const queryClient = useQueryClient();
const handleNotification = useCallback(
(notification: any) => {
(notification: EnforcementNotification) => {
// Filter by enforcement ID if specified
if (enforcementId && notification.entity_id !== enforcementId) {
return;
}
// Extract enforcement data from notification payload (flat structure)
const enforcementData = notification.payload as any;
const enforcementData =
notification.payload as Partial<EnforcementSummary>;
// Update specific enforcement query if it exists
queryClient.setQueryData(
["enforcements", notification.entity_id],
(old: any) => {
(old: EnforcementDetailCache | undefined) => {
if (!old) return old;
return {
...old,
@@ -108,21 +148,24 @@ export function useEnforcementStream(
// Update enforcement list queries by modifying existing data
// We need to iterate manually to access query keys for filtering
const queries = queryClient
.getQueriesData({ queryKey: ["enforcements"], exact: false })
.filter(([, data]) => data && Array.isArray((data as any)?.data));
.getQueriesData<EnforcementListCache>({
queryKey: ["enforcements"],
exact: false,
})
.filter(([, data]) => data && Array.isArray(data?.data));
queries.forEach(([queryKey, oldData]) => {
// Extract query params from the query key (format: ["enforcements", params])
const queryParams = queryKey[1];
const queryParams = queryKey[1] as EnforcementQueryParams | undefined;
const old = oldData as any;
const old = oldData as EnforcementListCache;
// Check if enforcement already exists in the list
const existingIndex = old.data.findIndex(
(enf: any) => enf.id === notification.entity_id,
(enf) => enf.id === notification.entity_id,
);
let updatedData;
let updatedData: EnforcementSummary[];
if (existingIndex >= 0) {
// Always update existing enforcement in the list
updatedData = [...old.data];
@@ -144,7 +187,10 @@ export function useEnforcementStream(
// Only add new enforcement if it matches the query parameters
if (enforcementMatchesParams(enforcementData, queryParams)) {
// Add to beginning and cap at 50 items to prevent performance issues
updatedData = [enforcementData, ...old.data].slice(0, 50);
updatedData = [
enforcementData as EnforcementSummary,
...old.data,
].slice(0, 50);
} else {
// Don't modify the list if the new enforcement doesn't match the query
return;