fix(commons): resolve Commons-005..007,009..012 — OPC UA parse status, TryConvert correctness, Result null guard, invariant formatting, doc refresh

This commit is contained in:
Joseph Doherty
2026-05-16 22:04:21 -04:00
parent 746ab90444
commit c07f524ca4
12 changed files with 602 additions and 99 deletions

View File

@@ -60,26 +60,28 @@ Commons must define persistence-ignorant POCO entity classes for all configurati
Entity classes are organized by domain area:
- **Template & Modeling**: `Template`, `TemplateAttribute`, `TemplateAlarm`, `TemplateScript`, `TemplateComposition`, `Instance`, `InstanceAttributeOverride`, `InstanceConnectionBinding`, `Area`.
- **Template & Modeling**: `Template`, `TemplateAttribute`, `TemplateAlarm`, `TemplateScript`, `TemplateComposition`, `TemplateFolder`.
- **Instances**: `Instance`, `InstanceAttributeOverride`, `InstanceConnectionBinding`, `InstanceAlarmOverride`, `Area`.
- **Shared Scripts**: `SharedScript`.
- **Sites & Data Connections**: `Site`, `DataConnection`, `SiteDataConnectionAssignment`.
- **Sites & Data Connections**: `Site`, `DataConnection`.
- **External Systems & Database Connections**: `ExternalSystemDefinition`, `ExternalSystemMethod`, `DatabaseConnectionDefinition`.
- **Notifications**: `NotificationList`, `NotificationRecipient`, `SmtpConfiguration`.
- **Inbound API**: `ApiKey`, `ApiMethod`.
- **Security**: `LdapGroupMapping`, `SiteScopeRule`.
- **Deployment**: `DeploymentRecord`, `SystemArtifactDeploymentRecord`.
- **Deployment**: `DeploymentRecord`, `SystemArtifactDeploymentRecord`, `DeployedConfigSnapshot`.
- **Audit**: `AuditLogEntry`.
### REQ-COM-4: Per-Component Repository Interfaces
Commons must define repository interfaces that consuming components use for data access. Each interface is tailored to the data needs of its consuming component:
- `ITemplateEngineRepository` — Templates, attributes, alarms, scripts, compositions, instances, overrides, connection bindings, areas.
- `ITemplateEngineRepository` — Templates, attributes, alarms, scripts, compositions, template folders, instances, overrides, alarm overrides, connection bindings, areas.
- `IDeploymentManagerRepository` — Deployment records, deployed configuration snapshots, system-wide artifact deployment records.
- `ISecurityRepository` — LDAP group mappings, site scoping rules.
- `IInboundApiRepository` — API keys, API method definitions.
- `IExternalSystemRepository` — External system definitions, method definitions, database connection definitions.
- `INotificationRepository` — Notification lists, recipients, SMTP configuration.
- `ISiteRepository` — Sites, data connections, and their site assignments.
- `ICentralUiRepository` — Read-oriented queries spanning multiple domain areas for display purposes.
All repository interfaces must:
@@ -93,7 +95,13 @@ Implementations of these interfaces are owned by the Configuration Database comp
Commons must define service interfaces for cross-cutting concerns that multiple components consume:
- **`IAuditService`**: Provides a single method for components to log audit entries: `LogAsync(user, action, entityType, entityId, entityName, afterState)`. The implementation (owned by the Audit Logging component) serializes the state as JSON and adds the audit entry to the current unit-of-work transaction. Defined in Commons so any central component can call it without depending on the Audit Logging component directly.
- **`IAuditService`**: Provides a single method for components to log audit entries: `LogAsync(user, action, entityType, entityId, entityName, afterState)`. The implementation (owned by the Configuration Database component) serializes the state as JSON and adds the audit entry to the current unit-of-work transaction. Defined in Commons so any central component can call it without depending on the Configuration Database component directly.
- **`IDatabaseGateway`**: Provides script-facing ADO.NET database access via named database connections. Implemented by the External System Gateway, consumed by the Site Runtime's script runtime context.
- **`IExternalSystemClient`**: Provides script-facing invocation of external system HTTP APIs (synchronous `Call` and store-and-forward `CachedCall`). Implemented by the External System Gateway, consumed by the script runtime context.
- **`IInstanceLocator`**: Resolves an instance unique name to its site identifier. Used by the Inbound API's `Route.To()` to determine the destination site.
- **`INotificationDeliveryService`**: Sends notifications to a named notification list, routing transient failures to store-and-forward. Implemented by the Notification Service, consumed by the script runtime context.
These interfaces are defined in Commons so that consuming components depend only on the abstraction, not on the implementing component.
### REQ-COM-5: Cross-Component Message Contracts
@@ -126,20 +134,23 @@ All types in Commons are organized by **category** and **domain area** using a c
```
ScadaLink.Commons/
├── Types/ # REQ-COM-1: Shared data types
│ ├── DataType.cs
│ ├── RetryPolicy.cs
│ ├── Result.cs
── Enums/
├── InstanceState.cs
├── DeploymentStatus.cs
├── AlarmState.cs
├── AlarmTriggerType.cs
└── ConnectionHealth.cs
── RetryPolicy.cs
├── ScriptArgs.cs # script-call parameter normalization helper
├── ScriptParameters.cs # typed script-parameter access helper
├── StaleTagMonitor.cs # heartbeat staleness watchdog
├── ValueFormatter.cs # culture-invariant value-to-string helper
├── DynamicJsonElement.cs # dynamic JSON wrapper for scripts
│ ├── Enums/ # InstanceState, DeploymentStatus, AlarmState,
│ │ # AlarmLevel, AlarmTriggerType, ConnectionHealth,
│ │ # DataType, StoreAndForwardCategory,
│ │ # StoreAndForwardMessageStatus
│ ├── DataConnections/ # OPC UA endpoint config value objects + enums
│ ├── Flattening/ # FlattenedConfiguration, ConfigurationDiff,
│ │ # DeploymentPackage, ValidationResult
│ └── Scripts/ # AlarmContext, ScriptScope
├── Interfaces/ # Shared interfaces by concern
│ ├── Protocol/ # REQ-COM-2: Protocol abstraction
│ │ ├── IDataConnection.cs
│ │ ├── TagValue.cs
│ │ └── SubscriptionCallback.cs
│ ├── Protocol/ # REQ-COM-2: Protocol abstraction (IDataConnection, etc.)
│ ├── Repositories/ # REQ-COM-4: Per-component repository interfaces
│ │ ├── ITemplateEngineRepository.cs
│ │ ├── IDeploymentManagerRepository.cs
@@ -147,55 +158,46 @@ ScadaLink.Commons/
│ │ ├── IInboundApiRepository.cs
│ │ ├── IExternalSystemRepository.cs
│ │ ├── INotificationRepository.cs
│ │ ├── ISiteRepository.cs
│ │ └── ICentralUiRepository.cs
│ └── Services/ # REQ-COM-4a: Cross-cutting service interfaces
── IAuditService.cs
── IAuditService.cs
│ ├── IDatabaseGateway.cs
│ ├── IExternalSystemClient.cs
│ ├── IInstanceLocator.cs
│ └── INotificationDeliveryService.cs
├── Entities/ # REQ-COM-3: Domain entity POCOs, by domain area
│ ├── Templates/
│ │ ├── Template.cs
│ ├── TemplateAttribute.cs
│ │ ├── TemplateAlarm.cs
│ ├── TemplateScript.cs
│ └── TemplateComposition.cs
├── Instances/
│ ├── Instance.cs
│ ├── InstanceAttributeOverride.cs
│ ├── InstanceConnectionBinding.cs
│ └── Area.cs
├── Sites/
│ ├── Site.cs
│ ├── DataConnection.cs
└── SiteDataConnectionAssignment.cs
│ ├── ExternalSystems/
│ │ ├── ExternalSystemDefinition.cs
│ │ ├── ExternalSystemMethod.cs
│ │ └── DatabaseConnectionDefinition.cs
│ ├── Notifications/
│ │ ├── NotificationList.cs
│ │ ├── NotificationRecipient.cs
│ │ └── SmtpConfiguration.cs
│ ├── InboundApi/
│ │ ├── ApiKey.cs
│ │ └── ApiMethod.cs
│ ├── Security/
│ │ ├── LdapGroupMapping.cs
│ │ └── SiteScopeRule.cs
│ ├── Templates/ # Template, TemplateAttribute, TemplateAlarm,
│ │ # TemplateScript, TemplateComposition, TemplateFolder
│ ├── Instances/ # Instance, InstanceAttributeOverride,
│ │ # InstanceConnectionBinding, InstanceAlarmOverride, Area
├── Sites/ # Site, DataConnection
├── ExternalSystems/ # ExternalSystemDefinition, ExternalSystemMethod,
│ # DatabaseConnectionDefinition
│ ├── Notifications/ # NotificationList, NotificationRecipient, SmtpConfiguration
│ ├── InboundApi/ # ApiKey, ApiMethod
│ ├── Security/ # LdapGroupMapping, SiteScopeRule
├── Deployment/ # DeploymentRecord, SystemArtifactDeploymentRecord,
│ # DeployedConfigSnapshot
│ ├── Scripts/ # SharedScript
└── Audit/ # AuditLogEntry
├── Messages/ # REQ-COM-5: Cross-component message contracts, by concern
│ ├── Deployment/
│ ├── DeploymentRecord.cs
│ └── SystemArtifactDeploymentRecord.cs
│ ├── Scripts/
│ └── SharedScript.cs
── Audit/
└── AuditLogEntry.cs
└── Messages/ # REQ-COM-5: Cross-component message contracts, by concern
├── Deployment/
├── Lifecycle/
├── Health/
├── Communication/
├── Streaming/
── DebugView/
├── ScriptExecution/
└── Artifacts/
│ ├── Lifecycle/
├── Health/
│ ├── Communication/
├── Streaming/
── DebugView/
├── ScriptExecution/
│ ├── Artifacts/
├── DataConnection/ # data-connection subscribe/write/health messages
├── Instance/ # attribute get/set request/command messages
├── Integration/ # external-integration call request/response
├── InboundApi/ # Route.To() request messages
├── RemoteQuery/ # event-log and parked-message query messages
── Management/ # HTTP/ClusterClient management commands + registry
├── Serialization/ # OpcUaEndpointConfigSerializer (typed↔legacy JSON)
└── Validators/ # OpcUaEndpointConfigValidator
```
**Naming rules**:
@@ -205,7 +207,7 @@ ScadaLink.Commons/
- Message contracts are named as commands, events, or responses: `DeployInstanceCommand`, `DeploymentStatusResponse`, `AttributeValueChanged`.
- Enums use singular names: `AlarmState`, not `AlarmStates`.
### REQ-COM-6: No Business Logic
### REQ-COM-6: No Business Logic; Pure Helpers Permitted
Commons must contain only:
@@ -213,8 +215,17 @@ Commons must contain only:
- Interfaces
- Enums
- Constants
- **Pure, stateless helpers** — see the carve-out below.
It must **not** contain any business logic, service implementations, actor definitions, or orchestration code. Any method bodies must be limited to trivial data-access logic (e.g., factory methods, validation of invariants in constructors).
It must **not** contain any business logic that orchestrates other components, service implementations that perform I/O (database, network, file system), actor definitions, or orchestration code.
**Pure-helper carve-out.** Commons *may* contain stateless, side-effect-free helper types whose behavior is confined to transforming, formatting, parsing, or validating the data types Commons already defines, provided they:
- have no dependency on Akka.NET, ASP.NET Core, EF Core, or any I/O surface (consistent with REQ-COM-7);
- hold no shared mutable state across calls (a self-contained instance helper such as `StaleTagMonitor`, which owns only its own timer, is acceptable);
- do not call into other components or perform orchestration.
Examples currently in Commons that fall under this carve-out: `Result<T>`, `ScriptParameters` and `ScriptArgs` (script-parameter shaping), `ValueFormatter` (value-to-string formatting), `DynamicJsonElement` (dynamic JSON access), `StaleTagMonitor` (a self-contained heartbeat watchdog), `OpcUaEndpointConfigSerializer` (typed↔legacy JSON conversion of a Commons value object) and `OpcUaEndpointConfigValidator` (rule checks over a Commons value object). These are intentionally placed in Commons so every consuming component shares one implementation rather than duplicating the logic. Anything that would require an I/O dependency, mutable cross-call state, or knowledge of another component's behavior does **not** qualify and must live in the owning component.
### REQ-COM-7: Minimal Dependencies