Initial commit (history squashed)
Some checks failed
Build Test Deploy / authority-container (push) Has been cancelled
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Build Test Deploy / build-test (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
2025-10-07 10:14:21 +03:00
commit b97fc7685a
1132 changed files with 117842 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Feedser.Source.CertCc.Configuration;
using StellaOps.Feedser.Source.CertCc.Internal;
using StellaOps.Feedser.Source.Common;
using StellaOps.Feedser.Source.Common.Fetch;
using StellaOps.Feedser.Source.Common.Cursors;
using StellaOps.Feedser.Storage.Mongo;
using StellaOps.Feedser.Storage.Mongo.Documents;
using StellaOps.Plugin;
namespace StellaOps.Feedser.Source.CertCc;
public sealed class CertCcConnector : IFeedConnector
{
private readonly CertCcSummaryPlanner _summaryPlanner;
private readonly SourceFetchService _fetchService;
private readonly IDocumentStore _documentStore;
private readonly ISourceStateRepository _stateRepository;
private readonly CertCcOptions _options;
private readonly TimeProvider _timeProvider;
private readonly ILogger<CertCcConnector> _logger;
public CertCcConnector(
CertCcSummaryPlanner summaryPlanner,
SourceFetchService fetchService,
IDocumentStore documentStore,
ISourceStateRepository stateRepository,
IOptions<CertCcOptions> options,
TimeProvider? timeProvider,
ILogger<CertCcConnector> logger)
{
_summaryPlanner = summaryPlanner ?? throw new ArgumentNullException(nameof(summaryPlanner));
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
_documentStore = documentStore ?? throw new ArgumentNullException(nameof(documentStore));
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
_options.Validate();
_timeProvider = timeProvider ?? TimeProvider.System;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public string SourceName => CertCcConnectorPlugin.SourceName;
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
{
var cursor = await GetCursorAsync(cancellationToken).ConfigureAwait(false);
var plan = _summaryPlanner.CreatePlan(cursor.SummaryState);
if (plan.Requests.Count == 0)
{
await UpdateCursorAsync(cursor.WithSummaryState(plan.NextState).WithLastRun(_timeProvider.GetUtcNow()), cancellationToken).ConfigureAwait(false);
return;
}
foreach (var request in plan.Requests)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var uri = request.Uri;
var existing = await _documentStore.FindBySourceAndUriAsync(SourceName, uri.ToString(), cancellationToken).ConfigureAwait(false);
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["certcc.scope"] = request.Scope.ToString().ToLowerInvariant(),
["certcc.year"] = request.Year.ToString("D4"),
};
if (request.Month.HasValue)
{
metadata["certcc.month"] = request.Month.Value.ToString("D2");
}
var fetchRequest = new SourceFetchRequest(CertCcOptions.HttpClientName, SourceName, uri)
{
Metadata = metadata,
AcceptHeaders = new[] { "application/json" },
ETag = existing?.Etag,
LastModified = existing?.LastModified,
};
var result = await _fetchService.FetchAsync(fetchRequest, cancellationToken).ConfigureAwait(false);
if (result.IsNotModified)
{
_logger.LogDebug("CERT/CC summary {Uri} returned 304 Not Modified", uri);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "CERT/CC summary fetch failed for {Uri}", request.Uri);
await _stateRepository.MarkFailureAsync(SourceName, _timeProvider.GetUtcNow(), TimeSpan.FromMinutes(5), ex.Message, cancellationToken).ConfigureAwait(false);
throw;
}
}
var updatedCursor = cursor
.WithSummaryState(plan.NextState)
.WithLastRun(_timeProvider.GetUtcNow());
await UpdateCursorAsync(updatedCursor, cancellationToken).ConfigureAwait(false);
}
public Task ParseAsync(IServiceProvider services, CancellationToken cancellationToken)
=> Task.CompletedTask;
public Task MapAsync(IServiceProvider services, CancellationToken cancellationToken)
=> Task.CompletedTask;
private async Task<CertCcCursor> GetCursorAsync(CancellationToken cancellationToken)
{
var record = await _stateRepository.TryGetAsync(SourceName, cancellationToken).ConfigureAwait(false);
return CertCcCursor.FromBson(record?.Cursor);
}
private async Task UpdateCursorAsync(CertCcCursor cursor, CancellationToken cancellationToken)
{
var document = cursor.ToBsonDocument();
await _stateRepository.UpdateCursorAsync(SourceName, document, _timeProvider.GetUtcNow(), cancellationToken).ConfigureAwait(false);
}
}