fix: lazy-compile API method scripts and prefix composed alarm trigger attributes

- InboundScriptExecutor lazy-compiles scripts on first request, solving
  the multi-node problem where methods created via CLI/UI were only compiled
  on the ManagementActor's node, not the node handling the HTTP request.
- ManagementActor hot-registers API method scripts on create/update/delete
  for the local node.
- FlatteningService prefixes the "attribute" field in composed alarm trigger
  configs with the composition instance name so alarms evaluate against the
  correct path-qualified attribute (e.g. CoolingTank.Level not Level).
This commit is contained in:
Joseph Doherty
2026-03-18 09:30:12 -04:00
parent db387c6613
commit da683d4fe9
4 changed files with 79 additions and 9 deletions

View File

@@ -15,9 +15,12 @@ public class InboundScriptExecutor
private readonly ILogger<InboundScriptExecutor> _logger;
private readonly Dictionary<string, Func<InboundScriptContext, Task<object?>>> _scriptHandlers = new();
public InboundScriptExecutor(ILogger<InboundScriptExecutor> logger)
private readonly IServiceProvider _serviceProvider;
public InboundScriptExecutor(ILogger<InboundScriptExecutor> logger, IServiceProvider serviceProvider)
{
_logger = logger;
_serviceProvider = serviceProvider;
}
/// <summary>
@@ -28,6 +31,14 @@ public class InboundScriptExecutor
_scriptHandlers[methodName] = handler;
}
/// <summary>
/// Removes a compiled script handler for a method name.
/// </summary>
public void RemoveHandler(string methodName)
{
_scriptHandlers.Remove(methodName);
}
/// <summary>
/// Compiles and registers a single API method script.
/// </summary>
@@ -107,16 +118,14 @@ public class InboundScriptExecutor
var context = new InboundScriptContext(parameters, route, cts.Token);
object? result;
if (_scriptHandlers.TryGetValue(method.Name, out var handler))
if (!_scriptHandlers.TryGetValue(method.Name, out var handler))
{
result = await handler(context).WaitAsync(cts.Token);
}
else
{
// No compiled handler — this means the script hasn't been registered.
// In production, we'd compile the method.Script and cache it.
return new InboundScriptResult(false, null, "Script not compiled or registered for this method");
// Lazy compile on first request (handles methods created after startup)
if (!CompileAndRegister(method))
return new InboundScriptResult(false, null, "Script compilation failed for this method");
handler = _scriptHandlers[method.Name];
}
result = await handler(context).WaitAsync(cts.Token);
var resultJson = result != null
? JsonSerializer.Serialize(result)