Apply the ZB.MOM.WW. prefix to all gateway-side projects, folders,
.csproj/.sln contents, C# namespaces, using directives, generated proto
C# (csharp_namespace + checked-in generated files), InternalsVisibleTo
attributes, project-name string literals (LoadProject, .sln lookups,
worker exe paths, staticwebassets manifest), and the install/script/doc
references that point at any of the above. Migrate the solution from
.sln to .slnx via `dotnet sln migrate` and delete the old file.
External-runtime identifiers are intentionally NOT prefixed so external
configuration keeps working:
- GatewayMetrics.cs MeterName ("MxGateway.Server")
- DashboardAuthenticationDefaults Scheme/Policy ("MxGateway.Dashboard")
- GatewayRequestLoggingMiddleware logger category ("MxGateway.Request")
- StaRuntime thread name ("MxGateway.Worker.STA")
- appsettings.json root section "MxGateway" + env-var prefix
MxGateway__... and secret-name MxGateway:ApiKeyPepper
- C:\ProgramData\MxGateway\ data dir paths
Also fixes two tests that were not rename-related but became visible
while validating the rename:
- WorkerLiveMxAccessSmokeTests.ShutDownAsync: cancellation that the
gateway service correctly maps to RpcException(Cancelled) per gRPC
convention was being misclassified as a stream fault. Added a sibling
catch on RpcException with StatusCode.Cancelled.
- IntegrationTestEnvironment.ResolveRepositoryRoot: extracted IsRepositoryRoot
and made it accept either a .git marker OR a .sln/.slnx next to src/
so the worker-exe walker works in non-git working copies.
clients/proto/proto-inputs.json's protoRoot updated to point at
src/ZB.MOM.WW.MxGateway.Contracts/Protos.
Verified by `dotnet build` and a full `dotnet test` of the .slnx with
MXGATEWAY_RUN_LIVE_{MXACCESS,LDAP,GALAXY}_TESTS=1:
Tests: 472/472 pass
Worker.Tests: 280/280 pass (4 dev-rig [Fact(Skip=...)] skipped)
IntegrationTests: 18/18 pass
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
Client Libraries Detailed Design
Purpose
This document defines the shared design for official MXAccess Gateway gRPC clients. Each supported language should provide:
- a reusable client library,
- a test CLI built on that library,
- unit tests that run without a live gateway,
- optional integration tests against a live gateway.
Target client languages:
- .NET 10 C#
- Go
- Rust
- Python
- Java
Language-specific plans:
clients/dotnet/DotnetClientDesign.mdclients/go/GoClientDesign.mdclients/rust/RustClientDesign.mdclients/python/PythonClientDesign.mdclients/java/JavaClientDesign.md
Shared generation inputs:
docs/ClientProtoGeneration.mddocs/ClientBehaviorFixtures.mddocs/ClientPackaging.mdclients/proto/proto-inputs.json
Language style guides:
| Client | Style guide |
|---|---|
| .NET C# | C# Style Guide |
| Go | Go Style Guide |
| Rust | Rust Style Guide |
| Python | Python Style Guide |
| Java | Java Style Guide |
| Generated protobuf/gRPC code | Protobuf Style Guide |
Goals
Client libraries should make the gateway pleasant to consume without hiding MXAccess behavior.
Goals:
- expose sessions as first-class objects,
- support unary
OpenSession,CloseSession, andInvoke, - support server-streaming
StreamEvents, - attach API key auth metadata to every call,
- preserve gateway, worker, COM, HRESULT, and MXAccess status detail,
- provide method-specific command helpers,
- provide raw command escape hatches for parity work,
- provide deterministic test CLIs for smoke and integration testing,
- keep generated protobuf/gRPC code separate from handwritten wrappers.
Non-goals for v1:
- client-side reconnectable sessions,
- client-side event replay,
- client-side command batching,
- synthetic MXAccess events,
- hiding MXAccess handles behind opaque client-only handles.
Public Client Concepts
All languages should expose the same core concepts, using idiomatic naming:
- gateway client,
- session,
- command request,
- command reply,
- event stream,
- MXAccess event,
- MX value,
- MX status proxy,
- gateway error,
- client options.
The gateway session id and MXAccess handles must remain visible. The library may offer helper methods, but it must not invent alternate handle semantics.
Shared API Shape
Each language should support this conceptual API:
client = GatewayClient.connect(endpoint, apiKey, options)
session = client.openSession(options)
serverHandle = session.register(clientName)
itemHandle = session.addItem(serverHandle, itemReference)
session.advise(serverHandle, itemHandle)
events = session.streamEvents()
session.write(serverHandle, itemHandle, value, userId)
session.close()
client.close()
Each language should expose the gateway bulk subscription commands with idiomatic names:
session.addItemBulk(serverHandle, tagAddresses)
session.adviseItemBulk(serverHandle, itemHandles)
session.removeItemBulk(serverHandle, itemHandles)
session.unAdviseItemBulk(serverHandle, itemHandles)
session.subscribeBulk(serverHandle, tagAddresses)
session.unsubscribeBulk(serverHandle, itemHandles)
These methods send one Invoke request using the matching bulk command kind.
They return the gateway SubscribeResult list without inventing client-only
handles. SubscribeBulk performs AddItem then Advise per tag inside the
worker session. UnsubscribeBulk performs UnAdvise then RemoveItem per item
handle. Per-item failures are returned in SubscribeResult; transport,
gateway, and cancellation failures still use each language's normal error
surface.
Each library should also expose lower-level calls:
client.openSession(rawRequest)
client.closeSession(rawRequest)
client.invoke(rawCommandRequest)
client.streamEvents(rawStreamRequest)
Authentication
The gateway uses API key auth for v1. Clients should support:
authorization: Bearer mxgw_<key-id>_<secret>
Rules:
- Do not log API keys.
- Redact keys in CLI error output.
- Allow API key from command line, environment variable, or config object.
- Recommended environment variable:
MXGATEWAY_API_KEY. - Attach auth metadata to every unary and streaming call.
- Treat
UnauthenticatedandPermissionDenieddistinctly.
TLS
Clients should support:
- plaintext for local development,
- TLS with system roots,
- TLS with custom CA file,
- optional server name override for test environments.
Default should be secure for packaged production examples, but the test CLI may
default to plaintext when endpoint is localhost or 127.0.0.1.
Timeouts And Cancellation
Each client library should support:
- connect timeout,
- unary call timeout,
- command timeout passed to gateway when the public API supports it,
- stream cancellation,
- graceful session close timeout.
Language wrappers should map cancellation to the native ecosystem:
- .NET:
CancellationToken - Go:
context.Context - Rust:
tokiocancellation / dropped future plus explicit timeout - Python:
asynciotask cancellation and deadlines - Java:
Deadline,CompletableFuture, and stream cancellation
Canceling a client call does not imply the worker COM call was aborted. Client docs and errors must make that clear.
Error Model
Each client should distinguish:
- transport errors,
- authentication/authorization errors,
- gateway session errors,
- worker process/protocol errors,
- MXAccess command failures,
- COM HRESULT/status failures.
Generated gRPC errors should not be the only error surface. The wrapper should return rich command replies when the gateway reached MXAccess and MXAccess returned HRESULT/status information.
Recommended high-level error categories:
TransportError
AuthenticationError
AuthorizationError
SessionError
WorkerError
ProtocolError
CommandError
MxAccessError
TimeoutError
CancelledError
Values
Each language should provide ergonomic conversion helpers for MxValue:
- bool,
- int32,
- int64,
- float,
- double,
- string,
- timestamp,
- typed arrays,
- raw variant fallback.
The raw protobuf value should always remain accessible.
Do not lose raw variant metadata when conversion is incomplete. For CLI output, render both typed projection and raw metadata when present.
Events
Each client should expose event streaming as the idiomatic streaming primitive:
- .NET:
IAsyncEnumerable<MxEvent> - Go: receive loop over generated stream
- Rust:
Stream<Item = Result<MxEvent, Error>> - Python: async iterator
- Java: blocking iterator and async observer variants
Events must preserve gateway order. Libraries should not reorder, coalesce, or drop events by default.
Long-lived event streams do not inherit unary call deadlines. Clients apply the default call timeout to unary operations only, and streams run until the caller cancels them or an explicit stream timeout is configured.
The event surface must include:
OnDataChangeOnWriteCompleteOperationCompleteOnBufferedDataChange- terminal session fault when represented as a message
OperationComplete is forwarded only when native MXAccess raises it.
OnBufferedDataChange payload conversion may include raw metadata until live
multi-sample buffered payloads are fully validated.
Test CLI Contract
Each language should include a test CLI that exercises the library. The CLI is not the production gateway server.
Required commands:
version
ping
open-session
close-session
register
add-item
advise
stream-events
write
write2
smoke
Optional commands:
add-item2
add-buffered-item
set-buffered-update-interval
authenticate-user
write-secured
write-secured2
get-worker-info
metadata-query
Common CLI flags:
--endpoint <host:port or URL>
--api-key <key>
--api-key-env <name>
--plaintext
--tls
--ca-file <path>
--session-id <id>
--client-name <name>
--server-handle <int>
--item-handle <int>
--item <reference>
--context <context>
--value <value>
--type <mx type>
--timeout <duration>
--json
--verbose
The smoke command should:
- open a session,
- register a client name,
- add one item,
- advise it,
- optionally write a value,
- stream events for a bounded duration,
- close the session.
CLI output should support JSON for automated tests.
Unit Tests
Unit tests must run without a live gateway. Use fake gRPC services, mock transports, or generated test servers depending on language.
Shared behavior fixtures live in clients/proto/fixtures/behavior. Every
client should include tests that load the fixture manifest and verify wrapper
behavior against the common command reply, event stream, value conversion,
status conversion, auth error, and timeout/cancel cases.
Required unit test areas:
- options parsing,
- auth metadata injection,
- TLS/plaintext channel setup,
- method-specific request construction,
- value conversion,
- status conversion,
- command reply error mapping,
- stream event iteration,
- stream cancellation,
- timeout behavior,
- CLI argument parsing,
- CLI JSON output redaction of secrets.
Integration Tests
Integration tests are optional and should be opt-in. They may require a live gateway and installed MXAccess on the gateway host.
Recommended environment variables:
MXGATEWAY_ENDPOINT
MXGATEWAY_API_KEY
MXGATEWAY_TEST_ITEM
MXGATEWAY_TEST_CONTEXT
MXGATEWAY_TEST_WRITE_VALUE
MXGATEWAY_INTEGRATION=1
Integration tests should skip unless MXGATEWAY_INTEGRATION=1.
Repository Layout
Recommended top-level layout:
clients/
dotnet/
go/
rust/
python/
java/
Each client should contain:
src or package source
generated protobuf/grpc source
test CLI
unit tests
README.md
examples/
Generated code should be reproducible from src/ZB.MOM.WW.MxGateway.Contracts/Protos/.
Do not hand-edit generated code.
The stable client proto manifest defines the generated-code directories:
clients/dotnet/generated
clients/go/internal/generated
clients/rust/src/generated
clients/python/src/mxgateway/generated
clients/java/src/main/generated
Versioning
All clients should expose:
- client library version,
- supported gateway protocol version,
- generated protobuf version if available.
Version compatibility should be tested against protocol-version mismatch cases.
Documentation
Each client README should include:
- install instructions,
- minimal open/register/add/advise example,
- API key configuration,
- TLS configuration,
- CLI examples,
- integration test instructions,
- warning that canceling a client call does not abort an in-flight MXAccess COM call.