Add Galaxy repository API and clients
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using MxGateway.Contracts.Proto;
|
||||
using MxGateway.Contracts.Proto.Galaxy;
|
||||
|
||||
namespace MxGateway.Client.Cli;
|
||||
|
||||
@@ -19,4 +20,20 @@ public interface IMxGatewayCliClient : IAsyncDisposable
|
||||
IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
StreamEventsRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task<TestConnectionReply> GalaxyTestConnectionAsync(
|
||||
TestConnectionRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task<GetLastDeployTimeReply> GalaxyGetLastDeployTimeAsync(
|
||||
GetLastDeployTimeRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task<DiscoverHierarchyReply> GalaxyDiscoverHierarchyAsync(
|
||||
DiscoverHierarchyRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
IAsyncEnumerable<DeployEvent> GalaxyWatchDeployEventsAsync(
|
||||
WatchDeployEventsRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -1,40 +1,84 @@
|
||||
using MxGateway.Client;
|
||||
using MxGateway.Contracts.Proto;
|
||||
using MxGateway.Contracts.Proto.Galaxy;
|
||||
|
||||
namespace MxGateway.Client.Cli;
|
||||
|
||||
internal sealed class MxGatewayCliClientAdapter(MxGatewayClient client) : IMxGatewayCliClient
|
||||
internal sealed class MxGatewayCliClientAdapter : IMxGatewayCliClient
|
||||
{
|
||||
private readonly MxGatewayClient _client;
|
||||
private readonly Lazy<GalaxyRepositoryClient> _galaxyClient;
|
||||
|
||||
public MxGatewayCliClientAdapter(MxGatewayClient client)
|
||||
{
|
||||
_client = client;
|
||||
_galaxyClient = new Lazy<GalaxyRepositoryClient>(
|
||||
() => GalaxyRepositoryClient.Create(_client.Options));
|
||||
}
|
||||
|
||||
public Task<OpenSessionReply> OpenSessionAsync(
|
||||
OpenSessionRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return client.OpenSessionRawAsync(request, cancellationToken);
|
||||
return _client.OpenSessionRawAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<CloseSessionReply> CloseSessionAsync(
|
||||
CloseSessionRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return client.CloseSessionRawAsync(request, cancellationToken);
|
||||
return _client.CloseSessionRawAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<MxCommandReply> InvokeAsync(
|
||||
MxCommandRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return client.InvokeAsync(request, cancellationToken);
|
||||
return _client.InvokeAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
StreamEventsRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return client.StreamEventsAsync(request, cancellationToken);
|
||||
return _client.StreamEventsAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
public Task<TestConnectionReply> GalaxyTestConnectionAsync(
|
||||
TestConnectionRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return client.DisposeAsync();
|
||||
return _galaxyClient.Value.TestConnectionRawAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<GetLastDeployTimeReply> GalaxyGetLastDeployTimeAsync(
|
||||
GetLastDeployTimeRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return _galaxyClient.Value.GetLastDeployTimeRawAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DiscoverHierarchyReply> GalaxyDiscoverHierarchyAsync(
|
||||
DiscoverHierarchyRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return _galaxyClient.Value.DiscoverHierarchyRawAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<DeployEvent> GalaxyWatchDeployEventsAsync(
|
||||
WatchDeployEventsRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return _galaxyClient.Value.WatchDeployEventsRawAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_galaxyClient.IsValueCreated)
|
||||
{
|
||||
await _galaxyClient.Value.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _client.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Text.Json;
|
||||
using Google.Protobuf;
|
||||
using MxGateway.Client;
|
||||
using MxGateway.Contracts.Proto;
|
||||
using MxGateway.Contracts.Proto.Galaxy;
|
||||
|
||||
namespace MxGateway.Client.Cli;
|
||||
|
||||
@@ -70,7 +71,7 @@ public static class MxGatewayClientCli
|
||||
}
|
||||
|
||||
await using IMxGatewayCliClient client = clientFactory(CreateOptions(arguments));
|
||||
using CancellationTokenSource cancellation = CreateCancellation(arguments);
|
||||
using CancellationTokenSource cancellation = CreateCancellation(arguments, command);
|
||||
|
||||
return command switch
|
||||
{
|
||||
@@ -98,6 +99,14 @@ public static class MxGatewayClientCli
|
||||
.ConfigureAwait(false),
|
||||
"smoke" => await SmokeAsync(arguments, client, standardOutput, cancellation.Token)
|
||||
.ConfigureAwait(false),
|
||||
"galaxy-test-connection" => await GalaxyTestConnectionAsync(arguments, client, standardOutput, cancellation.Token)
|
||||
.ConfigureAwait(false),
|
||||
"galaxy-last-deploy" => await GalaxyLastDeployAsync(arguments, client, standardOutput, cancellation.Token)
|
||||
.ConfigureAwait(false),
|
||||
"galaxy-discover" => await GalaxyDiscoverAsync(arguments, client, standardOutput, cancellation.Token)
|
||||
.ConfigureAwait(false),
|
||||
"galaxy-watch" => await GalaxyWatchAsync(arguments, client, standardOutput, cancellation.Token)
|
||||
.ConfigureAwait(false),
|
||||
_ => WriteUnknownCommand(command, standardError),
|
||||
};
|
||||
}
|
||||
@@ -168,9 +177,18 @@ public static class MxGatewayClientCli
|
||||
$"Gateway API key is required. Pass --api-key or set {apiKeyEnvironmentName}.");
|
||||
}
|
||||
|
||||
private static CancellationTokenSource CreateCancellation(CliArguments arguments)
|
||||
private static CancellationTokenSource CreateCancellation(CliArguments arguments, string command)
|
||||
{
|
||||
var cancellation = new CancellationTokenSource();
|
||||
// Long-running streaming commands run until Ctrl+C / cancellation by default;
|
||||
// a caller-supplied --timeout still applies if present.
|
||||
bool isLongRunning = command is "galaxy-watch";
|
||||
string? rawTimeout = arguments.GetOptional("timeout");
|
||||
if (isLongRunning && string.IsNullOrWhiteSpace(rawTimeout))
|
||||
{
|
||||
return cancellation;
|
||||
}
|
||||
|
||||
TimeSpan timeout = arguments.GetDuration("timeout", TimeSpan.FromSeconds(30));
|
||||
cancellation.CancelAfter(timeout);
|
||||
return cancellation;
|
||||
@@ -766,6 +784,146 @@ public static class MxGatewayClientCli
|
||||
.ToMxValue();
|
||||
}
|
||||
|
||||
private static Task<int> GalaxyTestConnectionAsync(
|
||||
CliArguments arguments,
|
||||
IMxGatewayCliClient client,
|
||||
TextWriter output,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return WriteReplyAsync(
|
||||
client.GalaxyTestConnectionAsync(new TestConnectionRequest(), cancellationToken),
|
||||
arguments,
|
||||
output);
|
||||
}
|
||||
|
||||
private static Task<int> GalaxyLastDeployAsync(
|
||||
CliArguments arguments,
|
||||
IMxGatewayCliClient client,
|
||||
TextWriter output,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return WriteReplyAsync(
|
||||
client.GalaxyGetLastDeployTimeAsync(new GetLastDeployTimeRequest(), cancellationToken),
|
||||
arguments,
|
||||
output);
|
||||
}
|
||||
|
||||
private static async Task<int> GalaxyDiscoverAsync(
|
||||
CliArguments arguments,
|
||||
IMxGatewayCliClient client,
|
||||
TextWriter output,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
DiscoverHierarchyReply reply = await client.GalaxyDiscoverHierarchyAsync(
|
||||
new DiscoverHierarchyRequest(),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (arguments.HasFlag("json"))
|
||||
{
|
||||
output.WriteLine(ProtobufJsonFormatter.Format(reply));
|
||||
return 0;
|
||||
}
|
||||
|
||||
output.WriteLine($"objects={reply.Objects.Count}");
|
||||
foreach (GalaxyObject galaxyObject in reply.Objects)
|
||||
{
|
||||
output.WriteLine($"- gobject_id={galaxyObject.GobjectId} tag_name={galaxyObject.TagName} contained_name={galaxyObject.ContainedName} parent={galaxyObject.ParentGobjectId} attributes={galaxyObject.Attributes.Count}");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static async Task<int> GalaxyWatchAsync(
|
||||
CliArguments arguments,
|
||||
IMxGatewayCliClient client,
|
||||
TextWriter output,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
bool json = arguments.HasFlag("json");
|
||||
uint maxEvents = arguments.GetUInt32("max-events", 0);
|
||||
if (maxEvents > MaxAggregateEvents)
|
||||
{
|
||||
throw new ArgumentException($"--max-events cannot exceed {MaxAggregateEvents}.");
|
||||
}
|
||||
|
||||
WatchDeployEventsRequest request = new();
|
||||
string? lastSeen = arguments.GetOptional("last-seen-deploy-time");
|
||||
if (!string.IsNullOrWhiteSpace(lastSeen))
|
||||
{
|
||||
DateTimeOffset parsed = DateTimeOffset.Parse(
|
||||
lastSeen,
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
|
||||
request.LastSeenDeployTime = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTimeOffset(parsed);
|
||||
}
|
||||
|
||||
using CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
ConsoleCancelEventHandler handler = (_, eventArgs) =>
|
||||
{
|
||||
eventArgs.Cancel = true;
|
||||
try
|
||||
{
|
||||
linked.Cancel();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
};
|
||||
Console.CancelKeyPress += handler;
|
||||
|
||||
uint emitted = 0;
|
||||
try
|
||||
{
|
||||
await foreach (DeployEvent deployEvent in client
|
||||
.GalaxyWatchDeployEventsAsync(request, linked.Token)
|
||||
.WithCancellation(linked.Token)
|
||||
.ConfigureAwait(false))
|
||||
{
|
||||
if (json)
|
||||
{
|
||||
output.WriteLine(ProtobufJsonFormatter.Format(deployEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
output.WriteLine(FormatDeployEvent(deployEvent));
|
||||
}
|
||||
|
||||
emitted++;
|
||||
if (maxEvents > 0 && emitted >= maxEvents)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (linked.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Ctrl+C-driven cancellation is a clean exit.
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.CancelKeyPress -= handler;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static string FormatDeployEvent(DeployEvent deployEvent)
|
||||
{
|
||||
string deployTime = deployEvent.TimeOfLastDeployPresent && deployEvent.TimeOfLastDeploy is not null
|
||||
? deployEvent.TimeOfLastDeploy
|
||||
.ToDateTimeOffset()
|
||||
.ToString("O", CultureInfo.InvariantCulture)
|
||||
: "<none>";
|
||||
string observed = deployEvent.ObservedAt is not null
|
||||
? deployEvent.ObservedAt
|
||||
.ToDateTimeOffset()
|
||||
.ToString("O", CultureInfo.InvariantCulture)
|
||||
: "<unknown>";
|
||||
|
||||
return $"sequence={deployEvent.Sequence} observed_at={observed} time_of_last_deploy={deployTime} objects={deployEvent.ObjectCount} attributes={deployEvent.AttributeCount}";
|
||||
}
|
||||
|
||||
private static int WriteUnknownCommand(string command, TextWriter standardError)
|
||||
{
|
||||
standardError.WriteLine($"Unknown command '{command}'.");
|
||||
@@ -793,7 +951,11 @@ public static class MxGatewayClientCli
|
||||
or "stream-events"
|
||||
or "write"
|
||||
or "write2"
|
||||
or "smoke";
|
||||
or "smoke"
|
||||
or "galaxy-test-connection"
|
||||
or "galaxy-last-deploy"
|
||||
or "galaxy-discover"
|
||||
or "galaxy-watch";
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> ParseStringList(string value)
|
||||
@@ -842,5 +1004,9 @@ public static class MxGatewayClientCli
|
||||
writer.WriteLine("mxgw-dotnet write --session-id <id> --server-handle <n> --item-handle <n> --type <type> --value <value> [--json]");
|
||||
writer.WriteLine("mxgw-dotnet write2 --session-id <id> --server-handle <n> --item-handle <n> --type <type> --value <value> [--timestamp <iso>] [--json]");
|
||||
writer.WriteLine("mxgw-dotnet smoke --item <ref> [--value <value> --type <type>] [--json]");
|
||||
writer.WriteLine("mxgw-dotnet galaxy-test-connection [--json]");
|
||||
writer.WriteLine("mxgw-dotnet galaxy-last-deploy [--json]");
|
||||
writer.WriteLine("mxgw-dotnet galaxy-discover [--json]");
|
||||
writer.WriteLine("mxgw-dotnet galaxy-watch [--last-seen-deploy-time <iso8601>] [--max-events <n>] [--json]");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user