refactor(audit): consolidate query-param parsers; widen CLI export to multi-value

This commit is contained in:
Joseph Doherty
2026-05-21 05:37:06 -04:00
parent 2a76be1f94
commit f64a7aed02
9 changed files with 350 additions and 177 deletions
@@ -82,12 +82,18 @@ public static class AuditExportEndpoints
/// <c>AuditLogPage.ApplyQueryStringFilters</c>) — an unparseable value within
/// a repeated set is dropped, not the whole set.
/// </summary>
/// <remarks>
/// This endpoint reads the source-site filter from the <c>site</c> query key,
/// whereas the ManagementService export endpoint reads it as
/// <c>sourceSiteId</c>. The divergence is deliberate — each endpoint matches
/// its own CLI / UI URL builder — so do NOT "fix" the two to one key name.
/// </remarks>
internal static AuditLogQueryFilter ParseFilter(IQueryCollection query)
{
var channels = ParseEnumList<AuditChannel>(query, "channel");
var kinds = ParseEnumList<AuditKind>(query, "kind");
var statuses = ParseEnumList<AuditStatus>(query, "status");
var sites = ParseStringList(query, "site");
var channels = AuditQueryParamParsers.ParseEnumList<AuditChannel>(query["channel"]);
var kinds = AuditQueryParamParsers.ParseEnumList<AuditKind>(query["kind"]);
var statuses = AuditQueryParamParsers.ParseEnumList<AuditStatus>(query["status"]);
var sites = AuditQueryParamParsers.ParseStringList(query["site"]);
string? target = TrimToNullable(query, "target");
string? actor = TrimToNullable(query, "actor");
@@ -114,53 +120,6 @@ public static class AuditExportEndpoints
ToUtc: toUtc);
}
/// <summary>
/// Reads EVERY value of a (possibly repeated) query param and parses each as
/// <typeparamref name="TEnum"/>, dropping unparseable values silently. Returns
/// <c>null</c> when the param is absent or no value parsed.
/// </summary>
private static IReadOnlyList<TEnum>? ParseEnumList<TEnum>(IQueryCollection query, string key)
where TEnum : struct, Enum
{
if (!query.TryGetValue(key, out var values))
{
return null;
}
var parsed = new List<TEnum>();
foreach (var raw in values)
{
if (Enum.TryParse<TEnum>(raw, ignoreCase: true, out var value))
{
parsed.Add(value);
}
}
return parsed.Count > 0 ? parsed : null;
}
/// <summary>
/// Reads EVERY value of a (possibly repeated) query param, trims each, and
/// drops blank entries. Returns <c>null</c> when the param is absent or every
/// value was blank.
/// </summary>
private static IReadOnlyList<string>? ParseStringList(IQueryCollection query, string key)
{
if (!query.TryGetValue(key, out var values))
{
return null;
}
var parsed = new List<string>();
foreach (var raw in values)
{
if (!string.IsNullOrWhiteSpace(raw))
{
parsed.Add(raw.Trim());
}
}
return parsed.Count > 0 ? parsed : null;
}
/// <summary>
/// Optional <c>maxRows=</c> query-string override. Falls back to
/// <see cref="DefaultMaxRows"/> on a missing / non-positive / unparseable