chore(per-instance-hook-scoping): archive verified+synced change
Move to openspec/changes/archive/2026-07-09-per-instance-hook-scoping/ (R100 renames preserved). 9 artifacts. Canonical openspec/specs/ service-instance-scoping/ remains. Resolves multi-instance wrong-data bug (hooks now scope by instance.id; instance switcher re-scopes). Carry-overs: fetchBackupDashboard untouched (design decision 5); subquery scoping for runs/alerts (schema asymmetry).
This commit is contained in:
@@ -6,7 +6,9 @@ import type {
|
||||
BackupRun,
|
||||
} from "../types/backups";
|
||||
|
||||
export async function fetchBackupJobs(serviceId?: string): Promise<BackupJob[]> {
|
||||
export async function fetchBackupJobs(
|
||||
serviceId?: string,
|
||||
): Promise<BackupJob[]> {
|
||||
return get<BackupJob[]>(
|
||||
"/api/backups/jobs",
|
||||
serviceId ? { service_id: serviceId } : undefined,
|
||||
|
||||
@@ -3,7 +3,10 @@ import { renderHook } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { createElement, type ReactNode } from "react";
|
||||
import { useBackupJobs, useBackupRuns, useBackupAlerts } from "../useBackups";
|
||||
import { useAlertmanagerAlerts, usePrometheusStatus } from "../useObservability";
|
||||
import {
|
||||
useAlertmanagerAlerts,
|
||||
usePrometheusStatus,
|
||||
} from "../useObservability";
|
||||
|
||||
vi.mock("../../api/client", () => ({
|
||||
fetchAlertmanagerAlerts: vi.fn(),
|
||||
@@ -68,10 +71,9 @@ describe("per-instance hook queryKey isolation", () => {
|
||||
|
||||
it("useAlertmanagerAlerts includes serviceId in queryKey", () => {
|
||||
const wrapper = createWrapper();
|
||||
const { result: a } = renderHook(
|
||||
() => useAlertmanagerAlerts("svc-a"),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result: a } = renderHook(() => useAlertmanagerAlerts("svc-a"), {
|
||||
wrapper,
|
||||
});
|
||||
expect(a).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,59 +1,75 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
acknowledgeBackupAlert,
|
||||
fetchBackupAlerts,
|
||||
fetchBackupDashboard,
|
||||
fetchBackupJob,
|
||||
fetchBackupJobs,
|
||||
fetchBackupRuns,
|
||||
acknowledgeBackupAlert,
|
||||
fetchBackupAlerts,
|
||||
fetchBackupDashboard,
|
||||
fetchBackupJob,
|
||||
fetchBackupJobs,
|
||||
fetchBackupRuns,
|
||||
} from "../api/backups";
|
||||
|
||||
export function useBackupJobs(serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "jobs", serviceId ?? ""],
|
||||
queryFn: () => fetchBackupJobs(serviceId),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: ["backups", "jobs", serviceId ?? ""],
|
||||
queryFn: () => fetchBackupJobs(serviceId),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useBackupJob(jobId: string) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "jobs", jobId],
|
||||
queryFn: () => fetchBackupJob(jobId),
|
||||
enabled: !!jobId,
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: ["backups", "jobs", jobId],
|
||||
queryFn: () => fetchBackupJob(jobId),
|
||||
enabled: !!jobId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useBackupRuns(jobId?: string, status?: string, serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "runs", jobId, status, serviceId ?? ""],
|
||||
queryFn: () => fetchBackupRuns(jobId, status, serviceId),
|
||||
refetchInterval: 15_000,
|
||||
});
|
||||
export function useBackupRuns(
|
||||
jobId?: string,
|
||||
status?: string,
|
||||
serviceId?: string,
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "runs", jobId, status, serviceId ?? ""],
|
||||
queryFn: () => fetchBackupRuns(jobId, status, serviceId),
|
||||
refetchInterval: 15_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useBackupAlerts(jobId?: string, acknowledged?: boolean, severity?: string, serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "alerts", jobId, acknowledged, severity, serviceId ?? ""],
|
||||
queryFn: () => fetchBackupAlerts(jobId, acknowledged, severity, serviceId),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
export function useBackupAlerts(
|
||||
jobId?: string,
|
||||
acknowledged?: boolean,
|
||||
severity?: string,
|
||||
serviceId?: string,
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: [
|
||||
"backups",
|
||||
"alerts",
|
||||
jobId,
|
||||
acknowledged,
|
||||
severity,
|
||||
serviceId ?? "",
|
||||
],
|
||||
queryFn: () => fetchBackupAlerts(jobId, acknowledged, severity, serviceId),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAcknowledgeAlert() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: acknowledgeBackupAlert,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["backups", "alerts"] });
|
||||
},
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: acknowledgeBackupAlert,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["backups", "alerts"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useBackupDashboard() {
|
||||
return useQuery({
|
||||
queryKey: ["dashboard", "backups"],
|
||||
queryFn: fetchBackupDashboard,
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: ["dashboard", "backups"],
|
||||
queryFn: fetchBackupDashboard,
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -110,7 +110,9 @@ export function AlertsTab({ instance }: { instance: ServiceInstance }) {
|
||||
isLoading: alertsLoading,
|
||||
error: alertsError,
|
||||
} = useAlertmanagerAlerts(instance.id);
|
||||
const { data: status, isLoading: statusLoading } = useAlertmanagerStatus(instance.id);
|
||||
const { data: status, isLoading: statusLoading } = useAlertmanagerStatus(
|
||||
instance.id,
|
||||
);
|
||||
|
||||
const statusDetail = status?.up
|
||||
? status.version
|
||||
|
||||
@@ -58,12 +58,12 @@ vi.mock("../../../hooks/useObservability", () => ({
|
||||
describe("AlertsTab", () => {
|
||||
it("passes instance.id to scoped hooks", () => {
|
||||
render(<AlertsTab instance={instance} />);
|
||||
expect(vi.mocked(useObservability.useAlertmanagerAlerts)).toHaveBeenCalledWith(
|
||||
"am-1",
|
||||
);
|
||||
expect(vi.mocked(useObservability.useAlertmanagerStatus)).toHaveBeenCalledWith(
|
||||
"am-1",
|
||||
);
|
||||
expect(
|
||||
vi.mocked(useObservability.useAlertmanagerAlerts),
|
||||
).toHaveBeenCalledWith("am-1");
|
||||
expect(
|
||||
vi.mocked(useObservability.useAlertmanagerStatus),
|
||||
).toHaveBeenCalledWith("am-1");
|
||||
});
|
||||
|
||||
it("renders the alert count and alert names", () => {
|
||||
|
||||
Reference in New Issue
Block a user