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 { 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 }; }