fix: scope FP workflow counts donut by BU
- Rewrite /fp-workflow-counts endpoint to query ivanti_findings table directly with optional teams ILIKE filter (replaces pre-computed JSON blob) - Frontend passes getActiveTeamsParam() to FP counts fetch - FP counts refresh on scope toggle change alongside open/closed counts - Both FP Finding Status and FP Workflow Status donuts now respect BU scope
This commit is contained in:
@@ -1222,18 +1222,56 @@ function createIvantiFindingsRouter(db, requireAuth) {
|
||||
*
|
||||
* Return FP finding counts and unique workflow ID counts (open + closed),
|
||||
* broken down by workflow status.
|
||||
* Accepts optional `teams` query parameter to scope to specific BUs.
|
||||
*
|
||||
* @query {string} [teams] - Comma-separated team names (e.g. 'STEAM,ACCESS-ENG')
|
||||
* @returns {Object} 200 - { findingCounts: Object, findingTotal: number, idCounts: Object, idTotal: number }
|
||||
* @returns {Object} 500 - { error: string } on database error
|
||||
*/
|
||||
router.get('/fp-workflow-counts', async (req, res) => {
|
||||
try {
|
||||
const { rows } = await pool.query('SELECT fp_workflow_counts_json, fp_id_counts_json FROM ivanti_counts_cache WHERE id=1');
|
||||
const row = rows[0];
|
||||
let findingCounts = {};
|
||||
let idCounts = {};
|
||||
try { findingCounts = JSON.parse(row?.fp_workflow_counts_json || '{}'); } catch (_) {}
|
||||
try { idCounts = JSON.parse(row?.fp_id_counts_json || '{}'); } catch (_) {}
|
||||
const teamsParam = req.query.teams;
|
||||
let whereExtra = '';
|
||||
const params = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (teamsParam) {
|
||||
const teams = teamsParam.split(',').map(t => t.trim()).filter(Boolean);
|
||||
if (teams.length > 0) {
|
||||
const patterns = teams.map(t => `%${t}%`);
|
||||
whereExtra = ` AND bu_ownership ILIKE ANY($${paramIndex++}::text[])`;
|
||||
params.push(patterns);
|
||||
}
|
||||
}
|
||||
|
||||
// Finding counts: number of findings per workflow state
|
||||
const findingResult = await pool.query(
|
||||
`SELECT workflow_state, COUNT(*) as count
|
||||
FROM ivanti_findings
|
||||
WHERE workflow_id IS NOT NULL ${whereExtra}
|
||||
GROUP BY workflow_state`,
|
||||
params
|
||||
);
|
||||
const findingCounts = {};
|
||||
findingResult.rows.forEach(r => {
|
||||
const state = r.workflow_state || 'Unknown';
|
||||
findingCounts[state] = parseInt(r.count);
|
||||
});
|
||||
|
||||
// ID counts: number of unique workflow IDs per state
|
||||
const idResult = await pool.query(
|
||||
`SELECT workflow_state, COUNT(DISTINCT workflow_id) as count
|
||||
FROM ivanti_findings
|
||||
WHERE workflow_id IS NOT NULL ${whereExtra}
|
||||
GROUP BY workflow_state`,
|
||||
params
|
||||
);
|
||||
const idCounts = {};
|
||||
idResult.rows.forEach(r => {
|
||||
const state = r.workflow_state || 'Unknown';
|
||||
idCounts[state] = parseInt(r.count);
|
||||
});
|
||||
|
||||
res.json({
|
||||
findingCounts,
|
||||
findingTotal: Object.values(findingCounts).reduce((a, b) => a + b, 0),
|
||||
|
||||
@@ -5069,7 +5069,11 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
|
||||
|
||||
const fetchFPWorkflowCounts = async () => {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/ivanti/findings/fp-workflow-counts`, { credentials: 'include' });
|
||||
const teamsParam = getActiveTeamsParam();
|
||||
const url = teamsParam
|
||||
? `${API_BASE}/ivanti/findings/fp-workflow-counts?teams=${encodeURIComponent(teamsParam)}`
|
||||
: `${API_BASE}/ivanti/findings/fp-workflow-counts`;
|
||||
const res = await fetch(url, { credentials: 'include' });
|
||||
const data = await res.json();
|
||||
if (res.ok) setFPCounts({
|
||||
findingCounts: data.findingCounts || {},
|
||||
@@ -5197,6 +5201,8 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(data => { if (data) setStatusCounts({ open: data.open ?? 0, closed: data.closed ?? 0 }); })
|
||||
.catch(() => {});
|
||||
// Also refresh FP workflow counts for the new scope
|
||||
fetchFPWorkflowCounts();
|
||||
}, [adminScope]); // eslint-disable-line
|
||||
|
||||
// Set/clear a single column filter
|
||||
|
||||
Reference in New Issue
Block a user