Align docs with StyleGuide and add CLAUDE.md
- Rename 16 kebab-case docs to PascalCase per StyleGuide - Move per-language client design docs from docs/ to clients/<lang>/ alongside their READMEs - Add ## Related Documentation sections to 15 docs that lacked one - Fix sentence-case violations in H3 headings (StyleGuide rule) - Update cross-references in gateway.md, client READMEs, scripts, and generate-proto.ps1 helpers to follow the new paths - Add CLAUDE.md with build/test commands, the source-update verification matrix, the parity-first contract, and pointers to MXAccess and Galaxy Repository analysis sources Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
# .NET 10 C# Client Detailed Design
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide an idiomatic .NET 10 C# client library for MXAccess Gateway, plus a test
|
||||
CLI and unit tests. This client is for modern .NET callers and must not load
|
||||
MXAccess COM.
|
||||
|
||||
Follow the [C# Style Guide](../../docs/style-guides/CSharpStyleGuide.md) for
|
||||
handwritten code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md)
|
||||
for generated contract inputs.
|
||||
|
||||
## Projects
|
||||
|
||||
Recommended layout:
|
||||
|
||||
```text
|
||||
clients/dotnet/
|
||||
MxGateway.Client.sln
|
||||
MxGateway.Client/
|
||||
MxGateway.Client.csproj
|
||||
GatewayClient.cs
|
||||
MxGatewaySession.cs
|
||||
MxGatewayClientOptions.cs
|
||||
Authentication/
|
||||
Conversion/
|
||||
Errors/
|
||||
Generated/
|
||||
MxGateway.Client.Cli/
|
||||
MxGateway.Client.Cli.csproj
|
||||
Program.cs
|
||||
Commands/
|
||||
MxGateway.Client.Tests/
|
||||
MxGateway.Client.Tests.csproj
|
||||
MxGateway.Client.IntegrationTests/
|
||||
MxGateway.Client.IntegrationTests.csproj
|
||||
```
|
||||
|
||||
Target framework:
|
||||
|
||||
```xml
|
||||
<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:
|
||||
|
||||
- `Grpc.Net.Client`
|
||||
- `Google.Protobuf`
|
||||
- `Grpc.Tools` for generation
|
||||
- `Microsoft.Extensions.Logging.Abstractions`
|
||||
- `System.CommandLine` or similar for CLI
|
||||
- test framework: xUnit or NUnit
|
||||
|
||||
## Library API
|
||||
|
||||
Suggested public types:
|
||||
|
||||
```csharp
|
||||
public sealed class MxGatewayClient : IAsyncDisposable
|
||||
{
|
||||
public static MxGatewayClient Create(MxGatewayClientOptions options);
|
||||
public Task<MxGatewaySession> OpenSessionAsync(
|
||||
OpenSessionOptions? options = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
public Task<MxCommandReply> InvokeAsync(
|
||||
MxCommandRequest request,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public sealed class MxGatewaySession : IAsyncDisposable
|
||||
{
|
||||
public string SessionId { get; }
|
||||
|
||||
public Task<int> RegisterAsync(string clientName, CancellationToken ct = default);
|
||||
public Task UnregisterAsync(int serverHandle, CancellationToken ct = default);
|
||||
public Task<int> AddItemAsync(int serverHandle, string item, CancellationToken ct = default);
|
||||
public Task<int> AddItem2Async(int serverHandle, string item, string context, CancellationToken ct = default);
|
||||
public Task AdviseAsync(int serverHandle, int itemHandle, CancellationToken ct = default);
|
||||
public Task UnAdviseAsync(int serverHandle, int itemHandle, CancellationToken ct = default);
|
||||
public Task<IReadOnlyList<SubscribeResult>> AddItemBulkAsync(int serverHandle, IReadOnlyList<string> tagAddresses, CancellationToken ct = default);
|
||||
public Task<IReadOnlyList<SubscribeResult>> AdviseItemBulkAsync(int serverHandle, IReadOnlyList<int> itemHandles, CancellationToken ct = default);
|
||||
public Task<IReadOnlyList<SubscribeResult>> RemoveItemBulkAsync(int serverHandle, IReadOnlyList<int> itemHandles, CancellationToken ct = default);
|
||||
public Task<IReadOnlyList<SubscribeResult>> UnAdviseItemBulkAsync(int serverHandle, IReadOnlyList<int> itemHandles, CancellationToken ct = default);
|
||||
public Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(int serverHandle, IReadOnlyList<string> tagAddresses, CancellationToken ct = default);
|
||||
public Task<IReadOnlyList<SubscribeResult>> UnsubscribeBulkAsync(int serverHandle, IReadOnlyList<int> itemHandles, CancellationToken ct = default);
|
||||
public Task WriteAsync(int serverHandle, int itemHandle, MxValue value, int userId, CancellationToken ct = default);
|
||||
public IAsyncEnumerable<MxEvent> StreamEventsAsync(CancellationToken ct = default);
|
||||
public Task CloseAsync(CancellationToken ct = default);
|
||||
}
|
||||
```
|
||||
|
||||
Generated protobuf types should remain available under a generated namespace.
|
||||
Handwritten wrappers should not hide raw replies.
|
||||
|
||||
## Options
|
||||
|
||||
```csharp
|
||||
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 MxGatewayClientRetryOptions Retry { get; init; } = new();
|
||||
public ILoggerFactory? LoggerFactory { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
The .NET client applies a bounded Polly retry policy only to idempotent calls:
|
||||
`CloseSession` and diagnostic `Invoke` commands such as `Ping`,
|
||||
`GetSessionState`, and `GetWorkerInfo`. It does not retry `OpenSession`, event
|
||||
streams, writes, secured writes, authentication, registration, item management,
|
||||
or subscription changes because those calls can partially succeed in MXAccess.
|
||||
|
||||
API key may be loaded from `MXGATEWAY_API_KEY` by the CLI, not implicitly by the
|
||||
library constructor unless a helper explicitly says it does that.
|
||||
|
||||
## Auth Interceptor
|
||||
|
||||
Use a gRPC call credentials/interceptor layer to attach:
|
||||
|
||||
```text
|
||||
authorization: Bearer <api key>
|
||||
```
|
||||
|
||||
The interceptor must redact the key in logs and exceptions.
|
||||
|
||||
## Streaming
|
||||
|
||||
Expose `StreamEventsAsync` as `IAsyncEnumerable<MxEvent>`. On cancellation,
|
||||
cancel the gRPC stream and surface `OperationCanceledException` only when the
|
||||
caller initiated cancellation.
|
||||
|
||||
Do not reorder events.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Recommended exceptions:
|
||||
|
||||
```csharp
|
||||
MxGatewayException
|
||||
MxGatewayAuthenticationException
|
||||
MxGatewayAuthorizationException
|
||||
MxGatewaySessionException
|
||||
MxGatewayWorkerException
|
||||
MxGatewayCommandException
|
||||
MxAccessException
|
||||
```
|
||||
|
||||
For command replies that include MXAccess HRESULT/status, prefer returning the
|
||||
reply and exposing helper methods:
|
||||
|
||||
```csharp
|
||||
reply.EnsureProtocolSuccess();
|
||||
reply.EnsureMxAccessSuccess();
|
||||
```
|
||||
|
||||
## Test CLI
|
||||
|
||||
Project: `MxGateway.Client.Cli`.
|
||||
|
||||
Command examples:
|
||||
|
||||
```powershell
|
||||
mxgw-dotnet version
|
||||
mxgw-dotnet smoke --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --item TestChildObject.TestInt
|
||||
mxgw-dotnet stream-events --session-id <id> --json
|
||||
mxgw-dotnet write --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123
|
||||
```
|
||||
|
||||
The CLI should use `System.CommandLine` or a similarly testable parser. JSON
|
||||
output should be deterministic and redact API keys.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Use an in-process fake gRPC service with `Grpc.AspNetCore.Server` test host or
|
||||
mock the generated client behind an internal interface.
|
||||
|
||||
Required tests:
|
||||
|
||||
- auth metadata is attached,
|
||||
- API key is redacted,
|
||||
- options build plaintext and TLS channels correctly,
|
||||
- `RegisterAsync` builds the right command payload,
|
||||
- `AddItem2Async` includes context,
|
||||
- `WriteAsync` converts scalar and array values,
|
||||
- command reply status helpers preserve MXAccess HRESULT,
|
||||
- `StreamEventsAsync` yields ordered events,
|
||||
- stream cancellation disposes the call,
|
||||
- CLI parsing and JSON output.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Use xUnit traits or categories. Skip unless:
|
||||
|
||||
```text
|
||||
MXGATEWAY_INTEGRATION=1
|
||||
MXGATEWAY_ENDPOINT=<endpoint>
|
||||
MXGATEWAY_API_KEY=<key>
|
||||
MXGATEWAY_TEST_ITEM=<item>
|
||||
```
|
||||
|
||||
Integration smoke should open, register, add, advise, stream for bounded time,
|
||||
and close.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [C# Style Guide](../../docs/style-guides/CSharpStyleGuide.md)
|
||||
@@ -164,6 +164,19 @@ foreach (GalaxyObject galaxyObject in objects)
|
||||
}
|
||||
```
|
||||
|
||||
Use `DiscoverHierarchyOptions` to request a server-side slice without pulling
|
||||
the full Galaxy:
|
||||
|
||||
```csharp
|
||||
IReadOnlyList<GalaxyObject> pumps = await repository.DiscoverHierarchyAsync(
|
||||
new DiscoverHierarchyOptions
|
||||
{
|
||||
RootContainedPath = "Area1/Line3",
|
||||
TagNameGlob = "Pump_*",
|
||||
IncludeAttributes = false,
|
||||
});
|
||||
```
|
||||
|
||||
The CLI exposes the same operations:
|
||||
|
||||
```powershell
|
||||
@@ -230,5 +243,5 @@ dotnet run --project clients/dotnet/MxGateway.Client.Cli -- smoke --endpoint $en
|
||||
## Related Documentation
|
||||
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Client Proto Generation](../../docs/client-proto-generation.md)
|
||||
- [.NET Client Detailed Design](../../docs/clients-dotnet-csharp-design.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [.NET Client Detailed Design](./DotnetClientDesign.md)
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
# Go Client Detailed Design
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide an idiomatic Go client module for MXAccess Gateway, plus a test CLI and
|
||||
unit tests. The Go client should be suitable for services and command-line
|
||||
automation.
|
||||
|
||||
Follow the [Go Style Guide](../../docs/style-guides/GoStyleGuide.md) for handwritten
|
||||
code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for
|
||||
generated contract inputs.
|
||||
|
||||
## Module Layout
|
||||
|
||||
Recommended layout:
|
||||
|
||||
```text
|
||||
clients/go/
|
||||
go.mod
|
||||
mxgateway/
|
||||
client.go
|
||||
session.go
|
||||
options.go
|
||||
auth.go
|
||||
values.go
|
||||
errors.go
|
||||
internal/generated/
|
||||
mxaccess_gateway.pb.go
|
||||
mxaccess_gateway_grpc.pb.go
|
||||
cmd/mxgw-go/
|
||||
main.go
|
||||
tests/
|
||||
```
|
||||
|
||||
Generated code should come from `protoc` plus:
|
||||
|
||||
- `protoc-gen-go`
|
||||
- `protoc-gen-go-grpc`
|
||||
|
||||
## Library API
|
||||
|
||||
Suggested API:
|
||||
|
||||
```go
|
||||
type Client struct {
|
||||
// owns grpc.ClientConn
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Endpoint string
|
||||
APIKey string
|
||||
Plaintext bool
|
||||
CACertFile string
|
||||
ServerNameOverride string
|
||||
DialTimeout time.Duration
|
||||
CallTimeout time.Duration
|
||||
}
|
||||
|
||||
func Dial(ctx context.Context, opts Options) (*Client, error)
|
||||
func (c *Client) OpenSession(ctx context.Context, opts OpenSessionOptions) (*Session, error)
|
||||
func (c *Client) Invoke(ctx context.Context, req *pb.MxCommandRequest) (*pb.MxCommandReply, error)
|
||||
func (c *Client) Close() error
|
||||
```
|
||||
|
||||
Session:
|
||||
|
||||
```go
|
||||
type Session struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (s *Session) Register(ctx context.Context, clientName string) (int32, error)
|
||||
func (s *Session) Unregister(ctx context.Context, serverHandle int32) error
|
||||
func (s *Session) AddItem(ctx context.Context, serverHandle int32, item string) (int32, error)
|
||||
func (s *Session) AddItem2(ctx context.Context, serverHandle int32, item, context string) (int32, error)
|
||||
func (s *Session) Advise(ctx context.Context, serverHandle, itemHandle int32) error
|
||||
func (s *Session) AddItemBulk(ctx context.Context, serverHandle int32, tagAddresses []string) ([]*pb.SubscribeResult, error)
|
||||
func (s *Session) AdviseItemBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*pb.SubscribeResult, error)
|
||||
func (s *Session) RemoveItemBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*pb.SubscribeResult, error)
|
||||
func (s *Session) UnAdviseItemBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*pb.SubscribeResult, error)
|
||||
func (s *Session) SubscribeBulk(ctx context.Context, serverHandle int32, tagAddresses []string) ([]*pb.SubscribeResult, error)
|
||||
func (s *Session) UnsubscribeBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*pb.SubscribeResult, error)
|
||||
func (s *Session) Write(ctx context.Context, serverHandle, itemHandle int32, value Value, userID int32) error
|
||||
func (s *Session) Events(ctx context.Context) (<-chan EventResult, error)
|
||||
func (s *Session) Close(ctx context.Context) error
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Use a unary and stream interceptor to attach:
|
||||
|
||||
```text
|
||||
authorization: Bearer <api key>
|
||||
```
|
||||
|
||||
The interceptor should use `metadata.AppendToOutgoingContext` or call options.
|
||||
Do not print API keys in errors.
|
||||
|
||||
## TLS
|
||||
|
||||
Support:
|
||||
|
||||
- `credentials/insecure` for local plaintext,
|
||||
- `credentials.NewClientTLSFromFile`,
|
||||
- custom `tls.Config` for advanced callers.
|
||||
|
||||
## Streaming
|
||||
|
||||
`Events(ctx)` should return a receive channel of:
|
||||
|
||||
```go
|
||||
type EventResult struct {
|
||||
Event *pb.MxEvent
|
||||
Err error
|
||||
}
|
||||
```
|
||||
|
||||
The receive goroutine exits on stream end, context cancellation, or error. The
|
||||
channel should be closed exactly once. Do not reorder events.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Expose typed errors:
|
||||
|
||||
```go
|
||||
type GatewayError struct { ... }
|
||||
type CommandError struct { ... }
|
||||
type MxAccessError struct { ... }
|
||||
```
|
||||
|
||||
Use `errors.Is` / `errors.As` support. Preserve raw protobuf replies on command
|
||||
errors.
|
||||
|
||||
## Test CLI
|
||||
|
||||
Binary: `mxgw-go`.
|
||||
|
||||
Recommended commands:
|
||||
|
||||
```text
|
||||
mxgw-go version
|
||||
mxgw-go smoke --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --item TestChildObject.TestInt
|
||||
mxgw-go write --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123
|
||||
mxgw-go stream-events --session-id <id> --json
|
||||
```
|
||||
|
||||
Recommended CLI library:
|
||||
|
||||
- standard `flag` for minimalism, or
|
||||
- Cobra if subcommand ergonomics matter.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Use `bufconn` for in-memory gRPC tests.
|
||||
|
||||
Required tests:
|
||||
|
||||
- auth interceptor on unary calls,
|
||||
- auth interceptor on streaming calls,
|
||||
- plaintext and TLS dial options,
|
||||
- command helper request construction,
|
||||
- value conversion,
|
||||
- status conversion,
|
||||
- typed error wrapping,
|
||||
- stream channel closes on cancellation,
|
||||
- late stream error propagation,
|
||||
- CLI JSON redaction.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Use Go build tags or environment skip:
|
||||
|
||||
```text
|
||||
MXGATEWAY_INTEGRATION=1
|
||||
```
|
||||
|
||||
Integration test should run `OpenSession`, `Register`, `AddItem`, `Advise`,
|
||||
bounded `StreamEvents`, and `CloseSession`.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Go Style Guide](../../docs/style-guides/GoStyleGuide.md)
|
||||
@@ -3,7 +3,7 @@
|
||||
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
|
||||
`../../docs/ClientProtoGeneration.md` so gateway and client contracts stay in
|
||||
sync.
|
||||
|
||||
## Layout
|
||||
@@ -28,7 +28,7 @@ Run generation after the shared `.proto` files or the Go output path changes:
|
||||
./generate-proto.ps1
|
||||
```
|
||||
|
||||
The script uses the tool paths recorded in `../../docs/toolchain-links.md`.
|
||||
The script uses the tool paths recorded in `../../docs/ToolchainLinks.md`.
|
||||
|
||||
## Build And Test
|
||||
|
||||
@@ -209,5 +209,5 @@ go run ./cmd/mxgw-go smoke -endpoint $env:MXGATEWAY_ENDPOINT -plaintext -api-key
|
||||
## Related Documentation
|
||||
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Client Proto Generation](../../docs/client-proto-generation.md)
|
||||
- [Go Client Detailed Design](../../docs/clients-golang-design.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Go Client Detailed Design](./GoClientDesign.md)
|
||||
|
||||
@@ -9,13 +9,13 @@ $protoc = 'C:\Users\dohertj2\AppData\Local\Microsoft\WinGet\Packages\Google.Prot
|
||||
$goPluginPath = 'C:\Users\dohertj2\go\bin'
|
||||
|
||||
if (-not (Test-Path $protoc)) {
|
||||
throw "protoc was not found at $protoc. See docs/toolchain-links.md."
|
||||
throw "protoc was not found at $protoc. See docs/ToolchainLinks.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."
|
||||
throw "$pluginName was not found at $pluginPath. See docs/ToolchainLinks.md."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# Java Client Detailed Design
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide a Java client library for MXAccess Gateway, plus a test CLI and unit
|
||||
tests. The Java client should work for JVM services and operator tooling.
|
||||
|
||||
Follow the [Java Style Guide](../../docs/style-guides/JavaStyleGuide.md) for handwritten
|
||||
code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for
|
||||
generated contract inputs.
|
||||
|
||||
## Build Layout
|
||||
|
||||
Recommended Gradle multi-project layout:
|
||||
|
||||
```text
|
||||
clients/java/
|
||||
settings.gradle
|
||||
build.gradle
|
||||
src/main/generated/
|
||||
mxgateway-client/
|
||||
build.gradle
|
||||
src/main/java/com/dohertylan/mxgateway/client/
|
||||
src/test/java/com/dohertylan/mxgateway/client/
|
||||
mxgateway-cli/
|
||||
build.gradle
|
||||
src/main/java/com/dohertylan/mxgateway/cli/
|
||||
```
|
||||
|
||||
Alternative Maven layout is acceptable if the repo standardizes on Maven.
|
||||
|
||||
Target Java:
|
||||
|
||||
- Java 21 recommended.
|
||||
- The Gradle scaffold uses the Java 21 toolchain for compilation and tests.
|
||||
|
||||
Expected dependencies:
|
||||
|
||||
- `grpc-netty-shaded`
|
||||
- `grpc-protobuf`
|
||||
- `grpc-stub`
|
||||
- `protobuf-java`
|
||||
- `picocli`
|
||||
- `junit-jupiter`
|
||||
- `mockito` if needed
|
||||
|
||||
## Library API
|
||||
|
||||
Suggested API:
|
||||
|
||||
```java
|
||||
public final class MxGatewayClient implements AutoCloseable {
|
||||
public static MxGatewayClient connect(MxGatewayClientOptions options);
|
||||
public MxGatewaySession openSession(OpenSessionOptions options);
|
||||
public MxCommandReply invoke(MxCommandRequest request);
|
||||
public CompletableFuture<MxCommandReply> invokeAsync(MxCommandRequest request);
|
||||
public void close();
|
||||
}
|
||||
|
||||
public final class MxGatewaySession implements AutoCloseable {
|
||||
public String sessionId();
|
||||
public int register(String clientName);
|
||||
public void unregister(int serverHandle);
|
||||
public int addItem(int serverHandle, String item);
|
||||
public int addItem2(int serverHandle, String item, String context);
|
||||
public void advise(int serverHandle, int itemHandle);
|
||||
public List<SubscribeResult> addItemBulk(int serverHandle, List<String> tagAddresses);
|
||||
public List<SubscribeResult> adviseItemBulk(int serverHandle, List<Integer> itemHandles);
|
||||
public List<SubscribeResult> removeItemBulk(int serverHandle, List<Integer> itemHandles);
|
||||
public List<SubscribeResult> unAdviseItemBulk(int serverHandle, List<Integer> itemHandles);
|
||||
public List<SubscribeResult> subscribeBulk(int serverHandle, List<String> tagAddresses);
|
||||
public List<SubscribeResult> unsubscribeBulk(int serverHandle, List<Integer> itemHandles);
|
||||
public void write(int serverHandle, int itemHandle, MxValue value, int userId);
|
||||
public Iterator<MxEvent> streamEvents();
|
||||
public void streamEventsAsync(StreamObserver<MxEvent> observer);
|
||||
public void close();
|
||||
}
|
||||
```
|
||||
|
||||
Expose generated protobuf classes for callers that need raw access.
|
||||
|
||||
## Options
|
||||
|
||||
```java
|
||||
public final class MxGatewayClientOptions {
|
||||
URI endpoint;
|
||||
String apiKey;
|
||||
boolean plaintext;
|
||||
Path caCertificatePath;
|
||||
String serverNameOverride;
|
||||
Duration connectTimeout;
|
||||
Duration callTimeout;
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Use a gRPC `ClientInterceptor` to attach:
|
||||
|
||||
```text
|
||||
authorization: Bearer <api key>
|
||||
```
|
||||
|
||||
Redact API keys in `toString`, logs, and CLI output.
|
||||
|
||||
## TLS
|
||||
|
||||
Support:
|
||||
|
||||
- plaintext for local development,
|
||||
- TLS with default JVM trust store,
|
||||
- custom CA certificate file,
|
||||
- server name override for test environments.
|
||||
|
||||
## Streaming
|
||||
|
||||
Support both:
|
||||
|
||||
- blocking iterator for simple CLIs,
|
||||
- async `StreamObserver` for services.
|
||||
|
||||
Do not reorder events. Stream cancellation should call `ClientCall.cancel`.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Recommended exceptions:
|
||||
|
||||
```java
|
||||
MxGatewayException
|
||||
MxGatewayAuthenticationException
|
||||
MxGatewayAuthorizationException
|
||||
MxGatewaySessionException
|
||||
MxGatewayWorkerException
|
||||
MxGatewayCommandException
|
||||
MxAccessException
|
||||
```
|
||||
|
||||
`MxGatewayCommandException` should carry the raw command reply when available.
|
||||
|
||||
## Test CLI
|
||||
|
||||
Binary wrapper name:
|
||||
|
||||
```text
|
||||
mxgw-java
|
||||
```
|
||||
|
||||
Use `picocli`.
|
||||
|
||||
Commands:
|
||||
|
||||
```text
|
||||
mxgw-java version
|
||||
mxgw-java smoke --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --item TestChildObject.TestInt
|
||||
mxgw-java stream-events --session-id <id> --json
|
||||
mxgw-java write --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123
|
||||
```
|
||||
|
||||
JSON output can use Jackson or protobuf JSON formatting. Keep it deterministic.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Use JUnit 5.
|
||||
|
||||
Use `InProcessServerBuilder` and `InProcessChannelBuilder` for fake gRPC tests.
|
||||
|
||||
Required tests:
|
||||
|
||||
- auth interceptor attaches metadata,
|
||||
- key redaction,
|
||||
- plaintext and TLS channel setup,
|
||||
- request construction helpers,
|
||||
- value conversion,
|
||||
- status/error mapping,
|
||||
- blocking event stream iteration,
|
||||
- async stream observer cancellation,
|
||||
- CLI parsing,
|
||||
- JSON output.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Skip unless:
|
||||
|
||||
```text
|
||||
MXGATEWAY_INTEGRATION=1
|
||||
```
|
||||
|
||||
Use JUnit assumptions. Integration flow should open, register, add, advise,
|
||||
stream for bounded time, and close.
|
||||
|
||||
## Packaging
|
||||
|
||||
Publish library and CLI separately:
|
||||
|
||||
- `mxgateway-client` jar,
|
||||
- `mxgateway-cli` runnable distribution.
|
||||
|
||||
Generated protobuf code should be produced during the build from shared proto
|
||||
files and should not be hand-edited.
|
||||
|
||||
## Current Build
|
||||
|
||||
Run the Java scaffold checks from `clients/java`:
|
||||
|
||||
```powershell
|
||||
gradle test
|
||||
```
|
||||
|
||||
The `mxgateway-client` project generates the gateway and worker protobuf/gRPC
|
||||
bindings into `src/main/generated`, compiles the generated contracts, and runs
|
||||
JUnit 5 tests. The `mxgateway-cli` project builds a Picocli-based `mxgw-java`
|
||||
entry point for later command implementation.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Java Style Guide](../../docs/style-guides/JavaStyleGuide.md)
|
||||
@@ -223,6 +223,6 @@ gradle :mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --pla
|
||||
## Related Documentation
|
||||
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Client Proto Generation](../../docs/client-proto-generation.md)
|
||||
- [Java Client Detailed Design](../../docs/clients-java-design.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Java Client Detailed Design](./JavaClientDesign.md)
|
||||
- [Java Style Guide](../../docs/style-guides/JavaStyleGuide.md)
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
# Python Client Detailed Design
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide an async Python client package for MXAccess Gateway, plus a test CLI and
|
||||
unit tests. The Python client should be useful for automation, diagnostics, and
|
||||
test harnesses.
|
||||
|
||||
Follow the [Python Style Guide](../../docs/style-guides/PythonStyleGuide.md) for
|
||||
handwritten code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md)
|
||||
for generated contract inputs.
|
||||
|
||||
## Package Layout
|
||||
|
||||
Recommended layout:
|
||||
|
||||
```text
|
||||
clients/python/
|
||||
pyproject.toml
|
||||
src/mxgateway/
|
||||
__init__.py
|
||||
client.py
|
||||
session.py
|
||||
options.py
|
||||
auth.py
|
||||
values.py
|
||||
errors.py
|
||||
generated/
|
||||
src/mxgateway_cli/
|
||||
__main__.py
|
||||
commands.py
|
||||
tests/
|
||||
```
|
||||
|
||||
Expected dependencies:
|
||||
|
||||
- `grpcio`
|
||||
- `grpcio-tools`
|
||||
- `protobuf`
|
||||
- `click` or `typer`
|
||||
- `pytest`
|
||||
- `pytest-asyncio`
|
||||
|
||||
## Library API
|
||||
|
||||
Use async-first API. A sync wrapper can be added later if needed.
|
||||
|
||||
Suggested API:
|
||||
|
||||
```python
|
||||
client = await GatewayClient.connect(
|
||||
endpoint="localhost:5000",
|
||||
api_key=api_key,
|
||||
plaintext=True,
|
||||
)
|
||||
|
||||
session = await client.open_session()
|
||||
server = await session.register("python-client")
|
||||
item = await session.add_item(server, "TestChildObject.TestInt")
|
||||
await session.advise(server, item)
|
||||
|
||||
async for event in session.stream_events():
|
||||
...
|
||||
|
||||
await session.close()
|
||||
await client.close()
|
||||
```
|
||||
|
||||
Classes:
|
||||
|
||||
```python
|
||||
class GatewayClient:
|
||||
@classmethod
|
||||
async def connect(cls, options: ClientOptions) -> "GatewayClient": ...
|
||||
async def open_session(self, options: OpenSessionOptions | None = None) -> "Session": ...
|
||||
async def invoke(self, request: MxCommandRequest) -> MxCommandReply: ...
|
||||
async def close(self) -> None: ...
|
||||
|
||||
class Session:
|
||||
session_id: str
|
||||
async def register(self, client_name: str) -> int: ...
|
||||
async def add_item(self, server_handle: int, item: str) -> int: ...
|
||||
async def add_item2(self, server_handle: int, item: str, context: str) -> int: ...
|
||||
async def advise(self, server_handle: int, item_handle: int) -> None: ...
|
||||
async def add_item_bulk(self, server_handle: int, tag_addresses: Sequence[str]) -> list[SubscribeResult]: ...
|
||||
async def advise_item_bulk(self, server_handle: int, item_handles: Sequence[int]) -> list[SubscribeResult]: ...
|
||||
async def remove_item_bulk(self, server_handle: int, item_handles: Sequence[int]) -> list[SubscribeResult]: ...
|
||||
async def unadvise_item_bulk(self, server_handle: int, item_handles: Sequence[int]) -> list[SubscribeResult]: ...
|
||||
async def subscribe_bulk(self, server_handle: int, tag_addresses: Sequence[str]) -> list[SubscribeResult]: ...
|
||||
async def unsubscribe_bulk(self, server_handle: int, item_handles: Sequence[int]) -> list[SubscribeResult]: ...
|
||||
async def write(self, server_handle: int, item_handle: int, value: MxValueInput, user_id: int = 0) -> None: ...
|
||||
async def stream_events(self) -> AsyncIterator[MxEvent]: ...
|
||||
async def close(self) -> None: ...
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Use gRPC metadata:
|
||||
|
||||
```python
|
||||
metadata = (("authorization", f"Bearer {api_key}"),)
|
||||
```
|
||||
|
||||
Provide a metadata helper that all unary and streaming calls use. Redact API
|
||||
keys in exceptions and CLI output.
|
||||
|
||||
## TLS
|
||||
|
||||
Support:
|
||||
|
||||
- insecure channel for local development,
|
||||
- TLS channel with default roots,
|
||||
- custom root certificate file.
|
||||
|
||||
## Streaming
|
||||
|
||||
Expose `stream_events` as an async iterator. Canceling the task should cancel
|
||||
the gRPC stream.
|
||||
|
||||
Do not hide stream errors. Convert common auth/session errors into typed
|
||||
exceptions.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Define typed exceptions:
|
||||
|
||||
```python
|
||||
MxGatewayError
|
||||
MxGatewayTransportError
|
||||
MxGatewayAuthenticationError
|
||||
MxGatewayAuthorizationError
|
||||
MxGatewaySessionError
|
||||
MxGatewayWorkerError
|
||||
MxGatewayCommandError
|
||||
MxAccessError
|
||||
```
|
||||
|
||||
`MxGatewayCommandError` should include the raw protobuf reply when available.
|
||||
|
||||
## Test CLI
|
||||
|
||||
Entry point:
|
||||
|
||||
```text
|
||||
mxgw-py
|
||||
```
|
||||
|
||||
Recommended commands:
|
||||
|
||||
```text
|
||||
mxgw-py version
|
||||
mxgw-py smoke --endpoint localhost:5000 --api-key-env MXGATEWAY_API_KEY --plaintext --item TestChildObject.TestInt
|
||||
mxgw-py stream-events --session-id <id> --json
|
||||
mxgw-py write --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123
|
||||
```
|
||||
|
||||
Use `click` or `typer`. JSON output should be stable for test automation.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Use `pytest` and `pytest-asyncio`.
|
||||
|
||||
Use fake generated stubs or an in-process test gRPC server where practical.
|
||||
|
||||
Required tests:
|
||||
|
||||
- API key metadata injection,
|
||||
- API key redaction,
|
||||
- insecure and TLS channel option construction,
|
||||
- request construction for method helpers,
|
||||
- value conversion from Python values,
|
||||
- status/error mapping,
|
||||
- async event iteration,
|
||||
- stream cancellation,
|
||||
- CLI parsing,
|
||||
- JSON output.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Skip unless:
|
||||
|
||||
```text
|
||||
MXGATEWAY_INTEGRATION=1
|
||||
```
|
||||
|
||||
Use bounded smoke flow and always attempt `close_session` in `finally`.
|
||||
|
||||
## Packaging
|
||||
|
||||
Use `pyproject.toml`. Publishable package name should be stable, for example:
|
||||
|
||||
```text
|
||||
mxaccess-gateway-client
|
||||
```
|
||||
|
||||
Generated protobuf code should be regenerated through a documented command, not
|
||||
edited by hand.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Python Style Guide](../../docs/style-guides/PythonStyleGuide.md)
|
||||
@@ -3,7 +3,7 @@
|
||||
The Python client package contains generated MXAccess Gateway protobuf
|
||||
bindings, the async `mxgateway` package, and the `mxgw-py` test CLI. The
|
||||
package uses the shared proto inputs documented in
|
||||
`../../docs/client-proto-generation.md` so gateway and client contracts stay in
|
||||
`../../docs/ClientProtoGeneration.md` so gateway and client contracts stay in
|
||||
sync.
|
||||
|
||||
## Layout
|
||||
@@ -31,7 +31,7 @@ changes:
|
||||
```
|
||||
|
||||
The script uses the Python tool path recorded in
|
||||
`../../docs/toolchain-links.md`.
|
||||
`../../docs/ToolchainLinks.md`.
|
||||
|
||||
## Build And Test
|
||||
|
||||
@@ -219,5 +219,5 @@ mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGAT
|
||||
## Related Documentation
|
||||
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Client Proto Generation](../../docs/client-proto-generation.md)
|
||||
- [Python Client Detailed Design](../../docs/clients-python-design.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Python Client Detailed Design](./PythonClientDesign.md)
|
||||
|
||||
@@ -7,7 +7,7 @@ $outputRoot = Join-Path $PSScriptRoot 'src\mxgateway\generated'
|
||||
$python = 'C:\Users\dohertj2\AppData\Local\Programs\Python\Python312\python.exe'
|
||||
|
||||
if (-not (Test-Path $python)) {
|
||||
throw "Python was not found at $python. See docs/toolchain-links.md."
|
||||
throw "Python was not found at $python. See docs/ToolchainLinks.md."
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null
|
||||
|
||||
@@ -4,7 +4,7 @@ The Rust client workspace contains the MXAccess Gateway client library, a
|
||||
test CLI, and tests for generated contract wiring plus wrapper behavior. The
|
||||
library uses
|
||||
the shared protobuf inputs documented in
|
||||
`../../docs/client-proto-generation.md` so the Rust bindings compile against
|
||||
`../../docs/ClientProtoGeneration.md` so the Rust bindings compile against
|
||||
the same public gateway and worker contracts as the server.
|
||||
|
||||
## Layout
|
||||
@@ -36,7 +36,7 @@ cargo clippy --workspace --all-targets -- -D warnings
|
||||
```
|
||||
|
||||
The build script uses `protoc` from `PATH` or the Windows path recorded in
|
||||
`../../docs/toolchain-links.md`.
|
||||
`../../docs/ToolchainLinks.md`.
|
||||
|
||||
## Packaging
|
||||
|
||||
@@ -184,6 +184,6 @@ cargo run -p mxgw-cli -- smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --
|
||||
## Related Documentation
|
||||
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Client Proto Generation](../../docs/client-proto-generation.md)
|
||||
- [Rust Client Detailed Design](../../docs/clients-rust-design.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Rust Client Detailed Design](./RustClientDesign.md)
|
||||
- [Rust Style Guide](../../docs/style-guides/RustStyleGuide.md)
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
# Rust Client Detailed Design
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide an async Rust client crate for MXAccess Gateway, plus a test CLI and
|
||||
unit tests. The Rust client should use `tonic` and `tokio`.
|
||||
|
||||
Follow the [Rust Style Guide](../../docs/style-guides/RustStyleGuide.md) for handwritten
|
||||
code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for
|
||||
generated contract inputs.
|
||||
|
||||
## Crate Layout
|
||||
|
||||
Recommended layout:
|
||||
|
||||
```text
|
||||
clients/rust/
|
||||
Cargo.toml
|
||||
build.rs
|
||||
crates/
|
||||
mxgateway-client/
|
||||
src/lib.rs
|
||||
src/client.rs
|
||||
src/session.rs
|
||||
src/options.rs
|
||||
src/auth.rs
|
||||
src/value.rs
|
||||
src/error.rs
|
||||
src/generated/
|
||||
mxgw-cli/
|
||||
src/main.rs
|
||||
tests/
|
||||
```
|
||||
|
||||
Expected dependencies:
|
||||
|
||||
- `tonic`
|
||||
- `prost`
|
||||
- `prost-types`
|
||||
- `tokio`
|
||||
- `tokio-stream`
|
||||
- `thiserror`
|
||||
- `clap`
|
||||
- `serde`
|
||||
- `serde_json`
|
||||
- `tracing`
|
||||
|
||||
## Library API
|
||||
|
||||
Suggested API:
|
||||
|
||||
```rust
|
||||
pub struct GatewayClient { /* tonic channel + generated client */ }
|
||||
|
||||
pub struct ClientOptions {
|
||||
pub endpoint: String,
|
||||
pub api_key: String,
|
||||
pub plaintext: bool,
|
||||
pub ca_file: Option<PathBuf>,
|
||||
pub server_name_override: Option<String>,
|
||||
pub connect_timeout: Duration,
|
||||
pub call_timeout: Duration,
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
pub async fn connect(options: ClientOptions) -> Result<Self, Error>;
|
||||
pub async fn open_session(&self, options: OpenSessionOptions) -> Result<Session, Error>;
|
||||
pub async fn invoke(&self, request: MxCommandRequest) -> Result<MxCommandReply, Error>;
|
||||
}
|
||||
```
|
||||
|
||||
Session:
|
||||
|
||||
```rust
|
||||
pub struct Session {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub async fn register(&self, client_name: &str) -> Result<i32, Error>;
|
||||
pub async fn add_item(&self, server_handle: i32, item: &str) -> Result<i32, Error>;
|
||||
pub async fn add_item2(&self, server_handle: i32, item: &str, context: &str) -> Result<i32, Error>;
|
||||
pub async fn advise(&self, server_handle: i32, item_handle: i32) -> Result<(), Error>;
|
||||
pub async fn add_item_bulk(&self, server_handle: i32, tag_addresses: Vec<String>) -> Result<Vec<SubscribeResult>, Error>;
|
||||
pub async fn advise_item_bulk(&self, server_handle: i32, item_handles: Vec<i32>) -> Result<Vec<SubscribeResult>, Error>;
|
||||
pub async fn remove_item_bulk(&self, server_handle: i32, item_handles: Vec<i32>) -> Result<Vec<SubscribeResult>, Error>;
|
||||
pub async fn un_advise_item_bulk(&self, server_handle: i32, item_handles: Vec<i32>) -> Result<Vec<SubscribeResult>, Error>;
|
||||
pub async fn subscribe_bulk(&self, server_handle: i32, tag_addresses: Vec<String>) -> Result<Vec<SubscribeResult>, Error>;
|
||||
pub async fn unsubscribe_bulk(&self, server_handle: i32, item_handles: Vec<i32>) -> Result<Vec<SubscribeResult>, Error>;
|
||||
pub async fn write(&self, server_handle: i32, item_handle: i32, value: MxValue, user_id: i32) -> Result<(), Error>;
|
||||
pub async fn events(&self) -> Result<impl Stream<Item = Result<MxEvent, Error>>, Error>;
|
||||
pub async fn close(&self) -> Result<(), Error>;
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Use a `tonic` interceptor or request extension layer to add:
|
||||
|
||||
```text
|
||||
authorization: Bearer <api key>
|
||||
```
|
||||
|
||||
Use `SecretString` or equivalent if a dependency is acceptable. Always redact
|
||||
API keys in `Debug` output.
|
||||
|
||||
## TLS
|
||||
|
||||
Support:
|
||||
|
||||
- plaintext channel for local development,
|
||||
- native or rustls TLS depending on project preference,
|
||||
- custom CA file,
|
||||
- domain override.
|
||||
|
||||
## Streaming
|
||||
|
||||
Expose event streams as a `Stream<Item = Result<MxEvent, Error>>`. Dropping the
|
||||
stream should cancel the underlying gRPC stream.
|
||||
|
||||
Do not buffer unboundedly in the client. If a helper channel is used, make it
|
||||
bounded.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Use `thiserror`:
|
||||
|
||||
```rust
|
||||
pub enum Error {
|
||||
Transport(tonic::transport::Error),
|
||||
Status(tonic::Status),
|
||||
Authentication(String),
|
||||
Authorization(String),
|
||||
Session(SessionError),
|
||||
Worker(WorkerError),
|
||||
Command(CommandError),
|
||||
MxAccess(MxAccessError),
|
||||
Timeout,
|
||||
Cancelled,
|
||||
}
|
||||
```
|
||||
|
||||
Preserve raw command replies in `CommandError` where applicable.
|
||||
|
||||
## Test CLI
|
||||
|
||||
Binary: `mxgw`.
|
||||
|
||||
Use `clap` derive.
|
||||
|
||||
Commands:
|
||||
|
||||
```text
|
||||
mxgw version
|
||||
mxgw smoke --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --item TestChildObject.TestInt
|
||||
mxgw stream-events --session-id <id> --json
|
||||
mxgw write --session-id <id> --server-handle 1 --item-handle 1 --type int32 --value 123
|
||||
```
|
||||
|
||||
JSON output should use `serde_json`.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Use a fake `tonic` server started on a local ephemeral port, or abstract the
|
||||
generated client behind a trait for unit tests.
|
||||
|
||||
Required tests:
|
||||
|
||||
- generated client compiles from proto,
|
||||
- auth metadata injection,
|
||||
- TLS/plaintext endpoint construction,
|
||||
- value conversion,
|
||||
- command request construction,
|
||||
- error mapping from `tonic::Status`,
|
||||
- event stream order,
|
||||
- stream cancellation,
|
||||
- CLI parsing,
|
||||
- JSON redaction.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Skip unless:
|
||||
|
||||
```text
|
||||
MXGATEWAY_INTEGRATION=1
|
||||
```
|
||||
|
||||
Use `tokio::test`. Run bounded smoke flow and ensure `CloseSession` is attempted
|
||||
with `drop` fallback docs, but do not rely on `Drop` for async close.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md)
|
||||
- [Client Proto Generation](../../docs/ClientProtoGeneration.md)
|
||||
- [Client Packaging](../../docs/ClientPackaging.md)
|
||||
- [Rust Style Guide](../../docs/style-guides/RustStyleGuide.md)
|
||||
Reference in New Issue
Block a user