Add MergeUsageAnalyzer to detect legacy merge service usage
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented MergeUsageAnalyzer to flag usage of AdvisoryMergeService and AddMergeModule. - Created AnalyzerReleases.Shipped.md and AnalyzerReleases.Unshipped.md for release documentation. - Added tests for MergeUsageAnalyzer to ensure correct diagnostics for various scenarios. - Updated project files for analyzers and tests to include necessary dependencies and configurations. - Introduced a sample report structure for scanner output.
This commit is contained in:
@@ -61,6 +61,11 @@ public sealed class ScannerWebServiceOptions
|
||||
/// </summary>
|
||||
public ApiOptions Api { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Console (UI) routing settings used for orchestrator link generation.
|
||||
/// </summary>
|
||||
public ConsoleOptions Console { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Platform event emission settings.
|
||||
/// </summary>
|
||||
@@ -266,6 +271,17 @@ public sealed class ScannerWebServiceOptions
|
||||
public string RuntimeSegment { get; set; } = "runtime";
|
||||
}
|
||||
|
||||
public sealed class ConsoleOptions
|
||||
{
|
||||
public string BasePath { get; set; } = "/ui";
|
||||
|
||||
public string ReportsSegment { get; set; } = "reports";
|
||||
|
||||
public string PolicySegment { get; set; } = "policy";
|
||||
|
||||
public string AttestationsSegment { get; set; } = "attestations";
|
||||
}
|
||||
|
||||
public sealed class EventsOptions
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
@@ -16,20 +16,24 @@ namespace StellaOps.Scanner.WebService.Services;
|
||||
|
||||
internal sealed class ReportEventDispatcher : IReportEventDispatcher
|
||||
{
|
||||
private const string DefaultTenant = "default";
|
||||
private const string Source = "scanner.webservice";
|
||||
|
||||
private readonly IPlatformEventPublisher _publisher;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly ILogger<ReportEventDispatcher> _logger;
|
||||
private readonly string[] _apiBaseSegments;
|
||||
private readonly string _reportsSegment;
|
||||
private readonly string _policySegment;
|
||||
|
||||
public ReportEventDispatcher(
|
||||
IPlatformEventPublisher publisher,
|
||||
IOptions<ScannerWebServiceOptions> options,
|
||||
TimeProvider timeProvider,
|
||||
private const string DefaultTenant = "default";
|
||||
private const string Source = "scanner.webservice";
|
||||
|
||||
private readonly IPlatformEventPublisher _publisher;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly ILogger<ReportEventDispatcher> _logger;
|
||||
private readonly string[] _apiBaseSegments;
|
||||
private readonly string _reportsSegment;
|
||||
private readonly string _policySegment;
|
||||
private readonly string[] _consoleBaseSegments;
|
||||
private readonly string _consoleReportsSegment;
|
||||
private readonly string _consolePolicySegment;
|
||||
private readonly string _consoleAttestationsSegment;
|
||||
|
||||
public ReportEventDispatcher(
|
||||
IPlatformEventPublisher publisher,
|
||||
IOptions<ScannerWebServiceOptions> options,
|
||||
TimeProvider timeProvider,
|
||||
ILogger<ReportEventDispatcher> logger)
|
||||
{
|
||||
_publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
|
||||
@@ -38,17 +42,28 @@ internal sealed class ReportEventDispatcher : IReportEventDispatcher
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
var apiOptions = options.Value.Api ?? new ScannerWebServiceOptions.ApiOptions();
|
||||
_apiBaseSegments = SplitSegments(apiOptions.BasePath);
|
||||
_reportsSegment = string.IsNullOrWhiteSpace(apiOptions.ReportsSegment)
|
||||
? "reports"
|
||||
: apiOptions.ReportsSegment.Trim('/');
|
||||
_policySegment = string.IsNullOrWhiteSpace(apiOptions.PolicySegment)
|
||||
? "policy"
|
||||
: apiOptions.PolicySegment.Trim('/');
|
||||
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
var apiOptions = options.Value.Api ?? new ScannerWebServiceOptions.ApiOptions();
|
||||
_apiBaseSegments = SplitSegments(apiOptions.BasePath);
|
||||
_reportsSegment = string.IsNullOrWhiteSpace(apiOptions.ReportsSegment)
|
||||
? "reports"
|
||||
: apiOptions.ReportsSegment.Trim('/');
|
||||
_policySegment = string.IsNullOrWhiteSpace(apiOptions.PolicySegment)
|
||||
? "policy"
|
||||
: apiOptions.PolicySegment.Trim('/');
|
||||
var consoleOptions = options.Value.Console ?? new ScannerWebServiceOptions.ConsoleOptions();
|
||||
_consoleBaseSegments = SplitSegments(consoleOptions.BasePath);
|
||||
_consoleReportsSegment = string.IsNullOrWhiteSpace(consoleOptions.ReportsSegment)
|
||||
? "reports"
|
||||
: consoleOptions.ReportsSegment.Trim('/');
|
||||
_consolePolicySegment = string.IsNullOrWhiteSpace(consoleOptions.PolicySegment)
|
||||
? "policy"
|
||||
: consoleOptions.PolicySegment.Trim('/');
|
||||
_consoleAttestationsSegment = string.IsNullOrWhiteSpace(consoleOptions.AttestationsSegment)
|
||||
? "attestations"
|
||||
: consoleOptions.AttestationsSegment.Trim('/');
|
||||
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task PublishAsync(
|
||||
ReportRequestDto request,
|
||||
@@ -240,21 +255,21 @@ internal sealed class ReportEventDispatcher : IReportEventDispatcher
|
||||
};
|
||||
}
|
||||
|
||||
private ReportLinksPayload BuildLinks(HttpContext context, ReportDocumentDto document, DsseEnvelopeDto? envelope)
|
||||
{
|
||||
if (!context.Request.Host.HasValue)
|
||||
{
|
||||
return new ReportLinksPayload();
|
||||
}
|
||||
|
||||
var reportUi = BuildAbsoluteUri(context, "ui", "reports", document.ReportId);
|
||||
private ReportLinksPayload BuildLinks(HttpContext context, ReportDocumentDto document, DsseEnvelopeDto? envelope)
|
||||
{
|
||||
if (!context.Request.Host.HasValue)
|
||||
{
|
||||
return new ReportLinksPayload();
|
||||
}
|
||||
|
||||
var reportUi = BuildAbsoluteUri(context, ConcatSegments(_consoleBaseSegments, _consoleReportsSegment, document.ReportId));
|
||||
var reportApi = BuildAbsoluteUri(context, ConcatSegments(_apiBaseSegments, _reportsSegment, document.ReportId));
|
||||
|
||||
LinkTarget? policyLink = null;
|
||||
if (!string.IsNullOrWhiteSpace(document.Policy.RevisionId))
|
||||
{
|
||||
var policyRevision = document.Policy.RevisionId!;
|
||||
var policyUi = BuildAbsoluteUri(context, "ui", "policy", "revisions", policyRevision);
|
||||
var policyUi = BuildAbsoluteUri(context, ConcatSegments(_consoleBaseSegments, _consolePolicySegment, "revisions", policyRevision));
|
||||
var policyApi = BuildAbsoluteUri(context, ConcatSegments(_apiBaseSegments, _policySegment, "revisions", policyRevision));
|
||||
policyLink = LinkTarget.Create(policyUi, policyApi);
|
||||
}
|
||||
@@ -262,7 +277,7 @@ internal sealed class ReportEventDispatcher : IReportEventDispatcher
|
||||
LinkTarget? attestationLink = null;
|
||||
if (envelope is not null)
|
||||
{
|
||||
var attestationUi = BuildAbsoluteUri(context, "ui", "attestations", document.ReportId);
|
||||
var attestationUi = BuildAbsoluteUri(context, ConcatSegments(_consoleBaseSegments, _consoleAttestationsSegment, document.ReportId));
|
||||
var attestationApi = BuildAbsoluteUri(context, ConcatSegments(_apiBaseSegments, _reportsSegment, document.ReportId, "attestation"));
|
||||
attestationLink = LinkTarget.Create(attestationUi, attestationApi);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
> 2025-11-05 19:18Z: Added configurator to project wiring and unit test ensuring Surface.Env cache root is honoured.
|
||||
| SCANNER-SECRETS-02 | DOING (2025-11-02) | Scanner WebService Guild, Security Guild | SURFACE-SECRETS-02 | Replace ad-hoc secret wiring with Surface.Secrets for report/export operations (registry and CAS tokens).<br>2025-11-02: Export/report flows now depend on Surface.Secrets stub; integration tests in progress. | Secrets fetched through shared provider; unit/integration tests cover rotation + failure cases. |
|
||||
| SCANNER-EVENTS-16-301 | BLOCKED (2025-10-26) | Scanner WebService Guild | ORCH-SVC-38-101, NOTIFY-SVC-38-001 | Emit orchestrator-compatible envelopes (`scanner.event.*`) and update integration tests to verify Notifier ingestion (no Redis queue coupling). | Tests assert envelope schema + orchestrator publish; Notifier consumer harness passes; docs updated with new event contract. Blocked by .NET 10 preview OpenAPI/Auth dependency drift preventing `dotnet test` completion. |
|
||||
| SCANNER-EVENTS-16-302 | DOING (2025-10-26) | Scanner WebService Guild | SCANNER-EVENTS-16-301 | Extend orchestrator event links (report/policy/attestation) once endpoints are finalised across gateway + console. | Links section covers UI/API targets; downstream consumers validated; docs/samples updated. |
|
||||
| SCANNER-EVENTS-16-302 | DONE (2025-11-06) | Scanner WebService Guild | SCANNER-EVENTS-16-301 | Extend orchestrator event links (report/policy/attestation) once endpoints are finalised across gateway + console.<br>2025-11-06 22:55Z: Dispatcher now honours configurable API/console base segments, JSON samples/docs refreshed, and `ReportEventDispatcherTests` extended. Tests: `StellaOps.Scanner.WebService.Tests` build until pre-existing `SurfaceCacheOptionsConfiguratorTests` ctor signature drift (tracked separately). | Links section covers UI/API targets; downstream consumers validated; docs/samples updated. |
|
||||
|
||||
## Graph Explorer v1 (Sprint 21)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user