fix(multivalue): NJ-3/NJ-4/NJ-5 review fixes

- NJ-3: widen per-row catch to Exception (an STJ encode failure can't abort startup); drop dead null-guard already excluded by the SQL filter
- NJ-4: capture logger/instanceName in locals for the fire-and-forget normalize continuation (match the sibling pattern in this actor)
- NJ-5: emit a warn-log when a malformed List value is imported verbatim; thread an optional ILogger<BundleImporter> to the sync re-import site
This commit is contained in:
Joseph Doherty
2026-06-16 18:25:42 -04:00
parent f4b101b532
commit feeae1371e
4 changed files with 32 additions and 19 deletions
@@ -1,6 +1,7 @@
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
@@ -70,6 +71,7 @@ public sealed class BundleImporter : IBundleImporter
private readonly IOptions<TransportOptions> _options;
private readonly TimeProvider _timeProvider;
private readonly SemanticValidator _semanticValidator;
private readonly ILogger<BundleImporter>? _logger;
/// <summary>
/// Initializes a new <see cref="BundleImporter"/> with all required dependencies.
@@ -106,7 +108,8 @@ public sealed class BundleImporter : IBundleImporter
IAuditService auditService,
IAuditCorrelationContext correlationContext,
ScadaBridgeDbContext dbContext,
SemanticValidator semanticValidator)
SemanticValidator semanticValidator,
ILogger<BundleImporter>? logger = null)
{
_bundleSerializer = bundleSerializer ?? throw new ArgumentNullException(nameof(bundleSerializer));
_manifestValidator = manifestValidator ?? throw new ArgumentNullException(nameof(manifestValidator));
@@ -124,6 +127,7 @@ public sealed class BundleImporter : IBundleImporter
_correlationContext = correlationContext ?? throw new ArgumentNullException(nameof(correlationContext));
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
_semanticValidator = semanticValidator ?? throw new ArgumentNullException(nameof(semanticValidator));
_logger = logger;
}
/// <inheritdoc />
@@ -1120,7 +1124,7 @@ public sealed class BundleImporter : IBundleImporter
// stores natively — otherwise an idempotent re-import of an old-form
// bundle would spuriously report a Value change.
var normalizedValue = ImportValueNormalizer.NormalizeListValue(
attrDto.Value, attrDto.DataType, attrDto.ElementDataType);
attrDto.Value, attrDto.DataType, attrDto.ElementDataType, _logger, attrDto.Name);
if (existingByName.TryGetValue(attrDto.Name, out var current))
{
// Update only if any field actually changed.
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.ScadaBridge.Commons.Types;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
@@ -29,7 +30,14 @@ internal static class ImportValueNormalizer
/// <param name="value">The attribute value as carried by the bundle DTO.</param>
/// <param name="dataType">The attribute's declared data type.</param>
/// <param name="elementType">The List element type (null for scalars).</param>
public static string? NormalizeListValue(string? value, DataType dataType, DataType? elementType)
/// <param name="logger">Optional logger; a warning is emitted when a malformed value is left as-is.</param>
/// <param name="attributeName">Optional attribute name for the diagnostic message.</param>
public static string? NormalizeListValue(
string? value,
DataType dataType,
DataType? elementType,
ILogger? logger = null,
string? attributeName = null)
{
if (dataType != DataType.List || string.IsNullOrEmpty(value))
{
@@ -41,10 +49,14 @@ internal static class ImportValueNormalizer
return AttributeValueCodec.Encode(
AttributeValueCodec.Decode(value, DataType.List, elementType));
}
catch (FormatException)
catch (FormatException ex)
{
// Leave malformed values exactly as imported; the DB normalizer is
// the backstop. Never abort the import for a single bad value.
logger?.LogWarning(ex,
"Bundle import: could not normalize List value for attribute {Attribute}; " +
"importing verbatim (the central DB normalizer is the backstop).",
attributeName ?? "(unknown)");
return value;
}
}