feat(per-instance-hook-scoping): scope observability + backup hooks by instance
This commit is contained in:
@@ -1,14 +1,9 @@
|
||||
/**
|
||||
* Alertmanager Alerts tab (spec R2.4, R8.2).
|
||||
*
|
||||
* Lifts the Alertmanager alerts content from the old cross-service
|
||||
* ObservabilityPage into an instance-scoped tab. Renders the active-alert
|
||||
* summary (total + by severity) and the expandable alert list.
|
||||
*
|
||||
* The hooks (useAlertmanagerAlerts, useAlertmanagerStatus) are global /
|
||||
* first-configured for now — they don't accept a service_id yet. Wiring
|
||||
* `instance.id` into them is a documented follow-up once the hooks gain the
|
||||
* parameter. The `instance` prop is accepted for future scoping.
|
||||
* Instance-scoped tab rendering the active-alert summary (total + by severity)
|
||||
* and the expandable alert list. Hooks are scoped by instance.id so
|
||||
* multi-instance setups show data for the selected Alertmanager only.
|
||||
*/
|
||||
import { AlertTriangle, Bell, ChevronDown, Inbox } from "lucide-react";
|
||||
import {
|
||||
@@ -110,16 +105,12 @@ function AlertItem({ alert }: { alert: AlertmanagerAlert }) {
|
||||
}
|
||||
|
||||
export function AlertsTab({ instance }: { instance: ServiceInstance }) {
|
||||
// Global / first-configured hooks for now; instance.id scoping is a
|
||||
// follow-up (see file docstring).
|
||||
void instance;
|
||||
|
||||
const {
|
||||
data: alertsSummary,
|
||||
isLoading: alertsLoading,
|
||||
error: alertsError,
|
||||
} = useAlertmanagerAlerts();
|
||||
const { data: status, isLoading: statusLoading } = useAlertmanagerStatus();
|
||||
} = useAlertmanagerAlerts(instance.id);
|
||||
const { data: status, isLoading: statusLoading } = useAlertmanagerStatus(instance.id);
|
||||
|
||||
const statusDetail = status?.up
|
||||
? status.version
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
/**
|
||||
* JobsTab — operational content for the backups service page.
|
||||
*
|
||||
* Lifted from the old top-level `components/BackupsPage.tsx`. The three
|
||||
* sub-tables (Jobs / Runs / Alerts) and their hooks are preserved verbatim.
|
||||
*
|
||||
* NOTE: the backup hooks currently query globally (no service_id filter).
|
||||
* The backend gained `service_id` attribution in Slice 3, but the hooks don't
|
||||
* yet accept a serviceId param. This tab shows ALL backups data for now;
|
||||
* per-instance scoping by `instance.id` is a follow-up once the hooks gain the
|
||||
* parameter.
|
||||
* Instance-scoped: hooks filter by instance.id so multi-instance setups
|
||||
* show only the selected backups service's jobs, runs, and alerts.
|
||||
*/
|
||||
import { useState } from "react";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
@@ -24,15 +18,18 @@ import BackupRunsTable from "../../components/BackupRunsTable";
|
||||
import type { ServiceInstance } from "../../types";
|
||||
|
||||
export function JobsTab({ instance }: { instance: ServiceInstance }) {
|
||||
// instance.id is not yet used — backup hooks query globally (see file
|
||||
// docstring). Per-instance scoping is a follow-up.
|
||||
void instance;
|
||||
const [tab, setTab] = useState("jobs");
|
||||
const { data: jobsData, isLoading: jobsLoading } = useBackupJobs();
|
||||
const { data: runsData, isLoading: runsLoading } = useBackupRuns();
|
||||
const { data: jobsData, isLoading: jobsLoading } = useBackupJobs(instance.id);
|
||||
const { data: runsData, isLoading: runsLoading } = useBackupRuns(
|
||||
undefined,
|
||||
undefined,
|
||||
instance.id,
|
||||
);
|
||||
const { data: alertsData, isLoading: alertsLoading } = useBackupAlerts(
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
instance.id,
|
||||
);
|
||||
const acknowledgeMutation = useAcknowledgeAlert();
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/**
|
||||
* Prometheus Metrics tab (spec R2.4, R8.2).
|
||||
*
|
||||
* Lifts the Prometheus status + targets content from the old cross-service
|
||||
* ObservabilityPage into an instance-scoped tab. Shows service health and
|
||||
* the Node Exporter scrape-targets list.
|
||||
*
|
||||
* The hooks (usePrometheusStatus, usePrometheusTargets) are global /
|
||||
* first-configured for now. Wiring `instance.id` is a follow-up.
|
||||
* Instance-scoped tab showing Prometheus service health.
|
||||
* usePrometheusStatus is scoped by instance.id; usePrometheusTargets
|
||||
* stays global (returns Node Exporter scrape targets for external Prom).
|
||||
*/
|
||||
import { Radio } from "lucide-react";
|
||||
import {
|
||||
@@ -41,15 +38,11 @@ function TargetsTable({ targets }: { targets: PrometheusTarget[] }) {
|
||||
}
|
||||
|
||||
export function MetricsTab({ instance }: { instance: ServiceInstance }) {
|
||||
// Global / first-configured hooks for now; instance.id scoping is a
|
||||
// follow-up (see file docstring).
|
||||
void instance;
|
||||
|
||||
const {
|
||||
data: status,
|
||||
isLoading: statusLoading,
|
||||
error: statusError,
|
||||
} = usePrometheusStatus();
|
||||
} = usePrometheusStatus(instance.id);
|
||||
const {
|
||||
data: targets,
|
||||
isLoading: targetsLoading,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import * as useObservability from "../../../hooks/useObservability";
|
||||
import { AlertsTab } from "../AlertsTab";
|
||||
import type { ServiceInstance } from "../../../types";
|
||||
|
||||
@@ -15,7 +16,7 @@ const instance: ServiceInstance = {
|
||||
};
|
||||
|
||||
vi.mock("../../../hooks/useObservability", () => ({
|
||||
useAlertmanagerAlerts: () => ({
|
||||
useAlertmanagerAlerts: vi.fn(() => ({
|
||||
data: {
|
||||
total: 2,
|
||||
by_severity: { critical: 1, warning: 1 },
|
||||
@@ -46,15 +47,25 @@ vi.mock("../../../hooks/useObservability", () => ({
|
||||
},
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
useAlertmanagerStatus: () => ({
|
||||
})),
|
||||
useAlertmanagerStatus: vi.fn(() => ({
|
||||
data: { up: true, version: "0.27.0", uptime: "", name: "", peers: [] },
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
})),
|
||||
}));
|
||||
|
||||
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",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders the alert count and alert names", () => {
|
||||
render(<AlertsTab instance={instance} />);
|
||||
expect(screen.getByText(/Active Alerts \(2\)/)).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user