fix(triggers): bound expression evaluation, align AlarmActor error handling, dedupe config parsing

This commit is contained in:
Joseph Doherty
2026-05-16 05:43:18 -04:00
parent 41c3fa3d84
commit 78b10d00d8
4 changed files with 80 additions and 45 deletions

View File

@@ -210,11 +210,21 @@ public class ScriptActor : ReceiveActor, IWithTimers
try
{
var globals = new TriggerExpressionGlobals(_attributeSnapshot);
var state = _compiledTriggerExpression.RunAsync(globals).GetAwaiter().GetResult();
// Bound evaluation with a short timeout. The CancellationToken
// covers cooperative/async cases; a pathological CPU-bound
// expression is not fully interruptible. Acceptable because
// trigger expressions are authored by trusted Design-role users
// and are compile-checked pre-deployment.
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
var state = _compiledTriggerExpression
.RunAsync(globals, cancellationToken: cts.Token)
.GetAwaiter().GetResult();
result = state.ReturnValue is bool b && b;
}
catch (Exception ex)
{
// OperationCanceledException (timeout) falls through here too,
// and is correctly treated as false.
LogExpressionError(ex);
result = false;
}
@@ -346,16 +356,8 @@ public class ScriptActor : ReceiveActor, IWithTimers
private static ExpressionTriggerConfig? ParseExpressionTrigger(string? json)
{
if (string.IsNullOrEmpty(json)) return null;
try
{
var doc = JsonDocument.Parse(json);
var expr = doc.RootElement.TryGetProperty("expression", out var e)
? e.GetString()
: null;
return string.IsNullOrWhiteSpace(expr) ? null : new ExpressionTriggerConfig(expr);
}
catch { return null; }
var expr = TriggerExpressionGlobals.ExtractExpression(json);
return expr == null ? null : new ExpressionTriggerConfig(expr);
}
private static IntervalTriggerConfig? ParseIntervalTrigger(string? json)