// ----------------------------------------------------------------------------- // S3UnifiedArtifactStore.Read.cs // Sprint: SPRINT_20260118_017_Evidence_artifact_store_unification // Task: AS-002 - Implement S3-backed ArtifactStore // Description: Read operations for the unified artifact store // ----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; using StellaOps.Artifact.Core; namespace StellaOps.Artifact.Infrastructure; public sealed partial class S3UnifiedArtifactStore { /// public async Task ReadAsync( string bomRef, string? serialNumber, string? artifactId, CancellationToken ct = default) { try { var entry = await ResolveEntryAsync(bomRef, serialNumber, artifactId, ct).ConfigureAwait(false); if (entry == null) { return ArtifactReadResult.NotFound($"No artifact found for bom-ref: {bomRef}"); } var stream = await _client.GetObjectAsync(_options.BucketName, entry.StorageKey, ct) .ConfigureAwait(false); if (stream == null) { return ArtifactReadResult.NotFound($"Object not found in S3: {entry.StorageKey}"); } return ArtifactReadResult.Succeeded(stream, CreateMetadata(entry)); } catch (Exception ex) { _logger.LogError(ex, "Failed to read artifact for bom-ref {BomRef}", bomRef); return ArtifactReadResult.NotFound(ex.Message); } } private async Task ResolveEntryAsync( string bomRef, string? serialNumber, string? artifactId, CancellationToken ct) { if (serialNumber != null && artifactId != null) { return await _indexRepository.GetAsync(bomRef, serialNumber, artifactId, ct).ConfigureAwait(false); } if (serialNumber != null) { var entries = await _indexRepository.FindByBomRefAndSerialAsync(bomRef, serialNumber, ct) .ConfigureAwait(false); return entries.FirstOrDefault(); } var candidates = await _indexRepository.FindByBomRefAsync(bomRef, ct).ConfigureAwait(false); return candidates.FirstOrDefault(); } }