[M3] mxaccess-nmx: high-level write/advise/un_advise wrappers (resolves F13)
Seven new high-level methods on NmxClient (port of cs:303-466). Each takes a GalaxyTagMetadata + typed WriteValue (re-exported from mxaccess-codec), builds the inner NMX body, wraps in NmxTransferEnvelope, and dispatches via the existing transfer_data opnum. Methods landed - write (cs:303-324) - write2 (cs:326-349, with explicit FILETIME timestamp) - write_secured2 (cs:351-380, dual user tokens via secured_write::resolve_observed_user_token; single-user secured = same id) - advise_supervisory (cs:382-399, ItemControl envelope) - send_observed_pre_advise_metadata (cs:401-420, hardcoded target platform/engine = (1, 1) per the .NET reference) - register_reference (cs:422-441, accepts caller-built NmxReferenceRegistrationMessage) - un_advise (cs:443-466, deliberately uses NmxTransferMessageKind::Write per cs:457 — the .NET reference's divergence from AdviseSupervisory's ItemControl envelope, preserved verbatim per CLAUDE.md unknown-bytes rule) Internal encode_*_transfer_body helpers extracted as pub(crate) fn for testability — mirrors the .NET reference's `internal static` shape. NmxClientError gained two new variants: Codec(CodecError) for metadata->reference-handle and value-encode failures, and UnsupportedDataType for the kind-resolution path. Cargo.toml: added mxaccess-galaxy as a dep on mxaccess-nmx. design/followups.md: F13 moved to Resolved. Test count delta: 459 -> 468 (+9 in mxaccess-nmx; 8 -> 17). Tests cover each encode helper standalone (envelope-kind + length checks) plus real-socket round-trip tests for write / advise_supervisory / send_observed_pre_advise_metadata. All four DoD gates green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+3
-5
@@ -66,11 +66,6 @@ move to `## Resolved` with a date + commit hash.
|
||||
**Why deferred:** `tiberius` is the recommended Rust SQL Server client; pulling it as a non-default dep means the `mxaccess-galaxy` crate keeps a slim default footprint (consumers can plug their own `Resolver` / `UserResolver` impl without dragging in TDS / native-tls / winauth). The actual `GalaxyRepositoryTagResolver` and `GalaxyRepositoryUserResolver` impls are short — they just bind the canonical SQL constants in `crate::sql` (`RESOLVE_SQL`, `BROWSE_SQL`, `USER_BY_GUID_SQL`, `USER_BY_NAME_SQL`) and translate `tiberius::Row` → typed `GalaxyTagMetadata` / `GalaxyUserProfile`.
|
||||
**Resolves when:** A `tiberius`-backed module lands behind the existing `galaxy-resolver` Cargo feature flag in `mxaccess-galaxy/Cargo.toml`. Live-probe gating: needs a Galaxy DB to verify against (`MX_GALAXY_DB` env var, populated by `tools/Setup-LiveProbeEnv.ps1`). The pure-Rust foundation (data types, parser, trait, SQL strings) is already in place — this is "fill in the backend" rather than "design the surface."
|
||||
|
||||
### F13 — `NmxClient` high-level write/advise/subscribe wrappers
|
||||
**Severity:** P1
|
||||
**Source:** M3 stream B, `crates/mxaccess-nmx/src/client.rs`
|
||||
**Why deferred:** The .NET `Write`, `Write2`, `WriteSecured2`, `AdviseSupervisory`, `SendObservedPreAdviseMetadata`, `RegisterReference`, and `UnAdvise` methods (`ManagedNmxService2Client.cs:303-466`) are short — each builds an inner NMX message via `mxaccess-codec` (`NmxWriteMessage`, `NmxItemControlMessage`, `NmxSecuredWrite2Message`, `NmxMetadataQueryMessage`, `NmxReferenceRegistrationMessage`), wraps it in an `NmxTransferEnvelope`, then calls `TransferData(...)`. They depend on `GalaxyTagMetadata` from M3 stream A (the Galaxy SQL resolver) for `PlatformId` / `EngineId` / `ToReferenceHandle(galaxyId)`.
|
||||
**Resolves when:** M3 stream A (`mxaccess-galaxy`) lands `GalaxyTagMetadata` (or an equivalent type) and `MxReferenceHandle` from `mxaccess-codec` is wired to it. At that point ~120 lines of wrappers go into `NmxClient` that delegate to the existing `transfer_data` opnum.
|
||||
|
||||
## Resolved
|
||||
|
||||
@@ -80,5 +75,8 @@ move to `## Resolved` with a date + commit hash.
|
||||
### F8 — `RpcError` is duplicated across `objref` and `pdu` modules
|
||||
**Resolved:** 2026-05-05 in this iteration's commit. `RpcError` was hoisted into the new shared `crate::error::RpcError` module as a single union of all wave 1 variants plus a generic `Decode { offset, reason: &'static str, buffer_len }` variant for the wave 2 ORPC parsers' one-off failures. `objref` and `pdu` re-export from there; M2 wave 2's `orpc`, `object_exporter`, and `rem_unknown` use it directly.
|
||||
|
||||
### F13 — `NmxClient` high-level write/advise/subscribe wrappers
|
||||
**Resolved:** 2026-05-05. All seven wrappers landed in `crates/mxaccess-nmx/src/client.rs`: `write`, `write2`, `write_secured2`, `advise_supervisory`, `send_observed_pre_advise_metadata`, `register_reference`, `un_advise`. Each takes a `GalaxyTagMetadata` + a typed `WriteValue` (re-exported from `mxaccess-codec`), builds the inner NMX body via `mxaccess-codec` (`write_message::encode` / `encode_timestamped` / `secured_write::encode` / `NmxItemControlMessage` / `NmxMetadataQueryMessage` / `NmxReferenceRegistrationMessage`), wraps in `NmxTransferEnvelope`, and routes through `transfer_data`. The pure-codec `encode_*_transfer_body` helpers are extracted as `pub(crate) fn` for testability, mirroring the .NET reference's `internal static` shape. `un_advise` preserves the .NET reference's quirky `NmxTransferMessageKind::Write` envelope (not `ItemControl`) per `cs:457`.
|
||||
|
||||
### F9 — `ObjectExporterClient.cs` ResolveOxid wrapper methods
|
||||
**Resolved:** 2026-05-05. Both portable methods land in `crates/mxaccess-rpc/src/object_exporter_client.rs`: `resolve_oxid_unauthenticated` (mirrors `cs:14-30`) and `resolve_oxid_with_managed_ntlm_packet_integrity` (mirrors `cs:66-81`). Each opens a TCP connection, binds to `IObjectExporter`, calls opnum 0 with the encoded request, and decodes the response — preferring `parse_resolve_oxid_result` then falling back to `parse_resolve_oxid_failure` for short stubs. The two SSPI flavours (`ResolveOxidWithNtlmConnect`, `ResolveOxidWithNtlmPacketIntegrity`) wrap .NET's `System.Net.Security.SspiClientContext` and are explicitly out of scope for the Rust port — that's a permanent skip, not a deferral.
|
||||
|
||||
Reference in New Issue
Block a user