feat(sql): factory + connectionStringRef env resolution + string-enum guard
SqlDriverFactoryExtensions turns a deployed DriverConfig blob into a live SqlDriver, and SqlConnectionStringResolver resolves the authored connectionStringRef NAME from Sql__ConnectionStrings__<ref> so credentials never ride in a config blob (design §8.2). Three behaviours worth naming: - String-enum guard. JsonOptions carries JsonStringEnumConverter + camelCase, so `provider` parses whether authored as a name or an ordinal and always round-trips as the NAME — the systemic AdminUI defect where a page serialised an enum numerically against a string-typed DTO. - Config validation lands here because nothing below does it. operationTimeout must be STRICTLY greater than commandTimeout (design §8.3); the reader and the driver stay deliberately usable with the pair inverted so the frozen-database tests can prove the client-side bound fires. Non-positive timeouts / poll interval and maxConcurrentGroups < 1 are rejected too. - Credential hygiene. The resolved connection string reaches the provider and nothing else: messages name the ref, the environment variable, or SqlDriver.Endpoint's credential-free server/database rendering. Both a log-leak and an exception-leak test cover it. DTO changes: - Adds RawTags (List<RawTagEntry>), following the Modbus precedent — the deploy artifact delivers a driver's tags this way and the DTO had no way to receive them, so an authored Sql tag could never reach the driver. - Deletes SqlProbeDto + the `probe` key. It had no consumer: SqlDriver was specified without a background probe loop, and deliberately so — its IHostConnectivityProbe state is a by-product of the Initialize liveness check and of every poll, i.e. a statement about traffic that actually happened rather than a synthetic ping. On-demand connectivity is Task 10's SqlDriverProbe. Shipping a config key that silently does nothing is worse than not shipping it; UnmappedMemberHandling.Skip means a blob that still carries `probe` keeps parsing. allowWrites stays inert (SqlDriver implements no write capability) but an authored `true` now WARNS rather than passing silently — the flag cannot do harm, an operator who believes writes are enabled can. Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts;
|
||||
|
||||
/// <summary>
|
||||
@@ -41,19 +43,22 @@ public sealed class SqlDriverConfigDto
|
||||
/// <summary>When <see langword="true"/>, a NULL cell publishes Bad rather than the default Uncertain.</summary>
|
||||
public bool? NullIsBad { get; init; }
|
||||
|
||||
/// <summary>Master write kill-switch. <b>v1 is read-only</b> — writes stay disabled regardless.</summary>
|
||||
/// <summary>
|
||||
/// Master write kill-switch. <b>v1 is read-only and this field is inert</b> — the driver does not
|
||||
/// implement <c>IWritable</c>, so no value here can produce a write. The factory <em>warns</em> when it
|
||||
/// is authored <see langword="true"/> rather than failing the driver, since the flag cannot do harm but
|
||||
/// an operator who believes writes are enabled can.
|
||||
/// </summary>
|
||||
public bool? AllowWrites { get; init; }
|
||||
|
||||
/// <summary>Background connectivity-probe settings. Absent ⇒ the factory default.</summary>
|
||||
public SqlProbeDto? Probe { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Background connectivity-probe settings inside a <see cref="SqlDriverConfigDto"/> (design §5.1).</summary>
|
||||
public sealed class SqlProbeDto
|
||||
{
|
||||
/// <summary>Whether the driver runs its background connectivity probe.</summary>
|
||||
public bool? Enabled { get; init; }
|
||||
|
||||
/// <summary>Interval between probe attempts.</summary>
|
||||
public TimeSpan? Interval { get; init; }
|
||||
/// <summary>
|
||||
/// The authored raw tags the deploy artifact delivers for this driver instance — RawPath identity plus
|
||||
/// the driver <c>TagConfig</c> blob, the same shape every other driver receives. The factory copies
|
||||
/// these onto the driver's options and the driver maps each through
|
||||
/// <see cref="SqlEquipmentTagParser.TryParse"/> at Initialize.
|
||||
/// <para>A SQL source has no tag-discovery protocol, so this list is the complete set of tags the
|
||||
/// instance serves — an absent or empty list is a driver with nothing to poll, not a driver that will
|
||||
/// find its tags later.</para>
|
||||
/// </summary>
|
||||
public List<RawTagEntry>? RawTags { get; init; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user