Assessor_2.0/server/gpo/gpo-backup-status-service.ts

60 lines
3.0 KiB
TypeScript

import type { GpoBackupStatusRecord, GpoBackupStatusResponse } from '../../src/api/assessor-api-contract';
import type { ImportProcessingRun, IngestionJob } from '../../src/domain/ingestion-persistence';
import type { AssessorRepository } from '../../src/persistence/assessor-repository';
import { RepositoryNotFoundError } from '../../src/persistence/repository-errors';
function latestByUpdatedAt(jobs: IngestionJob[]): IngestionJob | undefined {
return jobs.slice().sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt) || right.id.localeCompare(left.id))[0];
}
function latestRun(runs: ImportProcessingRun[]): ImportProcessingRun | undefined {
return runs.slice().sort((left, right) => Date.parse(right.completedAt) - Date.parse(left.completedAt) || right.id.localeCompare(left.id))[0];
}
function displayState(job: IngestionJob | undefined, run: ImportProcessingRun | undefined): GpoBackupStatusRecord['state'] {
if (!job) return 'recovery-required';
if (job.status === 'dead-letter') return 'dead-letter';
if (job.status === 'queued' || job.status === 'running') return job.status;
return run?.status === 'partial' ? 'partial' : 'succeeded';
}
export async function listProjectGpoBackupStatus(
repository: AssessorRepository,
projectId: string,
generatedAt: string
): Promise<GpoBackupStatusResponse> {
if (!await repository.getProject(projectId)) throw new RepositoryNotFoundError(`Projekt ${projectId} ist nicht vorhanden.`);
const [artifacts, jobs, instances, runs] = await Promise.all([
repository.listImportArtifacts(projectId),
repository.listIngestionJobs(projectId),
repository.listGpoBackupInstances(projectId),
repository.listProjectProcessingRuns(projectId)
]);
const records: GpoBackupStatusRecord[] = [];
for (const artifact of artifacts.filter(item => item.kind === 'source-file' && item.relativePath.startsWith('gpo-backups/'))) {
const job = latestByUpdatedAt(jobs.filter(item => item.kind === 'analyze-gpo-backup' && item.artifactId === artifact.id));
const run = latestRun(runs.filter(item => item.artifactId === artifact.id));
const gpoCount = run ? instances.filter(item => item.processingRunId === run.id).length : 0;
records.push({
version: 1,
artifactId: artifact.id,
assessmentId: artifact.assessmentId,
jobId: job?.id,
fileName: artifact.relativePath.slice('gpo-backups/'.length),
sizeBytes: artifact.sizeBytes,
submittedAt: artifact.createdAt,
updatedAt: job?.updatedAt ?? artifact.createdAt,
state: displayState(job, run),
attempts: job?.attempts ?? 0,
maxAttempts: job?.maxAttempts ?? 3,
lastErrorCode: job?.lastErrorCode,
processingStatus: run?.status,
completedAt: run?.completedAt,
gpoCount,
backupInstanceCount: gpoCount
});
}
records.sort((left, right) => Date.parse(right.submittedAt) - Date.parse(left.submittedAt) || right.artifactId.localeCompare(left.artifactId));
return { version: 1, projectId, generatedAt, records };
}