No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

128 líneas
5.3KB

  1. namespace Campaign_Tracker.Server.LegacyData.Schema;
  2. /// <summary>
  3. /// Default <see cref="ILegacySchemaCompatibilityCheck"/> implementation.
  4. ///
  5. /// Compares the baseline (captured at initialization) against the current
  6. /// schema returned by the inspector. Reports drift entries for:
  7. /// - tables present in baseline but missing in current (TableMissing)
  8. /// - tables present in current but missing in baseline (TableAdded)
  9. /// - columns present in baseline but missing in current (ColumnMissing)
  10. /// - columns present in current but missing in baseline (ColumnAdded)
  11. /// - columns present in both with different type/size/nullability
  12. /// Comparison is case-insensitive on table and column names to match Access
  13. /// behavior.
  14. /// </summary>
  15. public sealed class LegacySchemaCompatibilityCheck : ILegacySchemaCompatibilityCheck
  16. {
  17. private readonly LegacySchemaBaseline _baseline;
  18. private readonly ILegacySchemaInspector _inspector;
  19. private readonly TimeProvider _timeProvider;
  20. public LegacySchemaCompatibilityCheck(
  21. LegacySchemaBaseline baseline,
  22. ILegacySchemaInspector inspector,
  23. TimeProvider? timeProvider = null)
  24. {
  25. _baseline = baseline ?? throw new ArgumentNullException(nameof(baseline));
  26. _inspector = inspector ?? throw new ArgumentNullException(nameof(inspector));
  27. _timeProvider = timeProvider ?? TimeProvider.System;
  28. }
  29. public async Task<LegacySchemaCheckResult> RunAsync(CancellationToken cancellationToken = default)
  30. {
  31. var live = await _inspector.GetCurrentSchemaAsync(cancellationToken).ConfigureAwait(false);
  32. var drifts = new List<LegacySchemaDrift>();
  33. var baselineByName = _baseline.Tables
  34. .ToDictionary(t => t.Name, StringComparer.OrdinalIgnoreCase);
  35. var liveByName = live
  36. .ToDictionary(t => t.Name, StringComparer.OrdinalIgnoreCase);
  37. foreach (var (name, baselineTable) in baselineByName)
  38. {
  39. if (!liveByName.TryGetValue(name, out var liveTable))
  40. {
  41. drifts.Add(new LegacySchemaDrift(
  42. name, null, LegacySchemaChangeType.TableMissing,
  43. $"Table '{name}' is in the approved baseline but missing from the live database."));
  44. continue;
  45. }
  46. CompareColumns(name, baselineTable.Columns, liveTable.Columns, drifts);
  47. }
  48. foreach (var (name, _) in liveByName)
  49. {
  50. if (!baselineByName.ContainsKey(name))
  51. {
  52. drifts.Add(new LegacySchemaDrift(
  53. name, null, LegacySchemaChangeType.TableAdded,
  54. $"Table '{name}' exists in the live database but is not part of the approved baseline."));
  55. }
  56. }
  57. return new LegacySchemaCheckResult(
  58. Passed: drifts.Count == 0,
  59. TablesVerified: baselineByName.Count,
  60. DriftCount: drifts.Count,
  61. CheckedAt: _timeProvider.GetUtcNow(),
  62. Drifts: drifts,
  63. BaselineSource: _baseline.Source);
  64. }
  65. private static void CompareColumns(
  66. string tableName,
  67. IReadOnlyList<LegacyColumnDefinition> baseline,
  68. IReadOnlyList<LegacyColumnDefinition> live,
  69. List<LegacySchemaDrift> sink)
  70. {
  71. var baselineByName = baseline.ToDictionary(c => c.Name, StringComparer.OrdinalIgnoreCase);
  72. var liveByName = live.ToDictionary(c => c.Name, StringComparer.OrdinalIgnoreCase);
  73. foreach (var (name, baseCol) in baselineByName)
  74. {
  75. if (!liveByName.TryGetValue(name, out var liveCol))
  76. {
  77. sink.Add(new LegacySchemaDrift(
  78. tableName, name, LegacySchemaChangeType.ColumnMissing,
  79. $"Column '{tableName}.{name}' is in the approved baseline but missing from the live database."));
  80. continue;
  81. }
  82. if (baseCol.TypeCode != liveCol.TypeCode)
  83. {
  84. sink.Add(new LegacySchemaDrift(
  85. tableName, name, LegacySchemaChangeType.ColumnTypeChanged,
  86. $"Column '{tableName}.{name}' type changed: baseline={baseCol.TypeCode}, live={liveCol.TypeCode}."));
  87. }
  88. if (baseCol.Size != liveCol.Size)
  89. {
  90. sink.Add(new LegacySchemaDrift(
  91. tableName, name, LegacySchemaChangeType.ColumnSizeChanged,
  92. $"Column '{tableName}.{name}' size changed: baseline={Format(baseCol.Size)}, live={Format(liveCol.Size)}."));
  93. }
  94. if (baseCol.Nullable != liveCol.Nullable)
  95. {
  96. sink.Add(new LegacySchemaDrift(
  97. tableName, name, LegacySchemaChangeType.ColumnNullabilityChanged,
  98. $"Column '{tableName}.{name}' nullability changed: baseline={baseCol.Nullable}, live={liveCol.Nullable}."));
  99. }
  100. }
  101. foreach (var (name, _) in liveByName)
  102. {
  103. if (!baselineByName.ContainsKey(name))
  104. {
  105. sink.Add(new LegacySchemaDrift(
  106. tableName, name, LegacySchemaChangeType.ColumnAdded,
  107. $"Column '{tableName}.{name}' exists in the live database but is not part of the approved baseline."));
  108. }
  109. }
  110. }
  111. private static string Format(int? value) => value?.ToString() ?? "(none)";
  112. }

Powered by TurnKey Linux.