feat(sql): typed AdminUI Sql tag-config model + validator (string enums)

SqlTagConfigModel + SqlRowSelectorModel mirror SqlEquipmentTagParser's
accept/reject boundary byte-for-byte on the fields they own: model/type
serialize as NAME strings (never numbers), KeyValue requires
table+keyColumn+keyValue+valueColumn, WideRow requires table+columnName+a
selector (where-pair OR topByTimestamp), Query is rejected, and unknown keys
(top-level and nested rowSelector) survive load->save. Registered in
TagConfigEditorMap + TagConfigValidator keyed off SqlDriver.DriverTypeName
(DriverTypeNames.Sql is deferred to Task 11). A minimal placeholder
SqlTagConfigEditor.razor lands the map entry now; Task 20 fleshes out the UI.

The load-bearing test rounds editor output back through
SqlEquipmentTagParser.TryParse (editor-output <=> parser-input agreement).

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-24 15:27:14 -04:00
parent 02f2dafe2a
commit 443b718158
5 changed files with 532 additions and 0 deletions
@@ -0,0 +1,35 @@
@* Typed TagConfig editor for the read-only Sql driver. MINIMAL PLACEHOLDER (Task 19) — this shell only
wires the standard editor parameter contract and round-trips the config through SqlTagConfigModel so no
edit is lost while the full UI is pending. Task 20 replaces the body with the model/table/column fields
(KeyValue vs WideRow row-selector). Dispatched from the TagModal via TagConfigEditorMap, so it takes the
same (ConfigJson/ConfigJsonChanged/DriverType/GetDriverConfigJson) parameter shape every typed editor
takes; DriverType/GetDriverConfigJson are accepted for dispatch uniformity. *@
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors
<div class="text-muted small">Sql tag editor — coming soon (Task 20).</div>
@code {
/// <summary>The tag's current TagConfig JSON.</summary>
[Parameter] public string? ConfigJson { get; set; }
/// <summary>Raised with the updated TagConfig JSON when the model changes.</summary>
[Parameter] public EventCallback<string> ConfigJsonChanged { get; set; }
/// <summary>DriverType of the selected driver — accepted for dispatch uniformity.</summary>
[Parameter] public string DriverType { get; set; } = "";
/// <summary>Live accessor for the selected driver's DriverConfig JSON (browse connect config).</summary>
[Parameter] public Func<string> GetDriverConfigJson { get; set; } = () => "{}";
private SqlTagConfigModel _m = new();
private string? _lastConfigJson;
// Re-parse only when the incoming JSON actually changes, so an unrelated parent re-render can't reset
// an in-progress edit (mirrors the other typed editors).
protected override void OnParametersSet()
{
if (ConfigJson == _lastConfigJson) { return; }
_lastConfigJson = ConfigJson;
_m = SqlTagConfigModel.FromJson(ConfigJson);
}
}