Code/SboxSuperuser/Api/AuditQuery.cs

Immutable value object that represents bounded filters for querying an audit/history store. Validates inputs like page size, UTC bounds, request ID, command and Steam ID, and exposes typed filter properties.

Credential Access
using SboxSuperuser.Core.Contracts;
using SboxSuperuser.Core.Identity;

namespace SboxSuperuser;

/// <summary>Immutable bounded audit filters; the host applies authorization independently.</summary>
public sealed class AuditQuery
{
	/// <summary>Gets the maximum returned rows, from 1 through 100.</summary>
	public int PageSize { get; }

	/// <summary>Gets the prior response cursor, or null for the newest page.</summary>
	public AuditCursor Cursor { get; }

	/// <summary>Gets an optional exact request ID filter.</summary>
	public Guid? RequestId { get; }

	/// <summary>Gets an optional exact command key filter.</summary>
	public string Command { get; }

	/// <summary>Gets an optional exact actor subject filter.</summary>
	public string ActorSubject { get; }

	/// <summary>Gets an optional outcome filter.</summary>
	public CommandStatus? Outcome { get; }

	/// <summary>Gets an optional authenticated source filter.</summary>
	public CommandOrigin? Origin { get; }

	/// <summary>Gets an inclusive UTC creation lower bound.</summary>
	public DateTimeOffset? FromUtc { get; }

	/// <summary>Gets an inclusive UTC creation upper bound.</summary>
	public DateTimeOffset? ToUtc { get; }

	/// <summary>Gets an optional exact affected player ID filter.</summary>
	public string TargetSteamId { get; }

	/// <summary>Creates validated filters without reading or authorizing access to the audit store.</summary>
	public AuditQuery( int pageSize = 25, AuditCursor cursor = null, Guid? requestId = null, string command = null,
		string actorSubject = null, CommandStatus? outcome = null, CommandOrigin? origin = null,
		DateTimeOffset? fromUtc = null, DateTimeOffset? toUtc = null, string targetSteamId = null )
	{
		if ( pageSize is < 1 or > 100 || requestId == Guid.Empty || fromUtc > toUtc
			|| fromUtc.HasValue && fromUtc.Value.Offset != TimeSpan.Zero || toUtc.HasValue && toUtc.Value.Offset != TimeSpan.Zero )
		{
			throw new ArgumentException( "Audit pagination, request ID or UTC range is invalid." );
		}

		if ( command is not null )
		{
			ContractGuard.Key( command, nameof( command ) );
		}

		if ( command?.Length > 96 || actorSubject is not null && (string.IsNullOrWhiteSpace( actorSubject ) || actorSubject.Length > 96) )
		{
			throw new ArgumentException( "Audit filters exceed their limits." );
		}

		if ( targetSteamId is not null )
		{
			SteamIdentity.Validate( targetSteamId, nameof( targetSteamId ) );
		}

		if ( outcome.HasValue )
		{
			ContractGuard.EnumValue( outcome.Value, nameof( outcome ) );
		}
		if ( origin.HasValue )
		{
			ContractGuard.EnumValue( origin.Value, nameof( origin ) );
		}
		PageSize = pageSize;
		Cursor = cursor;
		RequestId = requestId;
		Command = command;
		ActorSubject = actorSubject;
		Outcome = outcome;
		Origin = origin;
		FromUtc = fromUtc;
		ToUtc = toUtc;
		TargetSteamId = targetSteamId;
	}
}