Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd4be85f26 | |||
| 7331c6157a |
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MxGateway.Client\MxGateway.Client.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using MxGateway.Client;
|
||||||
|
|
||||||
|
namespace MxGateway.Client.Cli;
|
||||||
|
|
||||||
|
public static class MxGatewayClientCli
|
||||||
|
{
|
||||||
|
public static int Run(
|
||||||
|
string[] args,
|
||||||
|
TextWriter standardOutput,
|
||||||
|
TextWriter standardError)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(args);
|
||||||
|
ArgumentNullException.ThrowIfNull(standardOutput);
|
||||||
|
ArgumentNullException.ThrowIfNull(standardError);
|
||||||
|
|
||||||
|
if (args.Length is 0 || IsHelp(args[0]))
|
||||||
|
{
|
||||||
|
WriteUsage(standardOutput);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(args[0], "version", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
standardOutput.WriteLine(
|
||||||
|
$"gateway-protocol={MxGatewayClientContractInfo.GatewayProtocolVersion}");
|
||||||
|
standardOutput.WriteLine(
|
||||||
|
$"worker-protocol={MxGatewayClientContractInfo.WorkerProtocolVersion}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
standardError.WriteLine($"Unknown command '{args[0]}'.");
|
||||||
|
WriteUsage(standardError);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsHelp(string value)
|
||||||
|
{
|
||||||
|
return string.Equals(value, "-h", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(value, "--help", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(value, "help", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteUsage(TextWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteLine("mxgw-dotnet version");
|
||||||
|
writer.WriteLine("mxgw-dotnet --help");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using MxGateway.Client.Cli;
|
||||||
|
|
||||||
|
return MxGatewayClientCli.Run(args, Console.Out, Console.Error);
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MxGateway.Client\MxGateway.Client.csproj" />
|
||||||
|
<ProjectReference Include="..\MxGateway.Client.Cli\MxGateway.Client.Cli.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using MxGateway.Client.Cli;
|
||||||
|
|
||||||
|
namespace MxGateway.Client.Tests;
|
||||||
|
|
||||||
|
public sealed class MxGatewayClientCliTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Run_Version_PrintsCompiledProtocolVersions()
|
||||||
|
{
|
||||||
|
using var output = new StringWriter();
|
||||||
|
using var error = new StringWriter();
|
||||||
|
|
||||||
|
var exitCode = MxGatewayClientCli.Run(["version"], output, error);
|
||||||
|
|
||||||
|
Assert.Equal(0, exitCode);
|
||||||
|
Assert.Contains("gateway-protocol=1", output.ToString());
|
||||||
|
Assert.Contains("worker-protocol=1", output.ToString());
|
||||||
|
Assert.Equal(string.Empty, error.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using MxGateway.Contracts;
|
||||||
|
|
||||||
|
namespace MxGateway.Client.Tests;
|
||||||
|
|
||||||
|
public sealed class MxGatewayClientContractInfoTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GatewayProtocolVersion_MatchesSharedContract()
|
||||||
|
{
|
||||||
|
Assert.Equal(
|
||||||
|
GatewayContractInfo.GatewayProtocolVersion,
|
||||||
|
MxGatewayClientContractInfo.GatewayProtocolVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WorkerProtocolVersion_MatchesSharedContract()
|
||||||
|
{
|
||||||
|
Assert.Equal(
|
||||||
|
GatewayContractInfo.WorkerProtocolVersion,
|
||||||
|
MxGatewayClientContractInfo.WorkerProtocolVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace MxGateway.Client.Tests;
|
||||||
|
|
||||||
|
public sealed class MxGatewayClientOptionsTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WithAbsoluteEndpointAndApiKey_Succeeds()
|
||||||
|
{
|
||||||
|
var options = new MxGatewayClientOptions
|
||||||
|
{
|
||||||
|
Endpoint = new Uri("http://localhost:5000"),
|
||||||
|
ApiKey = "test-api-key",
|
||||||
|
};
|
||||||
|
|
||||||
|
options.Validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WithEmptyApiKey_Throws()
|
||||||
|
{
|
||||||
|
var options = new MxGatewayClientOptions
|
||||||
|
{
|
||||||
|
Endpoint = new Uri("http://localhost:5000"),
|
||||||
|
ApiKey = "",
|
||||||
|
};
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(options.Validate);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
namespace MxGateway.Client.Tests;
|
||||||
|
|
||||||
|
public sealed class MxGatewayGeneratedContractTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task GeneratedGrpcClient_CanBeConstructedFromClientFactory()
|
||||||
|
{
|
||||||
|
var options = new MxGatewayClientOptions
|
||||||
|
{
|
||||||
|
Endpoint = new Uri("http://localhost:5000"),
|
||||||
|
ApiKey = "test-api-key",
|
||||||
|
};
|
||||||
|
|
||||||
|
await using var client = MxGatewayClient.Create(options);
|
||||||
|
|
||||||
|
Assert.NotNull(client.RawClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Client", "MxGateway.Client\MxGateway.Client.csproj", "{7CF9ED88-1F32-4040-BEB1-D0902E304C70}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Contracts", "..\..\src\MxGateway.Contracts\MxGateway.Contracts.csproj", "{9AB807A8-0469-40F7-A000-D240F36B6E5D}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Client.Cli", "MxGateway.Client.Cli\MxGateway.Client.Cli.csproj", "{EB061E77-2475-4322-9257-3F2456DD141C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxGateway.Client.Tests", "MxGateway.Client.Tests\MxGateway.Client.Tests.csproj", "{B77B5A8E-0C53-4419-9BCD-227C9753A074}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{7CF9ED88-1F32-4040-BEB1-D0902E304C70}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{9AB807A8-0469-40F7-A000-D240F36B6E5D}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{EB061E77-2475-4322-9257-3F2456DD141C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B77B5A8E-0C53-4419-9BCD-227C9753A074}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\src\MxGateway.Contracts\MxGateway.Contracts.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Grpc.Net.Client" Version="2.76.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using Grpc.Net.Client;
|
||||||
|
using MxGateway.Contracts.Proto;
|
||||||
|
|
||||||
|
namespace MxGateway.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides the initial .NET client entry point and raw generated gRPC client.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MxGatewayClient : IAsyncDisposable
|
||||||
|
{
|
||||||
|
private readonly GrpcChannel _channel;
|
||||||
|
|
||||||
|
private MxGatewayClient(GrpcChannel channel)
|
||||||
|
{
|
||||||
|
_channel = channel;
|
||||||
|
RawClient = new MxAccessGateway.MxAccessGatewayClient(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MxAccessGateway.MxAccessGatewayClient RawClient { get; }
|
||||||
|
|
||||||
|
public static MxGatewayClient Create(MxGatewayClientOptions options)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(options);
|
||||||
|
options.Validate();
|
||||||
|
|
||||||
|
var channel = GrpcChannel.ForAddress(
|
||||||
|
options.Endpoint,
|
||||||
|
new GrpcChannelOptions
|
||||||
|
{
|
||||||
|
LoggerFactory = options.LoggerFactory,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new MxGatewayClient(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
_channel.Dispose();
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using MxGateway.Contracts;
|
||||||
|
|
||||||
|
namespace MxGateway.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes the protocol versions compiled into this client package.
|
||||||
|
/// </summary>
|
||||||
|
public static class MxGatewayClientContractInfo
|
||||||
|
{
|
||||||
|
public const uint GatewayProtocolVersion =
|
||||||
|
GatewayContractInfo.GatewayProtocolVersion;
|
||||||
|
|
||||||
|
public const uint WorkerProtocolVersion =
|
||||||
|
GatewayContractInfo.WorkerProtocolVersion;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MxGateway.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures the gRPC channel used by the .NET MXAccess Gateway client.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MxGatewayClientOptions
|
||||||
|
{
|
||||||
|
public required Uri Endpoint { get; init; }
|
||||||
|
|
||||||
|
public required string ApiKey { get; init; }
|
||||||
|
|
||||||
|
public bool UseTls { get; init; }
|
||||||
|
|
||||||
|
public string? CaCertificatePath { get; init; }
|
||||||
|
|
||||||
|
public string? ServerNameOverride { get; init; }
|
||||||
|
|
||||||
|
public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
|
public TimeSpan DefaultCallTimeout { get; init; } = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
public ILoggerFactory? LoggerFactory { get; init; }
|
||||||
|
|
||||||
|
public void Validate()
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(Endpoint);
|
||||||
|
|
||||||
|
if (!Endpoint.IsAbsoluteUri)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
"The gateway endpoint must be an absolute URI.",
|
||||||
|
nameof(Endpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(ApiKey))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
"The gateway API key must not be empty.",
|
||||||
|
nameof(ApiKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConnectTimeout <= TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(
|
||||||
|
nameof(ConnectTimeout),
|
||||||
|
"The connect timeout must be greater than zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DefaultCallTimeout <= TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(
|
||||||
|
nameof(DefaultCallTimeout),
|
||||||
|
"The default call timeout must be greater than zero.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# .NET Client Projects
|
||||||
|
|
||||||
|
The .NET client workspace contains the MXAccess Gateway client library, test
|
||||||
|
CLI, and unit tests.
|
||||||
|
|
||||||
|
## Projects
|
||||||
|
|
||||||
|
| Project | Purpose |
|
||||||
|
|---------|---------|
|
||||||
|
| `MxGateway.Client` | .NET 10 library entry point and raw gRPC client access. |
|
||||||
|
| `MxGateway.Client.Cli` | Test CLI for smoke and diagnostic commands. |
|
||||||
|
| `MxGateway.Client.Tests` | Unit tests for the scaffold and generated contract wiring. |
|
||||||
|
|
||||||
|
The projects reference `src/MxGateway.Contracts/MxGateway.Contracts.csproj` so
|
||||||
|
the client compiles against the same generated protobuf and gRPC types as the
|
||||||
|
gateway. `clients/dotnet/generated` remains reserved for generator output if a
|
||||||
|
future client build switches to client-local `Grpc.Tools` generation.
|
||||||
|
|
||||||
|
## Build And Test
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet build clients/dotnet/MxGateway.Client.sln
|
||||||
|
dotnet test clients/dotnet/MxGateway.Client.sln --no-build
|
||||||
|
```
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# Go Client
|
|
||||||
|
|
||||||
The Go client module contains the generated MXAccess Gateway protobuf bindings,
|
|
||||||
a small handwritten `mxgateway` package, and the `mxgw-go` test CLI scaffold.
|
|
||||||
The module uses the shared proto inputs documented in
|
|
||||||
`../../docs/client-proto-generation.md` so gateway and client contracts stay in
|
|
||||||
sync.
|
|
||||||
|
|
||||||
## Layout
|
|
||||||
|
|
||||||
```text
|
|
||||||
clients/go/
|
|
||||||
go.mod
|
|
||||||
generate-proto.ps1
|
|
||||||
internal/generated/
|
|
||||||
mxgateway/
|
|
||||||
cmd/mxgw-go/
|
|
||||||
```
|
|
||||||
|
|
||||||
`internal/generated` contains code produced by `protoc`, `protoc-gen-go`, and
|
|
||||||
`protoc-gen-go-grpc`. Do not edit generated files by hand.
|
|
||||||
|
|
||||||
## Regenerating Protobuf Bindings
|
|
||||||
|
|
||||||
Run generation after the shared `.proto` files or the Go output path changes:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
./generate-proto.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
The script uses the tool paths recorded in `../../docs/toolchain-links.md`.
|
|
||||||
|
|
||||||
## Build And Test
|
|
||||||
|
|
||||||
Run the Go module checks from `clients/go`:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
go test ./...
|
|
||||||
go build ./...
|
|
||||||
```
|
|
||||||
|
|
||||||
The scaffold tests parse the shared golden JSON fixtures with the generated Go
|
|
||||||
types. Later client implementation tests add fake gRPC services, auth metadata,
|
|
||||||
streaming, value conversion, and CLI behavior.
|
|
||||||
|
|
||||||
## CLI
|
|
||||||
|
|
||||||
The scaffold CLI exposes version information:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
go run ./cmd/mxgw-go version -json
|
|
||||||
```
|
|
||||||
|
|
||||||
Additional commands are implemented with the client/session wrapper work.
|
|
||||||
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/mxgateway"
|
|
||||||
)
|
|
||||||
|
|
||||||
type versionOutput struct {
|
|
||||||
ClientVersion string `json:"clientVersion"`
|
|
||||||
GatewayProtocolVersion uint32 `json:"gatewayProtocolVersion"`
|
|
||||||
WorkerProtocolVersion uint32 `json:"workerProtocolVersion"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if err := run(os.Args[1:]); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return fmt.Errorf("usage: mxgw-go version [-json]")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch args[0] {
|
|
||||||
case "version":
|
|
||||||
return runVersion(args[1:])
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown command %q", args[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runVersion(args []string) error {
|
|
||||||
flags := flag.NewFlagSet("version", flag.ContinueOnError)
|
|
||||||
flags.SetOutput(os.Stderr)
|
|
||||||
jsonOutput := flags.Bool("json", false, "write JSON output")
|
|
||||||
|
|
||||||
if err := flags.Parse(args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
output := versionOutput{
|
|
||||||
ClientVersion: mxgateway.ClientVersion,
|
|
||||||
GatewayProtocolVersion: mxgateway.GatewayProtocolVersion,
|
|
||||||
WorkerProtocolVersion: mxgateway.WorkerProtocolVersion,
|
|
||||||
}
|
|
||||||
|
|
||||||
if *jsonOutput {
|
|
||||||
encoder := json.NewEncoder(os.Stdout)
|
|
||||||
encoder.SetIndent("", " ")
|
|
||||||
return encoder.Encode(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "mxgw-go %s\n", output.ClientVersion)
|
|
||||||
fmt.Fprintf(os.Stdout, "gateway protocol %d\n", output.GatewayProtocolVersion)
|
|
||||||
fmt.Fprintf(os.Stdout, "worker protocol %d\n", output.WorkerProtocolVersion)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
Set-StrictMode -Version Latest
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..')
|
|
||||||
$protoRoot = Join-Path $repoRoot 'src\MxGateway.Contracts\Protos'
|
|
||||||
$outputRoot = Join-Path $PSScriptRoot 'internal\generated'
|
|
||||||
$modulePath = 'gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated'
|
|
||||||
$protoc = 'C:\Users\dohertj2\AppData\Local\Microsoft\WinGet\Packages\Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe\bin\protoc.exe'
|
|
||||||
$goPluginPath = 'C:\Users\dohertj2\go\bin'
|
|
||||||
|
|
||||||
if (-not (Test-Path $protoc)) {
|
|
||||||
throw "protoc was not found at $protoc. See docs/toolchain-links.md."
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($pluginName in @('protoc-gen-go.exe', 'protoc-gen-go-grpc.exe')) {
|
|
||||||
$pluginPath = Join-Path $goPluginPath $pluginName
|
|
||||||
if (-not (Test-Path $pluginPath)) {
|
|
||||||
throw "$pluginName was not found at $pluginPath. See docs/toolchain-links.md."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null
|
|
||||||
Get-ChildItem -Path $outputRoot -Filter '*.pb.go' -File | Remove-Item
|
|
||||||
|
|
||||||
$env:Path = "$goPluginPath;$env:Path"
|
|
||||||
|
|
||||||
& $protoc `
|
|
||||||
--proto_path=$protoRoot `
|
|
||||||
--go_out=$outputRoot `
|
|
||||||
--go_opt=paths=source_relative `
|
|
||||||
"--go_opt=Mmxaccess_gateway.proto=$modulePath;generated" `
|
|
||||||
"--go_opt=Mmxaccess_worker.proto=$modulePath;generated" `
|
|
||||||
mxaccess_gateway.proto `
|
|
||||||
mxaccess_worker.proto
|
|
||||||
|
|
||||||
& $protoc `
|
|
||||||
--proto_path=$protoRoot `
|
|
||||||
--go-grpc_out=$outputRoot `
|
|
||||||
--go-grpc_opt=paths=source_relative `
|
|
||||||
"--go-grpc_opt=Mmxaccess_gateway.proto=$modulePath;generated" `
|
|
||||||
mxaccess_gateway.proto
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
module gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go
|
|
||||||
|
|
||||||
go 1.26
|
|
||||||
|
|
||||||
require (
|
|
||||||
google.golang.org/grpc v1.80.0
|
|
||||||
google.golang.org/protobuf v1.36.11
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
golang.org/x/net v0.49.0 // indirect
|
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
|
||||||
golang.org/x/text v0.33.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
|
|
||||||
)
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
|
||||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
|
||||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
|
||||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
|
||||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
|
||||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
|
||||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
|
||||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
|
||||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
||||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
|
||||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
|
||||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
|
||||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
|
||||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
|
||||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,243 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// - protoc-gen-go-grpc v1.6.1
|
|
||||||
// - protoc v7.34.1
|
|
||||||
// source: mxaccess_gateway.proto
|
|
||||||
|
|
||||||
package generated
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "context"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
codes "google.golang.org/grpc/codes"
|
|
||||||
status "google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
// Requires gRPC-Go v1.64.0 or later.
|
|
||||||
const _ = grpc.SupportPackageIsVersion9
|
|
||||||
|
|
||||||
const (
|
|
||||||
MxAccessGateway_OpenSession_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/OpenSession"
|
|
||||||
MxAccessGateway_CloseSession_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/CloseSession"
|
|
||||||
MxAccessGateway_Invoke_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/Invoke"
|
|
||||||
MxAccessGateway_StreamEvents_FullMethodName = "/mxaccess_gateway.v1.MxAccessGateway/StreamEvents"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MxAccessGatewayClient is the client API for MxAccessGateway service.
|
|
||||||
//
|
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
|
||||||
//
|
|
||||||
// Public client API for MXAccess sessions hosted by the gateway.
|
|
||||||
type MxAccessGatewayClient interface {
|
|
||||||
OpenSession(ctx context.Context, in *OpenSessionRequest, opts ...grpc.CallOption) (*OpenSessionReply, error)
|
|
||||||
CloseSession(ctx context.Context, in *CloseSessionRequest, opts ...grpc.CallOption) (*CloseSessionReply, error)
|
|
||||||
Invoke(ctx context.Context, in *MxCommandRequest, opts ...grpc.CallOption) (*MxCommandReply, error)
|
|
||||||
StreamEvents(ctx context.Context, in *StreamEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MxEvent], error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type mxAccessGatewayClient struct {
|
|
||||||
cc grpc.ClientConnInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMxAccessGatewayClient(cc grpc.ClientConnInterface) MxAccessGatewayClient {
|
|
||||||
return &mxAccessGatewayClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mxAccessGatewayClient) OpenSession(ctx context.Context, in *OpenSessionRequest, opts ...grpc.CallOption) (*OpenSessionReply, error) {
|
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(OpenSessionReply)
|
|
||||||
err := c.cc.Invoke(ctx, MxAccessGateway_OpenSession_FullMethodName, in, out, cOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mxAccessGatewayClient) CloseSession(ctx context.Context, in *CloseSessionRequest, opts ...grpc.CallOption) (*CloseSessionReply, error) {
|
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(CloseSessionReply)
|
|
||||||
err := c.cc.Invoke(ctx, MxAccessGateway_CloseSession_FullMethodName, in, out, cOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mxAccessGatewayClient) Invoke(ctx context.Context, in *MxCommandRequest, opts ...grpc.CallOption) (*MxCommandReply, error) {
|
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(MxCommandReply)
|
|
||||||
err := c.cc.Invoke(ctx, MxAccessGateway_Invoke_FullMethodName, in, out, cOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mxAccessGatewayClient) StreamEvents(ctx context.Context, in *StreamEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MxEvent], error) {
|
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
stream, err := c.cc.NewStream(ctx, &MxAccessGateway_ServiceDesc.Streams[0], MxAccessGateway_StreamEvents_FullMethodName, cOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
x := &grpc.GenericClientStream[StreamEventsRequest, MxEvent]{ClientStream: stream}
|
|
||||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := x.ClientStream.CloseSend(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return x, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
|
||||||
type MxAccessGateway_StreamEventsClient = grpc.ServerStreamingClient[MxEvent]
|
|
||||||
|
|
||||||
// MxAccessGatewayServer is the server API for MxAccessGateway service.
|
|
||||||
// All implementations must embed UnimplementedMxAccessGatewayServer
|
|
||||||
// for forward compatibility.
|
|
||||||
//
|
|
||||||
// Public client API for MXAccess sessions hosted by the gateway.
|
|
||||||
type MxAccessGatewayServer interface {
|
|
||||||
OpenSession(context.Context, *OpenSessionRequest) (*OpenSessionReply, error)
|
|
||||||
CloseSession(context.Context, *CloseSessionRequest) (*CloseSessionReply, error)
|
|
||||||
Invoke(context.Context, *MxCommandRequest) (*MxCommandReply, error)
|
|
||||||
StreamEvents(*StreamEventsRequest, grpc.ServerStreamingServer[MxEvent]) error
|
|
||||||
mustEmbedUnimplementedMxAccessGatewayServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedMxAccessGatewayServer must be embedded to have
|
|
||||||
// forward compatible implementations.
|
|
||||||
//
|
|
||||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
|
||||||
// pointer dereference when methods are called.
|
|
||||||
type UnimplementedMxAccessGatewayServer struct{}
|
|
||||||
|
|
||||||
func (UnimplementedMxAccessGatewayServer) OpenSession(context.Context, *OpenSessionRequest) (*OpenSessionReply, error) {
|
|
||||||
return nil, status.Error(codes.Unimplemented, "method OpenSession not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedMxAccessGatewayServer) CloseSession(context.Context, *CloseSessionRequest) (*CloseSessionReply, error) {
|
|
||||||
return nil, status.Error(codes.Unimplemented, "method CloseSession not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedMxAccessGatewayServer) Invoke(context.Context, *MxCommandRequest) (*MxCommandReply, error) {
|
|
||||||
return nil, status.Error(codes.Unimplemented, "method Invoke not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedMxAccessGatewayServer) StreamEvents(*StreamEventsRequest, grpc.ServerStreamingServer[MxEvent]) error {
|
|
||||||
return status.Error(codes.Unimplemented, "method StreamEvents not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedMxAccessGatewayServer) mustEmbedUnimplementedMxAccessGatewayServer() {}
|
|
||||||
func (UnimplementedMxAccessGatewayServer) testEmbeddedByValue() {}
|
|
||||||
|
|
||||||
// UnsafeMxAccessGatewayServer may be embedded to opt out of forward compatibility for this service.
|
|
||||||
// Use of this interface is not recommended, as added methods to MxAccessGatewayServer will
|
|
||||||
// result in compilation errors.
|
|
||||||
type UnsafeMxAccessGatewayServer interface {
|
|
||||||
mustEmbedUnimplementedMxAccessGatewayServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterMxAccessGatewayServer(s grpc.ServiceRegistrar, srv MxAccessGatewayServer) {
|
|
||||||
// If the following call panics, it indicates UnimplementedMxAccessGatewayServer was
|
|
||||||
// embedded by pointer and is nil. This will cause panics if an
|
|
||||||
// unimplemented method is ever invoked, so we test this at initialization
|
|
||||||
// time to prevent it from happening at runtime later due to I/O.
|
|
||||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
|
||||||
t.testEmbeddedByValue()
|
|
||||||
}
|
|
||||||
s.RegisterService(&MxAccessGateway_ServiceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _MxAccessGateway_OpenSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(OpenSessionRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(MxAccessGatewayServer).OpenSession(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: MxAccessGateway_OpenSession_FullMethodName,
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(MxAccessGatewayServer).OpenSession(ctx, req.(*OpenSessionRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _MxAccessGateway_CloseSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(CloseSessionRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(MxAccessGatewayServer).CloseSession(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: MxAccessGateway_CloseSession_FullMethodName,
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(MxAccessGatewayServer).CloseSession(ctx, req.(*CloseSessionRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _MxAccessGateway_Invoke_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(MxCommandRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(MxAccessGatewayServer).Invoke(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: MxAccessGateway_Invoke_FullMethodName,
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(MxAccessGatewayServer).Invoke(ctx, req.(*MxCommandRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _MxAccessGateway_StreamEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
|
|
||||||
m := new(StreamEventsRequest)
|
|
||||||
if err := stream.RecvMsg(m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return srv.(MxAccessGatewayServer).StreamEvents(m, &grpc.GenericServerStream[StreamEventsRequest, MxEvent]{ServerStream: stream})
|
|
||||||
}
|
|
||||||
|
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
|
||||||
type MxAccessGateway_StreamEventsServer = grpc.ServerStreamingServer[MxEvent]
|
|
||||||
|
|
||||||
// MxAccessGateway_ServiceDesc is the grpc.ServiceDesc for MxAccessGateway service.
|
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
|
||||||
// and not to be introspected or modified (even as a copy)
|
|
||||||
var MxAccessGateway_ServiceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "mxaccess_gateway.v1.MxAccessGateway",
|
|
||||||
HandlerType: (*MxAccessGatewayServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "OpenSession",
|
|
||||||
Handler: _MxAccessGateway_OpenSession_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "CloseSession",
|
|
||||||
Handler: _MxAccessGateway_CloseSession_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "Invoke",
|
|
||||||
Handler: _MxAccessGateway_Invoke_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{
|
|
||||||
{
|
|
||||||
StreamName: "StreamEvents",
|
|
||||||
Handler: _MxAccessGateway_StreamEvents_Handler,
|
|
||||||
ServerStreams: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Metadata: "mxaccess_gateway.proto",
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
|||||||
package mxgateway
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// Options configures future gateway connections.
|
|
||||||
type Options struct {
|
|
||||||
Endpoint string
|
|
||||||
APIKey string
|
|
||||||
Plaintext bool
|
|
||||||
CACertFile string
|
|
||||||
ServerNameOverride string
|
|
||||||
}
|
|
||||||
|
|
||||||
// RedactedAPIKey returns a display-safe representation of the configured API
|
|
||||||
// key for diagnostics and CLI output.
|
|
||||||
func (o Options) RedactedAPIKey() string {
|
|
||||||
return RedactAPIKey(o.APIKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RedactAPIKey hides credential material while keeping enough shape for
|
|
||||||
// troubleshooting whether a key was supplied.
|
|
||||||
func RedactAPIKey(apiKey string) string {
|
|
||||||
if apiKey == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(apiKey) <= 8 {
|
|
||||||
return "<redacted>"
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix, suffix := apiKey[:4], apiKey[len(apiKey)-4:]
|
|
||||||
return prefix + strings.Repeat("*", len(apiKey)-8) + suffix
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package mxgateway
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestRedactAPIKey(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
apiKey string
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{name: "empty", apiKey: "", want: ""},
|
|
||||||
{name: "short", apiKey: "mxgw_1", want: "<redacted>"},
|
|
||||||
{name: "long", apiKey: "mxgw_key_secret", want: "mxgw*******cret"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := RedactAPIKey(tt.apiKey); got != tt.want {
|
|
||||||
t.Fatalf("RedactAPIKey() = %q, want %q", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package mxgateway
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
pb "gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated"
|
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGeneratedGoldenFixturesParse(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
path string
|
|
||||||
msg proto.Message
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "open session reply",
|
|
||||||
path: filepath.Join("..", "..", "proto", "fixtures", "golden", "open-session-reply.ok.json"),
|
|
||||||
msg: &pb.OpenSessionReply{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "register command request",
|
|
||||||
path: filepath.Join("..", "..", "proto", "fixtures", "golden", "register-command-request.json"),
|
|
||||||
msg: &pb.MxCommandRequest{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "on data change event",
|
|
||||||
path: filepath.Join("..", "..", "proto", "fixtures", "golden", "on-data-change-event.json"),
|
|
||||||
msg: &pb.MxEvent{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
unmarshal := protojson.UnmarshalOptions{DiscardUnknown: false}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
data, err := os.ReadFile(tt.path)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("read fixture: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := unmarshal.Unmarshal(data, tt.msg); err != nil {
|
|
||||||
t.Fatalf("parse fixture: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOpenSessionFixtureProtocolVersions(t *testing.T) {
|
|
||||||
data, err := os.ReadFile(filepath.Join("..", "..", "proto", "fixtures", "golden", "open-session-reply.ok.json"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("read fixture: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var reply pb.OpenSessionReply
|
|
||||||
if err := protojson.Unmarshal(data, &reply); err != nil {
|
|
||||||
t.Fatalf("parse fixture: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if reply.GetGatewayProtocolVersion() != GatewayProtocolVersion {
|
|
||||||
t.Fatalf("gateway protocol = %d, want %d", reply.GetGatewayProtocolVersion(), GatewayProtocolVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
if reply.GetWorkerProtocolVersion() != WorkerProtocolVersion {
|
|
||||||
t.Fatalf("worker protocol = %d, want %d", reply.GetWorkerProtocolVersion(), WorkerProtocolVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package mxgateway
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ClientVersion identifies this Go client scaffold before package releases
|
|
||||||
// assign semantic versions.
|
|
||||||
ClientVersion = "0.1.0-dev"
|
|
||||||
|
|
||||||
// GatewayProtocolVersion matches GatewayContractInfo.GatewayProtocolVersion
|
|
||||||
// in the shared .NET contracts.
|
|
||||||
GatewayProtocolVersion uint32 = 1
|
|
||||||
|
|
||||||
// WorkerProtocolVersion matches GatewayContractInfo.WorkerProtocolVersion
|
|
||||||
// and is exposed for fake-worker and parity tests.
|
|
||||||
WorkerProtocolVersion uint32 = 1
|
|
||||||
)
|
|
||||||
@@ -100,17 +100,6 @@ Go clients should generate `mxaccess_gateway.proto` and
|
|||||||
`protoc-gen-go` and `protoc-gen-go-grpc`. Keep generated packages internal
|
`protoc-gen-go` and `protoc-gen-go-grpc`. Keep generated packages internal
|
||||||
unless the wrapper API intentionally exposes raw protobuf messages.
|
unless the wrapper API intentionally exposes raw protobuf messages.
|
||||||
|
|
||||||
The Go scaffold provides a repo-local generation script:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
clients/go/generate-proto.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
The script maps both proto files into the internal Go package
|
|
||||||
`gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated` because
|
|
||||||
the source `.proto` files do not carry Go-specific `go_package` options. This
|
|
||||||
keeps language-specific packaging outside the public contract files.
|
|
||||||
|
|
||||||
Rust clients should use `tonic-build` or the selected protobuf generator from
|
Rust clients should use `tonic-build` or the selected protobuf generator from
|
||||||
the Rust client build script, with generated modules placed under
|
the Rust client build script, with generated modules placed under
|
||||||
`clients/rust/src/generated` or included from the build output according to the
|
`clients/rust/src/generated` or included from the build output according to the
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ Recommended layout:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
clients/dotnet/
|
clients/dotnet/
|
||||||
|
MxGateway.Client.sln
|
||||||
MxGateway.Client/
|
MxGateway.Client/
|
||||||
MxGateway.Client.csproj
|
MxGateway.Client.csproj
|
||||||
GatewayClient.cs
|
GatewayClient.cs
|
||||||
@@ -41,6 +42,12 @@ Target framework:
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The scaffold uses a project reference to
|
||||||
|
`src/MxGateway.Contracts/MxGateway.Contracts.csproj` for generated protobuf and
|
||||||
|
gRPC types. `clients/dotnet/generated` remains reserved for client-local
|
||||||
|
generator output if the .NET client later needs to decouple from the contracts
|
||||||
|
project.
|
||||||
|
|
||||||
Expected packages:
|
Expected packages:
|
||||||
|
|
||||||
- `Grpc.Net.Client`
|
- `Grpc.Net.Client`
|
||||||
|
|||||||
Reference in New Issue
Block a user