Skip to content

[WotCon] Add the WoT Connectivity registry client - #4142

Open
marcschier wants to merge 27 commits into
marcschier/wot-13-registryfrom
marcschier/wot-15-client
Open

[WotCon] Add the WoT Connectivity registry client#4142
marcschier wants to merge 27 commits into
marcschier/wot-13-registryfrom
marcschier/wot-15-client

Conversation

@marcschier

Copy link
Copy Markdown
Collaborator

PR 15 of a stacked split of integration PR #4093 into independently reviewable pieces.

This branch is stacked on #4139 (marcschier/wot-10-wotcon-model) and additionally has #4128 (marcschier/wot-01-core-types) and #4130 (marcschier/wot-09-xregistry-client) merged in so this client slice can build.

Those extra dependencies are intentional:

The extra diff from #4128 and #4130 disappears once those PRs land.

This PR adds the WoT Connectivity registry client surface for browsing and reading registry groups, resources and versions, uploading documents through FileTransfer, invoking registry methods, and registering the client through DI/builder helpers.

Adds the client-side surface for the WoT Connectivity registry: browsing and reading registry groups, resources and versions, uploading documents through FileTransfer, and invoking the registry methods.

WotRegistryClient derives from the shared XRegistryClient so the generic registry traversal is not reimplemented here, and it takes an explicit registry root so a server that hosts the registry somewhere other than the well-known location can still be used.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
Reverts the temporary client-only compile exclusions so the WotCon test project keeps the existing server, provider and hosting tests from the base branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
Copilot AI review requested due to automatic review settings July 31, 2026 10:05
@marcschier

Copy link
Copy Markdown
Collaborator Author

Follow-up: the initial PR 15 commit temporarily excluded the non-client WotCon tests from Opc.Ua.WotCon.Tests.csproj; that was wrong because it removed existing base coverage. I have restored the test project to match the base branch, merged the #4139 fixes for the DataTypeIds shadowing / predefined-node partitioning issues, and revalidated with the full WotCon test project intact.

Validation now passes:

  • dotnet build tests\Opc.Ua.WotCon.Tests\Opc.Ua.WotCon.Tests.csproj -c Release -p:CustomTestTarget=net10.0 -v:m — 0 warnings / 0 errors
  • dotnet test tests\Opc.Ua.WotCon.Tests\Opc.Ua.WotCon.Tests.csproj -c Release -p:CustomTestTarget=net10.0 --no-build — 396 passed, 0 failed, 0 skipped
  • dotnet build src\Opc.Ua.WotCon.Client\Opc.Ua.WotCon.Client.csproj -c Release -v:m — 0 warnings / 0 errors

@marcschier

Copy link
Copy Markdown
Collaborator Author

Confirmed after merging the fixed marcschier/wot-10-wotcon-model (a88b971f2): the earlier temporary WotCon test exclusions were wrong and have been reverted. tests\Opc.Ua.WotCon.Tests\Opc.Ua.WotCon.Tests.csproj is byte-identical to the base branch, with no Compile Remove entries and the server project references restored.

Validation with the full test project intact:

  • dotnet build tests\Opc.Ua.WotCon.Tests\Opc.Ua.WotCon.Tests.csproj -c Release -p:CustomTestTarget=net10.0 -v:m — 0 warnings / 0 errors
  • dotnet test tests\Opc.Ua.WotCon.Tests\Opc.Ua.WotCon.Tests.csproj -c Release -p:CustomTestTarget=net10.0 --no-build — 396 passed, 0 failed, 0 skipped
  • dotnet build src\Opc.Ua.WotCon.Client\Opc.Ua.WotCon.Client.csproj -c Release -v:m — 0 warnings / 0 errors

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds the WoT Connectivity 1.1 registry client surface (plus supporting xRegistry and core type enhancements) and wires it into DI/builder helpers, including comprehensive session-mock tests.

Changes:

  • Introduce WotRegistryClient + group/resource wrappers, plus a shared browse-path resolver and DI registration helpers.
  • Extend xRegistry client construction to accept an explicit registry root NodeId.
  • Add atomic file replacement to the IFileSystem abstraction and update built-in file system implementations + tests; enhance Variant/ExtensionObject structure decoding and related type-system tests.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tools/Opc.Ua.SourceGeneration/SourceGeneratorFileSystem.cs Implements new IFileSystem.Replace API as not supported for the source generator FS.
tests/Opc.Ua.XRegistry.Tests/XRegistryClientTests.cs Adds tests for explicit registry root NodeId behavior in XRegistryClient.
tests/Opc.Ua.WotCon.Tests/Hosting/OpcUaWotRegistryClientBuilderExtensionsTests.cs Adds DI/builder tests for AddWotRegistryClient registration and behavior.
tests/Opc.Ua.WotCon.Tests/Client/WotRegistrySessionMock.cs Adds an in-memory scripted ISession mock to test WoT registry behavior end-to-end.
tests/Opc.Ua.WotCon.Tests/Client/WotRegistryClientTests.cs Adds WoT registry client tests (browse resolution, uploads, refresh, lifecycle).
tests/Opc.Ua.WotCon.Tests/Client/WotRegistryBulkLoadTests.cs Adds tests for bulk document load ordering and failure propagation.
tests/Opc.Ua.WotCon.Tests/Client/WotConnectivityClientTests.cs Updates/extends tests for connectivity client behavior (browse-path helper usage).
tests/Opc.Ua.Types.Tests/Utils/FileSystem/AtomicFileReplaceTests.cs Adds tests validating atomic replace semantics across file systems.
tests/Opc.Ua.Types.Tests/Nodes/TypeTableTests.cs Adds coverage for type/encoding equivalence handling.
tests/Opc.Ua.Types.Tests/BuiltIn/VariantCoverageTests.cs Adds coverage for new Variant.TryGetStructure(context, ...) overloads.
tests/Opc.Ua.Types.Tests/BuiltIn/ExtensionObjectTests.cs Adds coverage for context-based decoding of binary/xml/json extension bodies.
src/Opc.Ua.XRegistry.Client/XRegistryClient.cs Adds explicit-root constructor and stores RegistryNodeId as an instance property.
src/Opc.Ua.XRegistry.Client/GenericXRegistryClient.cs Adds explicit-root constructor for generic registry client.
src/Opc.Ua.WotCon.Client/WotRegistryResourceClient.cs Adds resource wrapper with validate/enable/default/delete and upload/download helpers.
src/Opc.Ua.WotCon.Client/WotRegistryGroupClient.cs Adds group wrapper for create/get-or-create/open resource and delete operations.
src/Opc.Ua.WotCon.Client/WotRegistryClient.cs Adds primary WoT registry client (group ops, refresh, bulk load workflow).
src/Opc.Ua.WotCon.Client/WotConnectivityClient.cs Refactors browse-path resolution to shared resolver; minor doc formatting.
src/Opc.Ua.WotCon.Client/WotConBrowsePathResolver.cs Adds centralized TranslateBrowsePaths helper for resolving single children.
src/Opc.Ua.WotCon.Client/Opc.Ua.WotCon.Client.csproj Adds dependency on Opc.Ua.XRegistry.Client for shared base lifecycle.
src/Opc.Ua.WotCon.Client/NugetREADME.md Updates package README to document registry client surface.
src/Opc.Ua.WotCon.Client/Hosting/WotRegistryClientOptions.cs Adds registry-client DI options (LazyConnect).
src/Opc.Ua.WotCon.Client/Hosting/OpcUaWotConClientBuilderExtensions.cs Adds AddWotRegistryClient extension methods and lazy accessor implementation.
src/Opc.Ua.Types/Utils/FileSystem/VirtualFileSystem.cs Implements atomic Replace for in-memory virtual file system.
src/Opc.Ua.Types/Utils/FileSystem/ResourceFileSystem.cs Implements Replace as not writeable for resource FS.
src/Opc.Ua.Types/Utils/FileSystem/NullFileSystem.cs Implements Replace as not available for null FS.
src/Opc.Ua.Types/Utils/FileSystem/LocalFileSystem.cs Implements Replace using File.Move(overwrite) or File.Replace fallback.
src/Opc.Ua.Types/Utils/FileSystem/IFileSystem.cs Adds Replace(source, destination) API with atomic publish semantics.
src/Opc.Ua.Types/Utils/FileSystem/CombinedFileSystem.cs Delegates Replace to the configured writeable side.
src/Opc.Ua.Types/Nodes/TypeTable.cs Adjusts type/encoding equivalence logic and fixes doc indentation.
src/Opc.Ua.Types/BuiltIn/Variant.cs Adds TryGetStructure(context, ...) overloads; adds docs for internal Union.
src/Opc.Ua.Types/BuiltIn/TypeInfo.cs Improves data type id resolution for known extension object type ids.
src/Opc.Ua.Types/BuiltIn/ExtensionObject.cs Adds context-based decoding for binary/xml/json bodies in TryGetValue.
Suppressed comments (1)

src/Opc.Ua.Types/Utils/FileSystem/IFileSystem.cs:1

  • The XML doc contains a duplicated <param name="destinationPath"> entry, which can break doc generation and IntelliSense. Remove the duplicate param line. Also, since some platform implementations (e.g., File.Replace on older TFMs) can fail across volumes/filesystems, it would be helpful to document any such constraints explicitly in this contract comment.
/* ========================================================================

Comment thread src/Opc.Ua.WotCon.Client/WotRegistryResourceClient.cs Outdated
Comment thread src/Opc.Ua.WotCon.Client/WotRegistryClient.cs Outdated
Comment thread src/Opc.Ua.WotCon.Client/WotRegistryClient.cs Outdated
Comment thread src/Opc.Ua.Types/Utils/FileSystem/VirtualFileSystem.cs Outdated
Comment thread tests/Opc.Ua.WotCon.Tests/Client/WotRegistrySessionMock.cs Outdated
The fluent builder generator emits calls to INodeManagerBuilder.VariableFromDataTypeId
and to a NodeManagerBuilder constructor overload that carries the data-type lookup, but
the runtime side of that API was missing, so every generated node manager failed to
compile with CS1729 and CS1061.

Adds the VariableFromDataTypeId resolution to the builder interface and implementation,
along with NodeStateLookupExtensions.FindByDataType, which is the lookup the builder
delegates to and has no other consumer.

Resolution reports BadNodeIdInvalid for a null data type, BadNodeIdUnknown when nothing
matches, BadBrowseNameDuplicated when the match is ambiguous, and BadTypeMismatch when
the resolved node is not a variable. An optional browse name disambiguates a data type
that is carried by more than one variable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
@marcschier

Copy link
Copy Markdown
Collaborator Author

Fixed the net10.0 solution build failure on this PR.

This branch carries the FluentBuilderGenerator change, which emits calls to
INodeManagerBuilder.VariableFromDataTypeId and to a NodeManagerBuilder constructor
overload carrying the data-type lookup. The runtime side of that API was not present on
any branch in the stack - it had been grouped into a later server PR that does not exist
yet - so every generated node manager failed to compile:

BoilerNodeManager.NodeManager.g.cs(97,31): CS1729: 'NodeManagerBuilder' does not
  contain a constructor that takes 7 arguments
BoilerNodeManager.FluentBuilders.g.cs(140,24): CS1061: 'INodeManagerBuilder' does not
  contain a definition for 'VariableFromDataTypeId'

The fix lands in #4132 (where the generator change belongs) and is cherry-picked here so
this branch builds standalone. It adds VariableFromDataTypeId to the builder interface
and implementation, NodeStateLookupExtensions.FindByDataType, and eight tests covering
the resolution rules.

Verified locally: MinimalBoilerServer - one of the two projects failing in CI - builds
clean on this branch.

DiNodeManager constructed NodeManagerBuilder without the data-type resolver, so
VariableFromDataTypeId reported BadNodeIdUnknown ("no predefined variable has DataType")
for every DI node manager - a misleading error, since the lookup had simply never
been supplied rather than the variable being absent.

Delegates to NodeStateLookupExtensions.FindByDataType rather than hand-rolling the
scan a fourth time in this file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
@marcschier marcschier closed this Jul 31, 2026
@marcschier marcschier reopened this Jul 31, 2026
…client

# Conflicts:
#	src/Opc.Ua.Di.Server/DiNodeManager.cs
Allow lazy client connection retries after failed attempts while keeping concurrent callers on the same in-flight task. Validate malformed method-call responses before indexing result arrays, avoid redundant resource lookups during bulk load, and add bounded spinning to virtual file replacement contention paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.25566% with 190 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.13%. Comparing base (66ccab9) to head (ed86073).

Files with missing lines Patch % Lines
src/Opc.Ua.WotCon.Client/WotRegistryClient.cs 65.99% 62 Missing and 22 partials ⚠️
...ient/Hosting/OpcUaWotConClientBuilderExtensions.cs 59.13% 30 Missing and 8 partials ⚠️
.../Opc.Ua.WotCon.Client/WotRegistryResourceClient.cs 54.71% 19 Missing and 5 partials ⚠️
src/Opc.Ua.Types/BuiltIn/ExtensionObject.cs 74.02% 12 Missing and 8 partials ⚠️
src/Opc.Ua.WotCon.Client/WotRegistryGroupClient.cs 62.85% 11 Missing and 2 partials ⚠️
...Opc.Ua.Types/Utils/FileSystem/VirtualFileSystem.cs 60.00% 4 Missing and 2 partials ⚠️
...c/Opc.Ua.Types/Utils/FileSystem/LocalFileSystem.cs 60.00% 1 Missing and 1 partial ⚠️
src/Opc.Ua.Types/BuiltIn/TypeInfo.cs 80.00% 0 Missing and 1 partial ⚠️
src/Opc.Ua.Types/BuiltIn/Variant.cs 95.83% 0 Missing and 1 partial ⚠️
...c.Ua.SourceGeneration/SourceGeneratorFileSystem.cs 0.00% 1 Missing ⚠️

❌ Your patch check has failed because the patch coverage (69.25%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@                        Coverage Diff                         @@
##           marcschier/wot-10-wotcon-model    #4142      +/-   ##
==================================================================
+ Coverage                           79.88%   80.13%   +0.24%     
==================================================================
  Files                                1540     1544       +4     
  Lines                              214958   215517     +559     
  Branches                            37133    37236     +103     
==================================================================
+ Hits                               171723   172701     +978     
+ Misses                              30301    29886     -415     
+ Partials                            12934    12930       -4     
Files with missing lines Coverage Δ
src/Opc.Ua.Types/Nodes/TypeTable.cs 98.57% <100.00%> (+0.02%) ⬆️
...pc.Ua.Types/Utils/FileSystem/CombinedFileSystem.cs 55.55% <100.00%> (+4.04%) ⬆️
...rc/Opc.Ua.Types/Utils/FileSystem/NullFileSystem.cs 100.00% <100.00%> (ø)
...pc.Ua.Types/Utils/FileSystem/ResourceFileSystem.cs 40.00% <100.00%> (+1.76%) ⬆️
...c/Opc.Ua.WotCon.Client/WotConBrowsePathResolver.cs 100.00% <100.00%> (ø)
src/Opc.Ua.WotCon.Client/WotConnectivityClient.cs 76.40% <100.00%> (+76.40%) ⬆️
.../Opc.Ua.XRegistry.Client/GenericXRegistryClient.cs 100.00% <100.00%> (ø)
src/Opc.Ua.XRegistry.Client/XRegistryClient.cs 97.67% <100.00%> (+0.11%) ⬆️
src/Opc.Ua.Types/BuiltIn/TypeInfo.cs 83.35% <80.00%> (+0.10%) ⬆️
src/Opc.Ua.Types/BuiltIn/Variant.cs 80.84% <95.83%> (+0.57%) ⬆️
... and 8 more

... and 40 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The registry client's fault handling, result validation and dependency-injection
configuration branches carried no tests, which is where a misbehaving or
unreachable registry would actually be observable to a client application.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
@marcschier

Copy link
Copy Markdown
Collaborator Author

Patch coverage: 69.25 % → ~98 % (local measurement)

codecov/patch was failing at 69.25 % against the 80 % gate. This adds 42 tests (no production code changed) that close the error, validation and DI-configuration branches of the WoT Connectivity registry client.

Local patch coverage (Cobertura from Opc.Ua.WotCon.Tests + Opc.Ua.XRegistry.Tests + Opc.Ua.Types.Tests, merged, diffed against origin/marcschier/wot-10-wotcon-model)

Before After
Changed lines hit 570 / 712 700 / 712
Patch coverage 80.06 % 98.31 %

Per file:

File Before After
src/Opc.Ua.WotCon.Client/WotRegistryClient.cs 64 missed / 293 (78.2 %) 2 missed (99.3 %)
src/Opc.Ua.WotCon.Client/Hosting/OpcUaWotConClientBuilderExtensions.cs 30 missed / 94 (68.1 %) 5 missed (94.7 %)
src/Opc.Ua.WotCon.Client/WotRegistryResourceClient.cs 21 missed / 75 (72.0 %) 2 missed (97.3 %)
src/Opc.Ua.WotCon.Client/WotRegistryGroupClient.cs 11 missed / 54 (79.6 %) 0 missed (100 %)
src/Opc.Ua.WotCon.Client/WotConBrowsePathResolver.cs 0 missed / 28 (no direct test) 0 missed (100 %, now directly tested)
src/Opc.Ua.XRegistry.Client/XRegistryClient.cs 11 / 11 (100 %) unchanged
src/Opc.Ua.Types/BuiltIn/ExtensionObject.cs 13 missed / 77 (83.1 %) 1 missed (98.7 %)
src/Opc.Ua.Types/Utils/FileSystem/LocalFileSystem.cs 1 missed / 5 (80 %) 0 missed (100 %)

What the new tests cover

WotRegistryClientTests.cs (+24) — argument validation (ISession / registryObjectId / telemetry null, blank groupId / resourceId), Refresh fault handling (bad StatusCode from the server, too few output arguments, non-structure output arguments, summary ExtensionObject with no body, malformed binary body, undecodable per-resource result), the binary-ExtensionObject decoding fallback used when the server publishes its own encoding ids, group TypeDefinition resolution failures (server reports no HasTypeDefinition reference / an unrecognised ObjectType), WotRegistryRefreshResult.HasFailures + EnsureSuccess(), CreateResource / OpenResource / group Delete, and a Stream upload/download round trip.

WotConBrowsePathResolverTests.cs (new, +4) — the shared TranslateBrowsePaths helper: the happy path (asserting the emitted BrowsePath shape) plus the three degenerate responses a non-conformant server can return (no result, no target, bad result status), each of which must surface the caller-supplied status code and message.

OpcUaWotRegistryClientBuilderExtensionsTests.cs (+8)AddWotRegistryClient on both IOpcUaBuilder and IOpcUaClientBuilder: IConfiguration / IConfigurationSection binding (verifying the options actually bind from OpcUa:WotCon:RegistryClient), all remaining null-argument guards, the accessor rejecting ConnectAsync after DisposeAsync (including the idempotent second dispose), and the lazy-connect factory when the registered ManagedSession factory yields no session.

WotRegistryBulkLoadTests.cs (+6)LoadDocumentsAsync(default) null guard, all three WotRegistryDocument constructor guards plus its VersionId defaulting, and WotRegistryDocumentLoadOutcome.ResourceNodeId / .Created across a re-load.

ExtensionObjectTests.cs (+4) — the decode-failure catch filter (truncated binary body of a registered type) and the unknown-type early-outs for XML and JSON bodies, plus a not-well-formed XML body.

AtomicFileReplaceTests.cs (+1)LocalFileSystem.Replace creating a missing destination directory.

WotRegistrySessionMock gained four scripting knobs (ReturnNoTypeDefinitionOnce, TypeDefinitionOverride, OverrideMethod, ResolveMethodId) so the existing mock stays the single mocking approach.

Remaining 12 uncovered changed lines — all deliberate

  • OpcUaWotConClientBuilderExtensions.cs 556-562 — reachable only with a real ManagedSession; its only constructor is private, so it cannot be constructed or mocked in a unit test.
  • WotRegistryClient.cs 357-358 and WotRegistryResourceClient.cs 166-167 — unreachable. Variant.TryGetStructure(context, out T) already performs the identical TryGetValue(out ExtensionObject) && extension.TryGetValue(out value, context) fallback, so the second if body in the client can never execute. The conditions themselves are covered. (Flagging rather than "fixing": removing the redundant branch is a source change and this PR is under review.)
  • VirtualFileSystem.cs 185-186 — the TryAdd race retry (spinner.SpinOnce(); continue;); only reachable under a lost concurrent publish, which cannot be forced deterministically.
  • ExtensionObject.cs 433 — needs a registered encodeable exposing a null TypeId/BinaryEncodingId/XmlEncodingId; none exists.

Validation

Project net10.0 net48
Opc.Ua.WotCon.Tests 443 passed, 0 failed (was 401) 443 passed, 0 failed
Opc.Ua.XRegistry.Tests 171 passed, 0 failed
Opc.Ua.Types.Tests 8430 passed, 0 failed (was 8425)

All builds Release, 0 warnings / 0 errors with TreatWarningsAsErrors. dotnet format reports no changes on every touched file. No production code was modified.

@marcschier
marcschier force-pushed the marcschier/wot-15-client branch from ab49cd9 to 4c32ce3 Compare August 1, 2026 11:45
# Conflicts:
#	src/Opc.Ua.Types/Utils/FileSystem/VirtualFileSystem.cs
@marcschier
marcschier force-pushed the marcschier/wot-15-client branch from 4c32ce3 to 237c7d0 Compare August 1, 2026 11:47
@marcschier
marcschier changed the base branch from marcschier/wot-10-wotcon-model to marcschier/wot-13-registry August 1, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants