using Grpc.Core; using MxGateway.Contracts.Proto; namespace MxGateway.Server.Grpc; public sealed class MxAccessGrpcRequestValidator { public void ValidateOpenSession(OpenSessionRequest request) { ArgumentNullException.ThrowIfNull(request); if (request.CommandTimeout is not null && request.CommandTimeout.ToTimeSpan() <= TimeSpan.Zero) { throw InvalidArgument("Command timeout must be greater than zero when provided."); } } public void ValidateCloseSession(CloseSessionRequest request) { ArgumentNullException.ThrowIfNull(request); RequireSessionId(request.SessionId); } public void ValidateStreamEvents(StreamEventsRequest request) { ArgumentNullException.ThrowIfNull(request); RequireSessionId(request.SessionId); } public void ValidateInvoke(MxCommandRequest request) { ArgumentNullException.ThrowIfNull(request); RequireSessionId(request.SessionId); if (request.Command is null) { throw InvalidArgument("Invoke requires a command payload."); } if (request.Command.Kind is MxCommandKind.Unspecified) { throw InvalidArgument("Invoke requires a command kind."); } ValidateCommandPayload(request.Command); } private static void RequireSessionId(string sessionId) { if (string.IsNullOrWhiteSpace(sessionId)) { throw InvalidArgument("Session id is required."); } } private static void ValidateCommandPayload(MxCommand command) { MxCommand.PayloadOneofCase expectedPayload = ExpectedPayload(command.Kind); if (command.PayloadCase != expectedPayload) { throw InvalidArgument( $"Command kind {command.Kind} requires payload {expectedPayload} but received {command.PayloadCase}."); } } private static MxCommand.PayloadOneofCase ExpectedPayload(MxCommandKind kind) { return kind switch { MxCommandKind.Register => MxCommand.PayloadOneofCase.Register, MxCommandKind.Unregister => MxCommand.PayloadOneofCase.Unregister, MxCommandKind.AddItem => MxCommand.PayloadOneofCase.AddItem, MxCommandKind.AddItem2 => MxCommand.PayloadOneofCase.AddItem2, MxCommandKind.RemoveItem => MxCommand.PayloadOneofCase.RemoveItem, MxCommandKind.Advise => MxCommand.PayloadOneofCase.Advise, MxCommandKind.UnAdvise => MxCommand.PayloadOneofCase.UnAdvise, MxCommandKind.AdviseSupervisory => MxCommand.PayloadOneofCase.AdviseSupervisory, MxCommandKind.AddBufferedItem => MxCommand.PayloadOneofCase.AddBufferedItem, MxCommandKind.SetBufferedUpdateInterval => MxCommand.PayloadOneofCase.SetBufferedUpdateInterval, MxCommandKind.Suspend => MxCommand.PayloadOneofCase.Suspend, MxCommandKind.Activate => MxCommand.PayloadOneofCase.Activate, MxCommandKind.Write => MxCommand.PayloadOneofCase.Write, MxCommandKind.Write2 => MxCommand.PayloadOneofCase.Write2, MxCommandKind.WriteSecured => MxCommand.PayloadOneofCase.WriteSecured, MxCommandKind.WriteSecured2 => MxCommand.PayloadOneofCase.WriteSecured2, MxCommandKind.AuthenticateUser => MxCommand.PayloadOneofCase.AuthenticateUser, MxCommandKind.ArchestraUserToId => MxCommand.PayloadOneofCase.ArchestraUserToId, MxCommandKind.Ping => MxCommand.PayloadOneofCase.Ping, MxCommandKind.GetSessionState => MxCommand.PayloadOneofCase.GetSessionState, MxCommandKind.GetWorkerInfo => MxCommand.PayloadOneofCase.GetWorkerInfo, MxCommandKind.DrainEvents => MxCommand.PayloadOneofCase.DrainEvents, MxCommandKind.ShutdownWorker => MxCommand.PayloadOneofCase.ShutdownWorker, _ => MxCommand.PayloadOneofCase.None, }; } private static RpcException InvalidArgument(string detail) { return new RpcException(new Status(StatusCode.InvalidArgument, detail)); } }