RemoteCommand
Base type for typed remote commands. A command class inherits from it, specifies its message type as T, and implements HandleCommandAsync. The base reads RemoteCommandAttribute for the command name and deserializes the incoming JSON into T before dispatching.
HandleCommandAsync
Handles the command after the incoming JSON payload has been deserialized into T. Write a reply through ICommandResponse when the client expects one; a command that returns nothing simply completes.
Deserialization failure never reaches this method. A payload whose Data does not produce a T raises a JsonException while the command is being dispatched, which the handler answers with a refused RemoteCommandResponse carrying InvalidMessage, sent on the wire as its underlying number.
using AlmightyShogun.RemoteCommands;
[RemoteCommand("ping", "Replies to a health-check command.")]
public sealed class PingCommand : RemoteCommand<PingCommandData>
{
public override Task HandleCommandAsync(
PingCommandData message,
ICommandResponse response,
CancellationToken cancellationToken
) => response.WriteAsync(
new PingCommandResponse(
"ok",
message.RequestId,
DateTimeOffset.UtcNow
),
cancellationToken
);
}public sealed record PingCommandData(
string RequestId,
DateTimeOffset SentAt
);public sealed record PingCommandResponse(
string Status,
string RequestId,
DateTimeOffset ReceivedAt
);WARNING
A class must declare RemoteCommandAttribute, because the name on it is what RegisterRemoteCommands records. A class without one throws InvalidOperationException during registration, so a missing attribute stops startup rather than reaching the first request, and constructing such a command directly throws the same exception.
Type signature
public abstract Task HandleCommandAsync(
T message,
ICommandResponse response,
CancellationToken cancellationToken = default
);CommandName
The command name declared on the class, read once in the constructor. Available to derived classes for logging and error messages, so a command does not have to reflect on its own attribute again.
using Microsoft.Extensions.Logging;
using AlmightyShogun.RemoteCommands;
[RemoteCommand("reindex")]
public sealed class ReindexCommand(
ILogger<ReindexCommand> logger
) : RemoteCommand<ReindexCommandData>
{
public override Task HandleCommandAsync(
ReindexCommandData message,
ICommandResponse response,
CancellationToken cancellationToken
)
{
logger.LogInformation(
"Running {Command} for {Index}",
CommandName,
message.Index
);
return response.WriteAsync(
new { status = "queued" },
cancellationToken
);
}
}public sealed record ReindexCommandData(string Index);Type signature
protected string CommandName { get; }