feat(phase-16): enhanced Tests view, Red/Blue dashboard metrics, and Template admin panel (T-122, T-123, T-124)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import client from "./client";
|
||||
import type { CoverageSummary, TacticCoverage } from "../types/models";
|
||||
|
||||
// ── V1 — Coverage ───────────────────────────────────────────────────
|
||||
|
||||
/** Fetch the global coverage summary. */
|
||||
export async function getCoverageSummary(): Promise<CoverageSummary> {
|
||||
const { data } = await client.get<CoverageSummary>("/metrics/summary");
|
||||
@@ -12,3 +14,69 @@ export async function getCoverageByTactic(): Promise<TacticCoverage[]> {
|
||||
const { data } = await client.get<TacticCoverage[]>("/metrics/by-tactic");
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── V2 — Test Pipeline ──────────────────────────────────────────────
|
||||
|
||||
export interface TestPipelineCounts {
|
||||
draft: number;
|
||||
red_executing: number;
|
||||
blue_evaluating: number;
|
||||
in_review: number;
|
||||
validated: number;
|
||||
rejected: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
/** Fetch test counts per pipeline state. */
|
||||
export async function getTestPipeline(): Promise<TestPipelineCounts> {
|
||||
const { data } = await client.get<TestPipelineCounts>("/metrics/test-pipeline");
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── V2 — Team Activity ──────────────────────────────────────────────
|
||||
|
||||
export interface TeamActivityItem {
|
||||
team: string;
|
||||
tests_completed: number;
|
||||
tests_pending: number;
|
||||
avg_completion_hours: number | null;
|
||||
}
|
||||
|
||||
/** Fetch activity summary for Red and Blue teams. */
|
||||
export async function getTeamActivity(): Promise<TeamActivityItem[]> {
|
||||
const { data } = await client.get<TeamActivityItem[]>("/metrics/team-activity");
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── V2 — Validation Rate ────────────────────────────────────────────
|
||||
|
||||
export interface ValidationRateItem {
|
||||
role: string;
|
||||
total_reviewed: number;
|
||||
approved: number;
|
||||
rejected: number;
|
||||
approval_rate: number;
|
||||
}
|
||||
|
||||
/** Fetch approval/rejection rates for managers. */
|
||||
export async function getValidationRate(): Promise<ValidationRateItem[]> {
|
||||
const { data } = await client.get<ValidationRateItem[]>("/metrics/validation-rate");
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── V2 — Recent Tests ───────────────────────────────────────────────
|
||||
|
||||
export interface RecentTestItem {
|
||||
id: string;
|
||||
name: string;
|
||||
state: string;
|
||||
technique_mitre_id: string | null;
|
||||
technique_name: string | null;
|
||||
created_at: string | null;
|
||||
}
|
||||
|
||||
/** Fetch the 10 most recently updated tests. */
|
||||
export async function getRecentTests(): Promise<RecentTestItem[]> {
|
||||
const { data } = await client.get<RecentTestItem[]>("/metrics/recent-tests");
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,53 @@ export async function createTemplate(
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Stats (admin) ──────────────────────────────────────────────────
|
||||
|
||||
export interface TemplateStats {
|
||||
total: number;
|
||||
active: number;
|
||||
inactive: number;
|
||||
by_source: Record<string, number>;
|
||||
by_platform: Record<string, number>;
|
||||
}
|
||||
|
||||
/** Fetch template catalog statistics. Admin only. */
|
||||
export async function getTemplateStats(): Promise<TemplateStats> {
|
||||
const { data } = await client.get<TemplateStats>("/test-templates/stats");
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Toggle active (admin) ──────────────────────────────────────────
|
||||
|
||||
/** Toggle a template between active/inactive. Admin only. */
|
||||
export async function toggleTemplateActive(
|
||||
id: string,
|
||||
): Promise<TestTemplate> {
|
||||
const { data } = await client.patch<TestTemplate>(
|
||||
`/test-templates/${id}/toggle-active`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── All templates (include inactive, for admin) ────────────────────
|
||||
|
||||
/** Fetch all templates including inactive ones (for admin management). */
|
||||
export async function getAllTemplates(
|
||||
filters?: TemplateFilters,
|
||||
): Promise<TestTemplate[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.source) params.append("source", filters.source);
|
||||
if (filters?.platform) params.append("platform", filters.platform);
|
||||
if (filters?.search) params.append("search", filters.search);
|
||||
if (filters?.offset !== undefined) params.append("offset", String(filters.offset));
|
||||
if (filters?.limit !== undefined) params.append("limit", String(filters.limit));
|
||||
|
||||
const { data } = await client.get<TestTemplate[]>(
|
||||
`/test-templates${params.toString() ? `?${params}` : ""}`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Import Atomic Red Team ─────────────────────────────────────────
|
||||
|
||||
/** Trigger Atomic Red Team import. Admin only. */
|
||||
|
||||
@@ -59,6 +59,9 @@ export interface TestValidatePayload {
|
||||
export interface TestListFilters {
|
||||
state?: TestState;
|
||||
technique_id?: string;
|
||||
platform?: string;
|
||||
created_by?: string;
|
||||
pending_validation_side?: "red" | "blue";
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
}
|
||||
@@ -70,6 +73,9 @@ export async function getTests(filters?: TestListFilters): Promise<Test[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.state) params.append("state", filters.state);
|
||||
if (filters?.technique_id) params.append("technique_id", filters.technique_id);
|
||||
if (filters?.platform) params.append("platform", filters.platform);
|
||||
if (filters?.created_by) params.append("created_by", filters.created_by);
|
||||
if (filters?.pending_validation_side) params.append("pending_validation_side", filters.pending_validation_side);
|
||||
if (filters?.offset !== undefined) params.append("offset", String(filters.offset));
|
||||
if (filters?.limit !== undefined) params.append("limit", String(filters.limit));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user