feat(mgmt): accept + validate ElementDataType on attribute add/update

This commit is contained in:
Joseph Doherty
2026-06-16 16:05:05 -04:00
parent ad6bfc8af9
commit 1525670fe7
3 changed files with 197 additions and 4 deletions
@@ -8,8 +8,8 @@ public record DeleteTemplateCommand(int TemplateId);
public record ValidateTemplateCommand(int TemplateId);
// Template member operations
public record AddTemplateAttributeCommand(int TemplateId, string Name, string DataType, string? Value, string? Description, string? DataSourceReference, bool IsLocked);
public record UpdateTemplateAttributeCommand(int AttributeId, string Name, string DataType, string? Value, string? Description, string? DataSourceReference, bool IsLocked);
public record AddTemplateAttributeCommand(int TemplateId, string Name, string DataType, string? Value, string? Description, string? DataSourceReference, bool IsLocked, string? ElementDataType = null);
public record UpdateTemplateAttributeCommand(int AttributeId, string Name, string DataType, string? Value, string? Description, string? DataSourceReference, bool IsLocked, string? ElementDataType = null);
public record DeleteTemplateAttributeCommand(int AttributeId);
public record AddTemplateAlarmCommand(int TemplateId, string Name, string TriggerType, int PriorityLevel, string? Description, string? TriggerConfiguration, bool IsLocked);
public record UpdateTemplateAlarmCommand(int AlarmId, string Name, string TriggerType, int PriorityLevel, string? Description, string? TriggerConfiguration, bool IsLocked);
@@ -1442,9 +1442,13 @@ public class ManagementActor : ReceiveActor
private static async Task<object?> HandleAddAttribute(IServiceProvider sp, AddTemplateAttributeCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var dataType = Enum.Parse<Commons.Types.Enums.DataType>(cmd.DataType, ignoreCase: true);
var elementType = ParseElementDataType(cmd.ElementDataType);
ValidateAttributeTypes(cmd.Name, dataType, elementType, cmd.Value);
var attr = new TemplateAttribute(cmd.Name)
{
DataType = Enum.Parse<ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.DataType>(cmd.DataType, ignoreCase: true),
DataType = dataType,
ElementDataType = elementType,
Value = cmd.Value,
Description = cmd.Description,
DataSourceReference = cmd.DataSourceReference,
@@ -1457,9 +1461,13 @@ public class ManagementActor : ReceiveActor
private static async Task<object?> HandleUpdateAttribute(IServiceProvider sp, UpdateTemplateAttributeCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();
var dataType = Enum.Parse<Commons.Types.Enums.DataType>(cmd.DataType, ignoreCase: true);
var elementType = ParseElementDataType(cmd.ElementDataType);
ValidateAttributeTypes(cmd.Name, dataType, elementType, cmd.Value);
var attr = new TemplateAttribute(cmd.Name)
{
DataType = Enum.Parse<ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.DataType>(cmd.DataType, ignoreCase: true),
DataType = dataType,
ElementDataType = elementType,
Value = cmd.Value,
Description = cmd.Description,
DataSourceReference = cmd.DataSourceReference,
@@ -1469,6 +1477,57 @@ public class ManagementActor : ReceiveActor
return result.IsSuccess ? result.Value : throw new ManagementCommandException(result.Error);
}
/// <summary>
/// Parses an optional element data type token. Returns null when the token is
/// empty/whitespace; throws <see cref="ManagementCommandException"/> on an
/// unrecognised type name.
/// </summary>
private static Commons.Types.Enums.DataType? ParseElementDataType(string? elementDataType)
{
if (string.IsNullOrWhiteSpace(elementDataType)) return null;
if (!Enum.TryParse<Commons.Types.Enums.DataType>(elementDataType, ignoreCase: true, out var parsed))
throw new ManagementCommandException($"Unrecognised element type '{elementDataType}'.");
return parsed;
}
/// <summary>
/// Validates the (DataType, ElementDataType, Value) triple shared by the add
/// and update attribute handlers. Throws <see cref="ManagementCommandException"/>
/// on any violation:
/// <list type="bullet">
/// <item>List attributes require a valid scalar element type.</item>
/// <item>Scalar attributes may not carry an element type.</item>
/// <item>A List default value must decode against the declared element type.</item>
/// </list>
/// </summary>
private static void ValidateAttributeTypes(
string name, Commons.Types.Enums.DataType dataType, Commons.Types.Enums.DataType? elementType, string? value)
{
if (dataType == Commons.Types.Enums.DataType.List)
{
if (elementType is null || !Commons.Types.AttributeValueCodec.IsValidElementType(elementType.Value))
throw new ManagementCommandException(
$"List attribute '{name}' requires a valid element type (String, Int32, Float, Double, Boolean, DateTime).");
if (!string.IsNullOrWhiteSpace(value))
{
try
{
Commons.Types.AttributeValueCodec.Decode(value, Commons.Types.Enums.DataType.List, elementType);
}
catch (FormatException ex)
{
throw new ManagementCommandException(
$"List attribute '{name}' has an invalid list value: {ex.Message}");
}
}
}
else if (elementType is not null)
{
throw new ManagementCommandException("Element type is only valid on List attributes.");
}
}
private static async Task<object?> HandleDeleteAttribute(IServiceProvider sp, DeleteTemplateAttributeCommand cmd, string user)
{
var svc = sp.GetRequiredService<TemplateService>();