refactor(transport): single-source template child equality — diff and overwrite-sync can no longer drift
New TemplateChildEquality is the one place "has this template child changed?" is decided, comparing the COMPLETE writable field set per entity (attributes now incl. ElementDataType + LockedInDerived; alarms incl. the on-trigger script by name + LockedInDerived; scripts incl. MinTimeBetweenRuns/ExecutionTimeoutSeconds + LockedInDerived; native sources per T5). ArtifactDiff.CompareTemplate and all four BundleImporter.SyncTemplate*Async predicates now route through it — closing the class of bug where Preview reported Identical for an artifact an Overwrite would mutate. The attribute sync passes a value-normalised DTO so List-value re-imports stay idempotent; the alarm on-trigger change now surfaces in both diff and sync. Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -1645,15 +1645,12 @@ public sealed class BundleImporter : IBundleImporter
|
||||
attrDto.Value, attrDto.DataType, attrDto.ElementDataType, _logger, attrDto.Name);
|
||||
if (existingByName.TryGetValue(attrDto.Name, out var current))
|
||||
{
|
||||
// Update only if any field actually changed.
|
||||
bool changed =
|
||||
!string.Equals(current.Value, normalizedValue, StringComparison.Ordinal) ||
|
||||
current.DataType != attrDto.DataType ||
|
||||
current.IsLocked != attrDto.IsLocked ||
|
||||
!string.Equals(current.Description, attrDto.Description, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.DataSourceReference, attrDto.DataSourceReference, StringComparison.Ordinal) ||
|
||||
current.ElementDataType != attrDto.ElementDataType ||
|
||||
current.LockedInDerived != attrDto.LockedInDerived;
|
||||
// Update only if any field actually changed — single-sourced through
|
||||
// TemplateChildEquality (#05-T6). Compare against the DTO with its
|
||||
// List value already normalised so an idempotent re-import of an
|
||||
// old-form bundle doesn't spuriously report a Value change.
|
||||
bool changed = !TemplateChildEquality.AttributesEqual(
|
||||
current, attrDto with { Value = normalizedValue });
|
||||
if (!changed) continue;
|
||||
|
||||
current.Value = normalizedValue;
|
||||
@@ -1738,6 +1735,13 @@ public sealed class BundleImporter : IBundleImporter
|
||||
var existingByName = ex.Alarms.ToDictionary(a => a.Name, a => a, StringComparer.Ordinal);
|
||||
var dtoByName = dto.Alarms.ToDictionary(a => a.Name, a => a, StringComparer.Ordinal);
|
||||
|
||||
// On-trigger script is referenced by name in the bundle; resolve the
|
||||
// persisted FK back to a name over this template's scripts so the shared
|
||||
// equality can compare it (#05-T6). Scripts are synced after alarms, so
|
||||
// this reflects the pre-sync (current) script set — which is exactly the
|
||||
// set current.OnTriggerScriptId points into.
|
||||
var scriptNameById = TemplateChildEquality.ScriptNameResolver(ex.Scripts);
|
||||
|
||||
foreach (var existing in existingByName.Values.ToList())
|
||||
{
|
||||
if (dtoByName.ContainsKey(existing.Name)) continue;
|
||||
@@ -1757,13 +1761,9 @@ public sealed class BundleImporter : IBundleImporter
|
||||
{
|
||||
if (existingByName.TryGetValue(alarmDto.Name, out var current))
|
||||
{
|
||||
bool changed =
|
||||
!string.Equals(current.Description, alarmDto.Description, StringComparison.Ordinal) ||
|
||||
current.PriorityLevel != alarmDto.PriorityLevel ||
|
||||
current.TriggerType != alarmDto.TriggerType ||
|
||||
!string.Equals(current.TriggerConfiguration, alarmDto.TriggerConfiguration, StringComparison.Ordinal) ||
|
||||
current.IsLocked != alarmDto.IsLocked ||
|
||||
current.LockedInDerived != alarmDto.LockedInDerived;
|
||||
// Single-sourced through TemplateChildEquality (#05-T6) — includes
|
||||
// the on-trigger script binding, which the diff also compares.
|
||||
bool changed = !TemplateChildEquality.AlarmsEqual(current, alarmDto, scriptNameById);
|
||||
if (!changed)
|
||||
{
|
||||
// Always reset the script FK on Overwrite so the post-flush
|
||||
@@ -1877,16 +1877,8 @@ public sealed class BundleImporter : IBundleImporter
|
||||
{
|
||||
if (existingByName.TryGetValue(scriptDto.Name, out var current))
|
||||
{
|
||||
bool changed =
|
||||
!string.Equals(current.Code, scriptDto.Code, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.TriggerType, scriptDto.TriggerType, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.TriggerConfiguration, scriptDto.TriggerConfiguration, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.ParameterDefinitions, scriptDto.ParameterDefinitions, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.ReturnDefinition, scriptDto.ReturnDefinition, StringComparison.Ordinal) ||
|
||||
current.IsLocked != scriptDto.IsLocked ||
|
||||
current.MinTimeBetweenRuns != scriptDto.MinTimeBetweenRuns ||
|
||||
current.ExecutionTimeoutSeconds != scriptDto.ExecutionTimeoutSeconds ||
|
||||
current.LockedInDerived != scriptDto.LockedInDerived;
|
||||
// Single-sourced through TemplateChildEquality (#05-T6).
|
||||
bool changed = !TemplateChildEquality.ScriptsEqual(current, scriptDto);
|
||||
if (!changed) continue;
|
||||
|
||||
current.Code = scriptDto.Code;
|
||||
@@ -1993,14 +1985,8 @@ public sealed class BundleImporter : IBundleImporter
|
||||
{
|
||||
if (existingByName.TryGetValue(srcDto.Name, out var current))
|
||||
{
|
||||
bool changed =
|
||||
!string.Equals(current.Description, srcDto.Description, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.ConnectionName, srcDto.ConnectionName, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.SourceReference, srcDto.SourceReference, StringComparison.Ordinal) ||
|
||||
!string.Equals(current.ConditionFilter, srcDto.ConditionFilter, StringComparison.Ordinal) ||
|
||||
current.IsLocked != srcDto.IsLocked ||
|
||||
current.IsInherited != srcDto.IsInherited ||
|
||||
current.LockedInDerived != srcDto.LockedInDerived;
|
||||
// Single-sourced through TemplateChildEquality (#05-T6).
|
||||
bool changed = !TemplateChildEquality.NativeAlarmSourcesEqual(current, srcDto);
|
||||
if (!changed) continue;
|
||||
|
||||
current.Description = srcDto.Description;
|
||||
|
||||
Reference in New Issue
Block a user