106 lines
3.5 KiB
C#
106 lines
3.5 KiB
C#
using StellaOps.Findings.Ledger.WebService.Contracts;
|
|
|
|
namespace StellaOps.Findings.Ledger.WebService.Services;
|
|
|
|
public interface IFindingSummaryService
|
|
{
|
|
Task<FindingSummary?> GetSummaryAsync(Guid findingId, CancellationToken ct);
|
|
Task<FindingSummaryPage> GetSummariesAsync(FindingSummaryFilter filter, CancellationToken ct);
|
|
}
|
|
|
|
public sealed class FindingSummaryService : IFindingSummaryService
|
|
{
|
|
private readonly IFindingSummaryBuilder _builder;
|
|
private readonly IFindingRepository _repository;
|
|
|
|
public FindingSummaryService(
|
|
IFindingSummaryBuilder builder,
|
|
IFindingRepository repository)
|
|
{
|
|
_builder = builder;
|
|
_repository = repository;
|
|
}
|
|
|
|
public async Task<FindingSummary?> GetSummaryAsync(Guid findingId, CancellationToken ct)
|
|
{
|
|
var finding = await _repository.GetByIdAsync(findingId, ct);
|
|
if (finding is null)
|
|
return null;
|
|
|
|
return _builder.Build(finding);
|
|
}
|
|
|
|
public async Task<FindingSummaryPage> GetSummariesAsync(FindingSummaryFilter filter, CancellationToken ct)
|
|
{
|
|
var (findings, totalCount) = await _repository.GetPagedAsync(
|
|
page: filter.Page,
|
|
pageSize: filter.PageSize,
|
|
status: filter.Status,
|
|
severity: filter.Severity,
|
|
minConfidence: filter.MinConfidence,
|
|
ct);
|
|
|
|
var summaries = findings.Select(f => _builder.Build(f)).ToList();
|
|
var sorted = ApplySort(summaries, filter.SortBy, filter.SortDirection);
|
|
|
|
return new FindingSummaryPage
|
|
{
|
|
Items = sorted,
|
|
TotalCount = totalCount,
|
|
Page = filter.Page,
|
|
PageSize = filter.PageSize
|
|
};
|
|
}
|
|
|
|
private static IReadOnlyList<FindingSummary> ApplySort(
|
|
List<FindingSummary> summaries,
|
|
string? sortBy,
|
|
string sortDirection)
|
|
{
|
|
if (string.IsNullOrEmpty(sortBy))
|
|
return summaries;
|
|
|
|
var descending = string.Equals(sortDirection, "desc", StringComparison.OrdinalIgnoreCase);
|
|
|
|
IEnumerable<FindingSummary> ordered = sortBy.ToLowerInvariant() switch
|
|
{
|
|
"cvss" => descending
|
|
? summaries.OrderByDescending(s => s.CvssScore ?? 0m)
|
|
: summaries.OrderBy(s => s.CvssScore ?? 0m),
|
|
"severity" => descending
|
|
? summaries.OrderByDescending(s => s.Severity)
|
|
: summaries.OrderBy(s => s.Severity),
|
|
"status" => descending
|
|
? summaries.OrderByDescending(s => s.Status)
|
|
: summaries.OrderBy(s => s.Status),
|
|
"component" => descending
|
|
? summaries.OrderByDescending(s => s.Component)
|
|
: summaries.OrderBy(s => s.Component),
|
|
"firstseen" => descending
|
|
? summaries.OrderByDescending(s => s.FirstSeen)
|
|
: summaries.OrderBy(s => s.FirstSeen),
|
|
"lastupdated" => descending
|
|
? summaries.OrderByDescending(s => s.LastUpdated)
|
|
: summaries.OrderBy(s => s.LastUpdated),
|
|
_ => summaries
|
|
};
|
|
|
|
return ordered.ToList();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Repository for finding data access.
|
|
/// </summary>
|
|
public interface IFindingRepository
|
|
{
|
|
Task<FindingData?> GetByIdAsync(Guid id, CancellationToken ct);
|
|
Task<(IReadOnlyList<FindingData> findings, int totalCount)> GetPagedAsync(
|
|
int page,
|
|
int pageSize,
|
|
string? status,
|
|
string? severity,
|
|
decimal? minConfidence,
|
|
CancellationToken ct);
|
|
}
|