Restructure solution layout by module
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
using System.Net;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.CertBund.Configuration;
|
||||
|
||||
public sealed class CertBundOptions
|
||||
{
|
||||
public const string HttpClientName = "concelier.source.certbund";
|
||||
|
||||
/// <summary>
|
||||
/// RSS feed providing the latest CERT-Bund advisories.
|
||||
/// </summary>
|
||||
public Uri FeedUri { get; set; } = new("https://wid.cert-bund.de/content/public/securityAdvisory/rss");
|
||||
|
||||
/// <summary>
|
||||
/// Portal endpoint used to bootstrap session cookies (required for the SPA JSON API).
|
||||
/// </summary>
|
||||
public Uri PortalBootstrapUri { get; set; } = new("https://wid.cert-bund.de/portal/");
|
||||
|
||||
/// <summary>
|
||||
/// Detail API endpoint template; advisory identifier is appended as the <c>name</c> query parameter.
|
||||
/// </summary>
|
||||
public Uri DetailApiUri { get; set; } = new("https://wid.cert-bund.de/portal/api/securityadvisory");
|
||||
|
||||
/// <summary>
|
||||
/// Optional timeout override for feed/detail requests.
|
||||
/// </summary>
|
||||
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// Delay applied between successive detail fetches to respect upstream politeness.
|
||||
/// </summary>
|
||||
public TimeSpan RequestDelay { get; set; } = TimeSpan.FromMilliseconds(250);
|
||||
|
||||
/// <summary>
|
||||
/// Backoff recorded in source state when a fetch attempt fails.
|
||||
/// </summary>
|
||||
public TimeSpan FailureBackoff { get; set; } = TimeSpan.FromMinutes(5);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of advisories to enqueue per fetch iteration.
|
||||
/// </summary>
|
||||
public int MaxAdvisoriesPerFetch { get; set; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of advisory identifiers remembered to prevent re-processing.
|
||||
/// </summary>
|
||||
public int MaxKnownAdvisories { get; set; } = 512;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (FeedUri is null || !FeedUri.IsAbsoluteUri)
|
||||
{
|
||||
throw new InvalidOperationException("CERT-Bund feed URI must be an absolute URI.");
|
||||
}
|
||||
|
||||
if (PortalBootstrapUri is null || !PortalBootstrapUri.IsAbsoluteUri)
|
||||
{
|
||||
throw new InvalidOperationException("CERT-Bund portal bootstrap URI must be an absolute URI.");
|
||||
}
|
||||
|
||||
if (DetailApiUri is null || !DetailApiUri.IsAbsoluteUri)
|
||||
{
|
||||
throw new InvalidOperationException("CERT-Bund detail API URI must be an absolute URI.");
|
||||
}
|
||||
|
||||
if (RequestTimeout <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(RequestTimeout)} must be positive.");
|
||||
}
|
||||
|
||||
if (RequestDelay < TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(RequestDelay)} cannot be negative.");
|
||||
}
|
||||
|
||||
if (FailureBackoff <= TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(FailureBackoff)} must be positive.");
|
||||
}
|
||||
|
||||
if (MaxAdvisoriesPerFetch <= 0)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(MaxAdvisoriesPerFetch)} must be greater than zero.");
|
||||
}
|
||||
|
||||
if (MaxKnownAdvisories <= 0)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(MaxKnownAdvisories)} must be greater than zero.");
|
||||
}
|
||||
}
|
||||
|
||||
public Uri BuildDetailUri(string advisoryId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(advisoryId))
|
||||
{
|
||||
throw new ArgumentException("Advisory identifier must be provided.", nameof(advisoryId));
|
||||
}
|
||||
|
||||
var builder = new UriBuilder(DetailApiUri);
|
||||
var queryPrefix = string.IsNullOrEmpty(builder.Query) ? string.Empty : builder.Query.TrimStart('?') + "&";
|
||||
builder.Query = $"{queryPrefix}name={Uri.EscapeDataString(advisoryId)}";
|
||||
return builder.Uri;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user