stabilizaiton work - projects rework for maintenanceability and ui livening
This commit is contained in:
@@ -8,8 +8,10 @@
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Artifact.Core;
|
||||
using StellaOps.Concelier.SbomIntegration.Parsing;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Infrastructure.Postgres.Options;
|
||||
|
||||
namespace StellaOps.Artifact.Infrastructure;
|
||||
@@ -31,32 +33,23 @@ public static class ServiceCollectionExtensions
|
||||
IConfiguration configuration,
|
||||
string sectionName = "ArtifactStore")
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(configuration);
|
||||
// Configure S3 store options
|
||||
services.Configure<S3UnifiedArtifactStoreOptions>(configuration.GetSection($"{sectionName}:S3"));
|
||||
|
||||
// Configure PostgreSQL options for index
|
||||
services.Configure<PostgresOptions>("Artifact", configuration.GetSection($"{sectionName}:Postgres"));
|
||||
services.Configure<PostgresOptions>(ArtifactDataSource.OptionsName, configuration.GetSection($"{sectionName}:Postgres"));
|
||||
|
||||
services.TryAddSingleton(TimeProvider.System);
|
||||
services.TryAddSingleton<IGuidProvider, SystemGuidProvider>();
|
||||
services.TryAddScoped<IArtifactTenantContext, ArtifactTenantContext>();
|
||||
// Register data source
|
||||
services.AddSingleton<ArtifactDataSource>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptionsSnapshot<PostgresOptions>>().Get("Artifact");
|
||||
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<ArtifactDataSource>>();
|
||||
return new ArtifactDataSource(Options.Create(options), logger);
|
||||
});
|
||||
|
||||
services.AddSingleton<ArtifactDataSource>();
|
||||
// Register core services
|
||||
services.AddSingleton<IParsedSbomParser, ParsedSbomParser>();
|
||||
services.AddSingleton<ICycloneDxExtractor, CycloneDxExtractor>();
|
||||
|
||||
// Register index repository
|
||||
services.AddScoped<IArtifactIndexRepository>(sp =>
|
||||
{
|
||||
var dataSource = sp.GetRequiredService<ArtifactDataSource>();
|
||||
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<PostgresArtifactIndexRepository>>();
|
||||
// TODO: Get tenant ID from context
|
||||
return new PostgresArtifactIndexRepository(dataSource, logger, "default");
|
||||
});
|
||||
|
||||
services.AddScoped<IArtifactIndexRepository, PostgresArtifactIndexRepository>();
|
||||
// Register S3 artifact store
|
||||
services.AddScoped<IArtifactStore, S3UnifiedArtifactStore>();
|
||||
|
||||
@@ -70,6 +63,10 @@ public static class ServiceCollectionExtensions
|
||||
/// <returns>Service collection for chaining.</returns>
|
||||
public static IServiceCollection AddInMemoryArtifactStore(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddSingleton(TimeProvider.System);
|
||||
services.TryAddSingleton<IGuidProvider, SystemGuidProvider>();
|
||||
services.TryAddScoped<IArtifactTenantContext, ArtifactTenantContext>();
|
||||
services.AddSingleton<IParsedSbomParser, ParsedSbomParser>();
|
||||
services.AddSingleton<ICycloneDxExtractor, CycloneDxExtractor>();
|
||||
services.AddSingleton<IArtifactIndexRepository, InMemoryArtifactIndexRepository>();
|
||||
services.AddSingleton<IArtifactStore, InMemoryArtifactStore>();
|
||||
@@ -90,113 +87,11 @@ public static class ServiceCollectionExtensions
|
||||
var options = new ArtifactMigrationOptions();
|
||||
configure?.Invoke(options);
|
||||
services.AddSingleton(options);
|
||||
services.TryAddSingleton(TimeProvider.System);
|
||||
services.TryAddSingleton<IGuidProvider, SystemGuidProvider>();
|
||||
|
||||
services.AddScoped<ArtifactMigrationService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In-memory artifact store for testing.
|
||||
/// </summary>
|
||||
public sealed class InMemoryArtifactStore : IArtifactStore
|
||||
{
|
||||
private readonly Dictionary<string, (byte[] Content, ArtifactMetadata Metadata)> _artifacts = new();
|
||||
private readonly object _lock = new();
|
||||
|
||||
public Task<ArtifactStoreResult> StoreAsync(ArtifactStoreRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var key = $"{request.BomRef}/{request.SerialNumber}/{request.ArtifactId}";
|
||||
using var ms = new MemoryStream();
|
||||
request.Content.CopyTo(ms);
|
||||
var content = ms.ToArray();
|
||||
|
||||
using var sha = System.Security.Cryptography.SHA256.Create();
|
||||
var hash = sha.ComputeHash(content);
|
||||
var sha256 = Convert.ToHexStringLower(hash);
|
||||
|
||||
var metadata = new ArtifactMetadata
|
||||
{
|
||||
StorageKey = key,
|
||||
BomRef = request.BomRef,
|
||||
SerialNumber = request.SerialNumber,
|
||||
ArtifactId = request.ArtifactId,
|
||||
ContentType = request.ContentType,
|
||||
SizeBytes = content.Length,
|
||||
Sha256 = sha256,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
Type = request.Type,
|
||||
TenantId = request.TenantId
|
||||
};
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var wasCreated = !_artifacts.ContainsKey(key);
|
||||
_artifacts[key] = (content, metadata);
|
||||
return Task.FromResult(ArtifactStoreResult.Succeeded(key, sha256, content.Length, wasCreated));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<ArtifactReadResult> ReadAsync(string bomRef, string? serialNumber, string? artifactId, CancellationToken ct = default)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var matching = _artifacts
|
||||
.Where(kvp => kvp.Value.Metadata.BomRef == bomRef)
|
||||
.Where(kvp => serialNumber == null || kvp.Value.Metadata.SerialNumber == serialNumber)
|
||||
.Where(kvp => artifactId == null || kvp.Value.Metadata.ArtifactId == artifactId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (matching.Value.Content == null)
|
||||
{
|
||||
return Task.FromResult(ArtifactReadResult.NotFound());
|
||||
}
|
||||
|
||||
return Task.FromResult(ArtifactReadResult.Succeeded(
|
||||
new MemoryStream(matching.Value.Content),
|
||||
matching.Value.Metadata));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<ArtifactMetadata>> ListAsync(string bomRef, string? serialNumber = null, CancellationToken ct = default)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var result = _artifacts.Values
|
||||
.Where(x => x.Metadata.BomRef == bomRef)
|
||||
.Where(x => serialNumber == null || x.Metadata.SerialNumber == serialNumber)
|
||||
.Select(x => x.Metadata)
|
||||
.ToList();
|
||||
|
||||
return Task.FromResult<IReadOnlyList<ArtifactMetadata>>(result);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<bool> ExistsAsync(string bomRef, string serialNumber, string artifactId, CancellationToken ct = default)
|
||||
{
|
||||
var key = $"{bomRef}/{serialNumber}/{artifactId}";
|
||||
lock (_lock)
|
||||
{
|
||||
return Task.FromResult(_artifacts.ContainsKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<ArtifactMetadata?> GetMetadataAsync(string bomRef, string serialNumber, string artifactId, CancellationToken ct = default)
|
||||
{
|
||||
var key = $"{bomRef}/{serialNumber}/{artifactId}";
|
||||
lock (_lock)
|
||||
{
|
||||
return Task.FromResult(_artifacts.TryGetValue(key, out var entry) ? entry.Metadata : null);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<bool> DeleteAsync(string bomRef, string serialNumber, string artifactId, CancellationToken ct = default)
|
||||
{
|
||||
var key = $"{bomRef}/{serialNumber}/{artifactId}";
|
||||
lock (_lock)
|
||||
{
|
||||
return Task.FromResult(_artifacts.Remove(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user