feat: add Reachability Center and Why Drawer components with tests
- Implemented ReachabilityCenterComponent for displaying asset reachability status with summary and filtering options. - Added ReachabilityWhyDrawerComponent to show detailed reachability evidence and call paths. - Created unit tests for both components to ensure functionality and correctness. - Updated accessibility test results for the new components.
This commit is contained in:
@@ -3,7 +3,7 @@ using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Conversion;
|
||||
using MongoContracts = StellaOps.Concelier.Storage.Mongo.Advisories;
|
||||
using MongoContracts = StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.IO;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Bson.IO;
|
||||
using Contracts = StellaOps.Concelier.Storage.Contracts;
|
||||
using MongoContracts = StellaOps.Concelier.Storage.Mongo;
|
||||
using MongoContracts = StellaOps.Concelier.Storage;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using StellaOps.Concelier.Storage.Mongo;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using Contracts = StellaOps.Concelier.Storage.Contracts;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
@@ -35,9 +35,7 @@ public sealed class PostgresDocumentStore : IDocumentStore, Contracts.IStorageDo
|
||||
|
||||
public async Task<DocumentRecord> UpsertAsync(DocumentRecord record, CancellationToken cancellationToken)
|
||||
{
|
||||
// Ensure source exists
|
||||
var source = await _sourceRepository.GetByKeyAsync(record.SourceName, cancellationToken).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException($"Source '{record.SourceName}' not provisioned.");
|
||||
var source = await EnsureSourceAsync(record.SourceName, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var entity = new DocumentRecordEntity(
|
||||
Id: record.Id == Guid.Empty ? Guid.NewGuid() : record.Id,
|
||||
@@ -99,4 +97,29 @@ public sealed class PostgresDocumentStore : IDocumentStore, Contracts.IStorageDo
|
||||
ExpiresAt: row.ExpiresAt,
|
||||
Payload: row.Payload);
|
||||
}
|
||||
|
||||
private async Task<SourceEntity> EnsureSourceAsync(string sourceName, CancellationToken cancellationToken)
|
||||
{
|
||||
var existing = await _sourceRepository.GetByKeyAsync(sourceName, cancellationToken).ConfigureAwait(false);
|
||||
if (existing is not null)
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
return await _sourceRepository.UpsertAsync(new SourceEntity
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Key = sourceName,
|
||||
Name = sourceName,
|
||||
SourceType = sourceName,
|
||||
Url = null,
|
||||
Priority = 0,
|
||||
Enabled = true,
|
||||
Config = "{}",
|
||||
Metadata = "{}",
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now,
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using Dapper;
|
||||
using StellaOps.Concelier.Storage.Mongo.ChangeHistory;
|
||||
using StellaOps.Concelier.Storage.ChangeHistory;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Dapper;
|
||||
using StellaOps.Concelier.Storage.Mongo;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using Contracts = StellaOps.Concelier.Storage.Contracts;
|
||||
using StellaOps.Concelier.Storage.Postgres;
|
||||
|
||||
@@ -83,7 +83,7 @@ internal sealed class PostgresDtoStore : IDtoStore, Contracts.IStorageDtoStore
|
||||
|
||||
private DtoRecord ToRecord(DtoRow row)
|
||||
{
|
||||
var payload = MongoDB.Bson.BsonDocument.Parse(row.PayloadJson);
|
||||
var payload = StellaOps.Concelier.Bson.BsonDocument.Parse(row.PayloadJson);
|
||||
return new DtoRecord(
|
||||
row.Id,
|
||||
row.DocumentId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using Dapper;
|
||||
using StellaOps.Concelier.Storage.Mongo.Exporting;
|
||||
using StellaOps.Concelier.Storage.Exporting;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Dapper;
|
||||
using StellaOps.Concelier.Storage.Mongo.JpFlags;
|
||||
using StellaOps.Concelier.Storage.JpFlags;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Dapper;
|
||||
using StellaOps.Concelier.Storage.Mongo.PsirtFlags;
|
||||
using StellaOps.Concelier.Storage.PsirtFlags;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ using StellaOps.Concelier.Storage.Postgres.Advisories;
|
||||
using StellaOps.Infrastructure.Postgres;
|
||||
using StellaOps.Infrastructure.Postgres.Options;
|
||||
using StellaOps.Concelier.Core.Linksets;
|
||||
using MongoContracts = StellaOps.Concelier.Storage.Mongo;
|
||||
using MongoAdvisories = StellaOps.Concelier.Storage.Mongo.Advisories;
|
||||
using MongoExporting = StellaOps.Concelier.Storage.Mongo.Exporting;
|
||||
using MongoJpFlags = StellaOps.Concelier.Storage.Mongo.JpFlags;
|
||||
using MongoPsirt = StellaOps.Concelier.Storage.Mongo.PsirtFlags;
|
||||
using MongoHistory = StellaOps.Concelier.Storage.Mongo.ChangeHistory;
|
||||
using MongoContracts = StellaOps.Concelier.Storage;
|
||||
using MongoAdvisories = StellaOps.Concelier.Storage.Advisories;
|
||||
using MongoExporting = StellaOps.Concelier.Storage.Exporting;
|
||||
using MongoJpFlags = StellaOps.Concelier.Storage.JpFlags;
|
||||
using MongoPsirt = StellaOps.Concelier.Storage.PsirtFlags;
|
||||
using MongoHistory = StellaOps.Concelier.Storage.ChangeHistory;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres;
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<IAdvisoryCreditRepository, AdvisoryCreditRepository>();
|
||||
services.AddScoped<IAdvisoryWeaknessRepository, AdvisoryWeaknessRepository>();
|
||||
services.AddScoped<IKevFlagRepository, KevFlagRepository>();
|
||||
services.AddScoped<ISourceStateRepository, SourceStateRepository>();
|
||||
services.AddScoped<StellaOps.Concelier.Storage.Postgres.Repositories.ISourceStateRepository, SourceStateRepository>();
|
||||
services.AddScoped<MongoAdvisories.IAdvisoryStore, PostgresAdvisoryStore>();
|
||||
services.AddScoped<IDocumentRepository, DocumentRepository>();
|
||||
services.AddScoped<MongoContracts.ISourceStateRepository, PostgresSourceStateAdapter>();
|
||||
@@ -88,7 +88,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<IAdvisoryCreditRepository, AdvisoryCreditRepository>();
|
||||
services.AddScoped<IAdvisoryWeaknessRepository, AdvisoryWeaknessRepository>();
|
||||
services.AddScoped<IKevFlagRepository, KevFlagRepository>();
|
||||
services.AddScoped<ISourceStateRepository, SourceStateRepository>();
|
||||
services.AddScoped<StellaOps.Concelier.Storage.Postgres.Repositories.ISourceStateRepository, SourceStateRepository>();
|
||||
services.AddScoped<MongoAdvisories.IAdvisoryStore, PostgresAdvisoryStore>();
|
||||
services.AddScoped<IDocumentRepository, DocumentRepository>();
|
||||
services.AddScoped<MongoContracts.ISourceStateRepository, PostgresSourceStateAdapter>();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Collections.Generic;
|
||||
using MongoDB.Bson;
|
||||
using StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
using Contracts = StellaOps.Concelier.Storage.Contracts;
|
||||
using MongoContracts = StellaOps.Concelier.Storage.Mongo;
|
||||
using MongoContracts = StellaOps.Concelier.Storage;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres;
|
||||
|
||||
@@ -45,6 +45,7 @@ public sealed class PostgresSourceStateAdapter : MongoContracts.ISourceStateRepo
|
||||
}
|
||||
|
||||
var cursor = string.IsNullOrWhiteSpace(state.Cursor) ? null : BsonDocument.Parse(state.Cursor);
|
||||
var backoffUntil = TryParseBackoffUntil(state.Metadata);
|
||||
return new MongoContracts.SourceStateRecord(
|
||||
sourceName,
|
||||
Enabled: true,
|
||||
@@ -53,7 +54,7 @@ public sealed class PostgresSourceStateAdapter : MongoContracts.ISourceStateRepo
|
||||
LastSuccess: state.LastSuccessAt,
|
||||
LastFailure: state.LastError is null ? null : state.LastSyncAt,
|
||||
FailCount: state.ErrorCount,
|
||||
BackoffUntil: null,
|
||||
BackoffUntil: backoffUntil,
|
||||
UpdatedAt: state.UpdatedAt,
|
||||
LastFailureReason: state.LastError);
|
||||
}
|
||||
@@ -183,4 +184,32 @@ public sealed class PostgresSourceStateAdapter : MongoContracts.ISourceStateRepo
|
||||
return delta < TimeSpan.Zero ? DateTimeOffset.MinValue : DateTimeOffset.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static DateTimeOffset? TryParseBackoffUntil(string? metadata)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(metadata))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var document = JsonDocument.Parse(metadata);
|
||||
if (!document.RootElement.TryGetProperty("backoffUntil", out var backoffProperty))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (backoffProperty.ValueKind == JsonValueKind.String
|
||||
&& DateTimeOffset.TryParse(backoffProperty.GetString(), out var parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user