58 lines
2.8 KiB
TypeScript
58 lines
2.8 KiB
TypeScript
import { readEnginePreferences } from '../../app/engine-preferences';
|
|
import { beginActivity } from '../../platform/activity-store';
|
|
import type { RegulatoryFramework, RegulatoryMappingResponse } from '../regulatory-mapping';
|
|
import { statusPollApi } from './status-poll-client';
|
|
|
|
const tracked = new Set<string>();
|
|
const listeners = new Set<(key: string, response: RegulatoryMappingResponse) => void>();
|
|
const isActive = (response: RegulatoryMappingResponse) => response.state === 'queued' || response.state === 'running';
|
|
const wait = (milliseconds: number) => new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
|
|
// ISO keys keep their earlier form; a BSI mapping of the same finding is tracked separately.
|
|
export const mappingRunKey = (projectId: string, resultProjectionId: string, framework: RegulatoryFramework = 'iso27001') =>
|
|
`${projectId}\u0000${resultProjectionId}${framework === 'iso27001' ? '' : `\u0000${framework}`}`;
|
|
|
|
/** Views showing a mapping receive the tracker's status updates instead of polling themselves. */
|
|
export function subscribeRegulatoryMappingRuns(listener: (key: string, response: RegulatoryMappingResponse) => void): () => void {
|
|
listeners.add(listener);
|
|
return () => { listeners.delete(listener); };
|
|
}
|
|
|
|
/**
|
|
* Keeps one `Mapping ISO controls` activity for a queued or running mapping until the backend ends it,
|
|
* independent of the open window, the selected finding or the active view. Switching the Compliance
|
|
* Engine off stops observing; the backend work itself is never cancelled.
|
|
*/
|
|
export function trackRegulatoryMappingRun(projectId: string, resultProjectionId: string, response: RegulatoryMappingResponse): void {
|
|
const framework = response.framework ?? 'iso27001';
|
|
const key = mappingRunKey(projectId, resultProjectionId, framework);
|
|
if (!isActive(response) || tracked.has(key)) return;
|
|
tracked.add(key);
|
|
const label = framework === 'bsi-kernel' ? 'Mapping BSI requirements' : 'Mapping ISO controls';
|
|
const activity = beginActivity('Compliance Engine', label, `compliance-mapping:${key}`);
|
|
if (response.state === 'queued') activity.queue();
|
|
void (async () => {
|
|
let current = response;
|
|
try {
|
|
while (isActive(current)) {
|
|
await wait(current.pollAfterMs ?? 1_500);
|
|
if (!readEnginePreferences().compliance) {
|
|
activity.update(`${label} continues in the backend`);
|
|
activity.complete();
|
|
return;
|
|
}
|
|
current = await statusPollApi.getRegulatoryMappings(projectId, resultProjectionId, framework);
|
|
if (current.state === 'running') activity.run();
|
|
listeners.forEach(listener => listener(key, current));
|
|
}
|
|
if (current.state === 'failed') activity.fail();
|
|
else activity.complete();
|
|
} catch {
|
|
activity.update(`${label}: status could not be checked`);
|
|
activity.fail();
|
|
} finally {
|
|
tracked.delete(key);
|
|
}
|
|
})();
|
|
}
|