Skip to content

[WotCon] Add the WoT Connectivity registry and its stores - #4146

Open
marcschier wants to merge 27 commits into
marcschier/wot-12-executorsfrom
marcschier/wot-13-registry
Open

[WotCon] Add the WoT Connectivity registry and its stores#4146
marcschier wants to merge 27 commits into
marcschier/wot-12-executorsfrom
marcschier/wot-13-registry

Conversation

@marcschier

Copy link
Copy Markdown
Collaborator

Adds the WoT Connectivity registry service and stores for Thing Description and Thing Model documents.

This is PR 13 of the stacked split of integration PR #4093 and is stacked on #4140 (marcschier/wot-11-bindings-core). The materialization runtime that consumes this registry follows separately.

The registry has no dependency on the binding runtime or the NodeManager lifecycle; the stacking is only because this change shares the WoT Connectivity server project file with the preceding PRs.

Validation:

  • dotnet build src\Opc.Ua.WotCon.Server\Opc.Ua.WotCon.Server.csproj -c Release -f net10.0 -v:m
  • dotnet build tests\Opc.Ua.WotCon.Tests\Opc.Ua.WotCon.Tests.csproj -c Release -p:CustomTestTarget=net10.0 -v:m
  • dotnet test tests\Opc.Ua.WotCon.Tests\Opc.Ua.WotCon.Tests.csproj -c Release -p:CustomTestTarget=net10.0 --no-build -v:q
  • dotnet build src\Opc.Ua.WotCon.Server\Opc.Ua.WotCon.Server.csproj -c Release -v:m

marcschier and others added 3 commits July 31, 2026 11:44
Adds a stable, injectable registry for Thing Description and Thing Model documents, with in-memory and file-backed stores behind one interface.

The file-backed store commits atomically: content-addressed blobs are verified and skipped rather than rewritten, blob writes are staged under a temporary name and published in a single indivisible step through the file-system atomic-replace capability, and a commit that is interrupted leaves either the previous state or the new one, never a partially written blob that the manifest already references. Recovery artefacts left by an interrupted commit are detected and rolled back on load.

Documents are versioned with epochs and desired/active state, labels and bounds are persisted, and blob storage is delegated to the shared xRegistry resource store contract rather than reimplemented.

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

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
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.

marcschier and others added 5 commits July 31, 2026 14:57
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
…-registry

# Conflicts:
#	src/Opc.Ua.Di.Server/DiNodeManager.cs
#	src/Opc.Ua.WotCon.Server/WotConModelPartition.cs
#	src/Opc.Ua.WotCon.Server/WotConnectivityNodeManager.cs
An empty collection expression binds to ImmutableDictionary<TKey, TValue> only on
the newer framework, so the registry model compiled on net10.0 but failed on net48
with CS1729 and CS0019. Naming the Empty singleton explicitly works on every target
and matches how the labels defaults are already written.

The ImmutableArray collection expressions elsewhere in the registry are unaffected
and keep their existing form.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
marcschier and others added 4 commits August 1, 2026 09:06
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
Linearising the stack brought the registry together with the core-types change that
moved atomic replacement onto IFileSystem itself. The registry still carried the
superseded IAtomicFileReplace capability interface, so both designs were present at
once.

Deletes IAtomicFileReplace and migrates its users to IFileSystem.Replace. The blob
store no longer probes for the capability, because every file system now provides it,
and the test that asserted the failure path for a file system without the capability
is removed with it - that state is no longer representable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
@marcschier
marcschier changed the base branch from marcschier/wot-11-bindings-core to marcschier/wot-12-executors August 1, 2026 11:55
Copilot AI review requested due to automatic review settings August 1, 2026 16:21

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

Adds the WoT Connectivity (WoT-Con) registry surface to Opc.Ua.WotCon.Server, including an injectable registry service model and document stores (in-memory and file-backed via an xRegistry IXRegistryResourceStore abstraction), plus a comprehensive new test suite validating CRUD, labels, persistence, and OPC UA FileType file-handle behavior.

Changes:

  • Introduces the registry domain model + bounds/validation helpers (WotRegistryModel, WotRegistryPersistenceBounds, WotLabelValidator) and injectable service/store contracts (IWotRegistryService, IWotRegistryStore).
  • Adds document storage implementations, including a content-addressed blob store behind IXRegistryResourceStore (WotBlobResourceStore) and a generalized per-resource FileType manager (WotResourceFileManager).
  • Adds extensive NUnit coverage for store contracts, transactional commit outcomes, FileType behavior, labels/ordering, and file-store reload behavior.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/Opc.Ua.WotCon.Tests/TestMaterialization.cs Shared TD/TM/invalid JSON builders used across registry/materialization tests.
tests/Opc.Ua.WotCon.Tests/Registry/XRegistryResourceStoreContractTests.cs Shared contract test suite for IXRegistryResourceStore implementations.
tests/Opc.Ua.WotCon.Tests/Registry/WotResourceFileManagerTests.cs Unit tests for the new WotResourceFileManager FileType behavior (handles, auth, bounds, commit).
tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryTransactionTests.cs Fault-injection tests for transactional commit outcomes and service publish semantics.
tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryServiceTests.cs Core CRUD/versioning/state tests for WotRegistryService.
tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryServiceExtendedTests.cs Additional coverage for group helpers, slug/normalize paths, and registry/group label APIs.
tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryServiceCrudTests.cs Tests for xRegistry CRUD methods used by UA management methods (group/resource create/delete, validate).
tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryModelTests.cs Value-object tests for digest/version/labels model types.
tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryLabelsServiceTests.cs Label/attribute service tests (validation, bounds, ordering, persistence).
tests/Opc.Ua.WotCon.Tests/Registry/WotBlobResourceStoreTests.cs Runs the shared store contract against WotBlobResourceStore and validates on-disk layout expectations.
tests/Opc.Ua.WotCon.Tests/Registry/FileWotRegistryStoreExtendedTests.cs File-store error-path and persistence regression coverage (manifest/blob/labels).
src/Opc.Ua.WotCon.Server/WotConnectivityNodeManager.cs Updates method handler result types to match source-generated method state result types.
src/Opc.Ua.WotCon.Server/Registry/WotResourceFileManager.cs New generalized FileType manager for registry document nodes (read snapshot, buffered write, commit-on-close).
src/Opc.Ua.WotCon.Server/Registry/WotRegistryPersistenceBounds.cs New bounds object enforcing caps for documents, groups/resources, labels, etc.
src/Opc.Ua.WotCon.Server/Registry/WotRegistryModel.cs New immutable snapshot model for registry/groups/resources/versions + digest and label ordering helpers.
src/Opc.Ua.WotCon.Server/Registry/WotLabelValidator.cs Key/value validation for xRegistry labels prior to materialization into browseable nodes.
src/Opc.Ua.WotCon.Server/Registry/WotBlobResourceStore.cs New IXRegistryResourceStore implementation using content-addressed blob files with atomic replace.
src/Opc.Ua.WotCon.Server/Registry/IWotRegistryStore.cs Defines transactional commit contract + explicit commit outcome exceptions for external stores.
src/Opc.Ua.WotCon.Server/Registry/IWotRegistryService.cs Defines the stable, injectable registry service API and DTOs/event args.
src/Opc.Ua.WotCon.Server/Registry/InMemoryWotRegistryStore.cs In-memory implementation of the transactional store contract for tests/startup-seeded deployments.
src/Opc.Ua.WotCon.Server/Opc.Ua.WotCon.Server.csproj Adds reference to Opc.Ua.XRegistry.Server.
src/Opc.Ua.WotCon.Server/Assets/WotAssetFileManager.cs Updates handler delegate type to the source-generated method handler type.
src/Opc.Ua.Types/Utils/FileSystem/VirtualFileSystem.cs Improves Replace atomic publish behavior using AddOrUpdate while preserving “no partial publish” semantics.
Suppressed comments (3)

tests/Opc.Ua.WotCon.Tests/Registry/XRegistryResourceStoreContractTests.cs:128

  • Assert.Multiple(async () => ...) becomes an async-void lambda and can hide assertion failures. Capture the awaited values before entering Assert.Multiple and keep the delegate synchronous.
            Assert.Multiple(async () =>
            {
                Assert.That(document.Span.ToArray(),
                    Is.EqualTo(new byte[] { 0x01, 0xAA, 0xBB, 0x04 }));
                Assert.That(await store.GetLengthAsync("a").ConfigureAwait(false), Is.EqualTo(4),

tests/Opc.Ua.WotCon.Tests/Registry/XRegistryResourceStoreContractTests.cs:172

  • Assert.Multiple(async () => ...) uses an async-void delegate, so the awaited ReadAsync and the asserts may run outside the test body. Await ReadAsync first, then assert synchronously inside Assert.Multiple.
            Assert.Multiple(async () =>
            {
                Assert.That(removed, Is.True);
                Assert.That(again, Is.False, "Deleting an absent key is a no-op, not a fault.");
                Assert.That(

tests/Opc.Ua.WotCon.Tests/Registry/XRegistryResourceStoreContractTests.cs:189

  • Assert.Multiple(async () => ...) is async-void under NUnit and can drop failures from awaited reads. Await the reads first and then assert synchronously.
            Assert.Multiple(async () =>
            {
                Assert.That(
                    (await store.ReadAsync("a", 0, 16).ConfigureAwait(false)).Span.ToArray(),
                    Is.EqualTo(new byte[] { 0x01 }));

Comment thread tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryServiceCrudTests.cs
Comment thread tests/Opc.Ua.WotCon.Tests/Registry/WotRegistryLabelsServiceTests.cs
Comment thread src/Opc.Ua.WotCon.Server/Registry/WotRegistryPersistenceBounds.cs
Comment thread src/Opc.Ua.WotCon.Server/Registry/WotResourceFileManager.cs
Comment thread src/Opc.Ua.WotCon.Server/Registry/WotResourceFileManager.cs
Comment thread src/Opc.Ua.WotCon.Server/Registry/WotBlobResourceStore.cs
Comment thread src/Opc.Ua.WotCon.Server/Registry/WotResourceFileManager.cs
…istry

# Conflicts:
#	src/Opc.Ua.WotCon.Server/Assets/WotAssetFileManager.cs
Tighten registry blob and file-handle bounds, reset file manager disposal state, and remove sync-over-async in affected tests.

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

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
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