mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:31:33 +00:00
fix(scep-intune): use useTrackedMutation for trust-anchor reload (M-009)
Phase 9 follow-up — the M-009 hard-zero regression guard in
.github/workflows/ci.yml flagged the SCEPAdminPage's reload mutation as
a bare useMutation() call. The repo's invalidation contract requires
every mutation to go through useTrackedMutation with explicit
invalidates: QueryKey[] | 'noop' so cached data never goes stale after
a write.
Swap the bare useMutation for useTrackedMutation with
invalidates: [['admin', 'scep', 'intune', 'stats']] — the trust-anchor
reload changes the per-profile trust pool reflected in IntuneStats, so
the stats query MUST refetch on success. The audit-log queries stay on
their own 60s timer (a SIGHUP-equivalent reload doesn't backfill new
audit rows; nothing to invalidate there).
Verification:
* tsc --noEmit clean
* vitest SCEPAdminPage.test.tsx: 13/13 still pass (the wrapper's
onSuccess fires AFTER invalidation, so the modal-close + state
reset assertions hold)
* M-009 grep guard reproduced locally — bare useMutation sites = 0
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getAdminSCEPIntuneStats, reloadAdminSCEPIntuneTrust, getAuditEvents } from '../api/client';
|
import { getAdminSCEPIntuneStats, reloadAdminSCEPIntuneTrust, getAuditEvents } from '../api/client';
|
||||||
import PageHeader from '../components/PageHeader';
|
import PageHeader from '../components/PageHeader';
|
||||||
import ErrorState from '../components/ErrorState';
|
import ErrorState from '../components/ErrorState';
|
||||||
import { useAuth } from '../components/AuthProvider';
|
import { useAuth } from '../components/AuthProvider';
|
||||||
|
import { useTrackedMutation } from '../hooks/useTrackedMutation';
|
||||||
import { formatDateTime } from '../api/utils';
|
import { formatDateTime } from '../api/utils';
|
||||||
import type { IntuneStatsSnapshot, IntuneTrustAnchorInfo, AuditEvent } from '../api/types';
|
import type { IntuneStatsSnapshot, IntuneTrustAnchorInfo, AuditEvent } from '../api/types';
|
||||||
|
|
||||||
@@ -299,7 +300,6 @@ function RecentFailuresTable({ events }: { events: AuditEvent[] }) {
|
|||||||
|
|
||||||
export default function SCEPAdminPage() {
|
export default function SCEPAdminPage() {
|
||||||
const auth = useAuth();
|
const auth = useAuth();
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const [reloadTarget, setReloadTarget] = useState<IntuneStatsSnapshot | null>(null);
|
const [reloadTarget, setReloadTarget] = useState<IntuneStatsSnapshot | null>(null);
|
||||||
const [reloadError, setReloadError] = useState<string | undefined>(undefined);
|
const [reloadError, setReloadError] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
@@ -328,12 +328,23 @@ export default function SCEPAdminPage() {
|
|||||||
refetchInterval: 60_000,
|
refetchInterval: 60_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const reloadMutation = useMutation({
|
// Bundle-8 / M-009 invalidation contract: trust-anchor reload changes
|
||||||
|
// both the per-profile trust pool (reflected in IntuneStats) AND every
|
||||||
|
// recently-failed Intune enrollment counter that might now succeed on
|
||||||
|
// retry. We invalidate the stats key so the per-profile trust-anchor
|
||||||
|
// panel reflects the new pool immediately; the audit log queries
|
||||||
|
// remain on their 60s timer (a SIGHUP-equivalent reload doesn't
|
||||||
|
// backfill new audit rows).
|
||||||
|
const reloadMutation = useTrackedMutation<
|
||||||
|
Awaited<ReturnType<typeof reloadAdminSCEPIntuneTrust>>,
|
||||||
|
Error,
|
||||||
|
string
|
||||||
|
>({
|
||||||
mutationFn: (pathID: string) => reloadAdminSCEPIntuneTrust(pathID),
|
mutationFn: (pathID: string) => reloadAdminSCEPIntuneTrust(pathID),
|
||||||
|
invalidates: [['admin', 'scep', 'intune', 'stats']],
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setReloadTarget(null);
|
setReloadTarget(null);
|
||||||
setReloadError(undefined);
|
setReloadError(undefined);
|
||||||
void queryClient.invalidateQueries({ queryKey: ['admin', 'scep', 'intune', 'stats'] });
|
|
||||||
},
|
},
|
||||||
onError: (err: Error) => {
|
onError: (err: Error) => {
|
||||||
setReloadError(err.message);
|
setReloadError(err.message);
|
||||||
|
|||||||
Reference in New Issue
Block a user