- Introduced Storybook configuration files (`main.ts`, `preview.ts`, `tsconfig.json`) for Angular components. - Created motion tokens in `motion-tokens.ts` to define durations, easing functions, and transforms. - Developed a Storybook story for motion tokens showcasing their usage and reduced motion fallback. - Added SCSS variables for motion durations, easing, and transforms in `_motion.scss`. - Implemented accessibility smoke tests using Playwright and Axe for automated accessibility checks. - Created portable and sealed bundle structures with corresponding JSON files for evidence locker. - Added shell script for verifying notify kit determinism.
80 lines
3.5 KiB
C#
80 lines
3.5 KiB
C#
using System.Collections.Immutable;
|
|
using System.Text.Json.Nodes;
|
|
using MongoDB.Bson;
|
|
using MongoDB.Driver;
|
|
using StellaOps.Graph.Indexer.Infrastructure;
|
|
|
|
namespace StellaOps.Graph.Indexer.Analytics;
|
|
|
|
public sealed class MongoGraphSnapshotProvider : IGraphSnapshotProvider
|
|
{
|
|
private readonly IMongoCollection<BsonDocument> _snapshots;
|
|
private readonly IMongoCollection<BsonDocument> _progress;
|
|
private readonly MongoGraphSnapshotProviderOptions _options;
|
|
|
|
public MongoGraphSnapshotProvider(IMongoDatabase database, MongoGraphSnapshotProviderOptions? options = null)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(database);
|
|
_options = options ?? new MongoGraphSnapshotProviderOptions();
|
|
_snapshots = database.GetCollection<BsonDocument>(_options.SnapshotCollectionName);
|
|
_progress = database.GetCollection<BsonDocument>(_options.ProgressCollectionName);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<GraphAnalyticsSnapshot>> GetPendingSnapshotsAsync(CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
var processedIds = await _progress
|
|
.Find(FilterDefinition<BsonDocument>.Empty)
|
|
.Project(doc => doc["snapshot_id"].AsString)
|
|
.ToListAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
var filter = Builders<BsonDocument>.Filter.Nin("snapshot_id", processedIds);
|
|
var snapshots = await _snapshots
|
|
.Find(filter)
|
|
.Limit(_options.MaxBatch)
|
|
.Sort(Builders<BsonDocument>.Sort.Descending("generated_at"))
|
|
.ToListAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
var result = new List<GraphAnalyticsSnapshot>(snapshots.Count);
|
|
foreach (var snapshot in snapshots)
|
|
{
|
|
var tenant = snapshot.GetValue("tenant", string.Empty).AsString;
|
|
var snapshotId = snapshot.GetValue("snapshot_id", string.Empty).AsString;
|
|
var generatedAt = snapshot.TryGetValue("generated_at", out var generated)
|
|
&& generated.BsonType == BsonType.DateTime
|
|
? DateTime.SpecifyKind(generated.ToUniversalTime(), DateTimeKind.Utc)
|
|
: DateTimeOffset.UtcNow;
|
|
|
|
var nodes = snapshot.TryGetValue("nodes", out var nodesValue) && nodesValue is BsonArray nodesArray
|
|
? BsonJsonConverter.ToJsonArray(nodesArray).Select(n => (JsonObject)n!).ToImmutableArray()
|
|
: ImmutableArray<JsonObject>.Empty;
|
|
|
|
var edges = snapshot.TryGetValue("edges", out var edgesValue) && edgesValue is BsonArray edgesArray
|
|
? BsonJsonConverter.ToJsonArray(edgesArray).Select(n => (JsonObject)n!).ToImmutableArray()
|
|
: ImmutableArray<JsonObject>.Empty;
|
|
|
|
result.Add(new GraphAnalyticsSnapshot(tenant, snapshotId, generatedAt, nodes, edges));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task MarkProcessedAsync(string tenant, string snapshotId, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
var filter = Builders<BsonDocument>.Filter.Eq("snapshot_id", snapshotId)
|
|
& Builders<BsonDocument>.Filter.Eq("tenant", tenant);
|
|
|
|
var update = Builders<BsonDocument>.Update.Set("snapshot_id", snapshotId)
|
|
.Set("tenant", tenant)
|
|
.SetOnInsert("processed_at", DateTimeOffset.UtcNow.UtcDateTime);
|
|
|
|
await _progress.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
}
|
|
}
|