Files
jube-fork/Jube.Engine/EntityAnalysisModelInvoke/Context/Extensions/ActivationRules/ActivationRuleActivationWatcherExtensions.cs
richard-churchman 2a3e1e981f Bug fixes relating to Inline Script enhancements in GitHub Issue #95
* Renamed GetModelFieldByEntityAnalysisModelIdParseTypeIdQuery to GetEntityAnalysisModelFieldByEntityAnalysisModelIdParseTypeIdQuery to align with database model naming.
* Fixed bugs where Inline Scripts were not being matched correctly in Completions and Soft Parsing because EntityAnalysisModelInlineScript.Id was being used instead of EntityAnalysisInlineScript.Id (affecting three areas).
* Swapped Latitude and Longitude matching in Activation Watcher processing for Activation Rules.
* Updated the Sandbox code for Inline Scripts to be less brittle and a more reliable example for client use.
2026-01-13 08:59:52 +02:00

252 lines
14 KiB
C#

/* Copyright (C) 2022-present Jube Holdings Limited.
*
* This file is part of Jube™ software.
*
* Jube™ is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* Jube™ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License along with Jube™. If not,
* see <https://www.gnu.org/licenses/>.
*/
namespace Jube.Engine.EntityAnalysisModelInvoke.Context.Extensions.ActivationRules
{
using System;
using System.Linq;
using System.Text;
using Data.Messaging;
using Data.Poco;
using Newtonsoft.Json;
using RabbitMQ.Client;
using EntityAnalysisModelActivationRule=EntityAnalysisModelManager.EntityAnalysisModel.Models.Models.EntityAnalysisModelActivationRule;
public static class ActivationRuleActivationWatcherExtensions
{
public static void ActivationRuleActivationWatcher(this Context context, EntityAnalysisModelActivationRule evaluateActivationRule,
bool suppressed, IModel rabbitMqChannel)
{
if (!evaluateActivationRule.SendToActivationWatcher || suppressed || context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelReprocessingRuleInstanceId.HasValue ||
!context.EntityAnalysisModel.Flags.EnableActivationWatcher)
{
return;
}
try
{
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} the current activation watch count is {context.EntityAnalysisModel.Counters.ActivationWatcherCount} which will be tested against the threshold {context.EntityAnalysisModel.Counters.MaxActivationWatcherThreshold}.");
}
if (!(context.EntityAnalysisModel.Counters.ActivationWatcherCount <
context.EntityAnalysisModel.Counters.MaxActivationWatcherThreshold) ||
!(context.EntityAnalysisModel.Counters.ActivationWatcherSample >= context.Random.NextDouble()))
{
return;
}
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} the current activation watch count is {context.EntityAnalysisModel.Counters.ActivationWatcherCount} which will be tested against the threshold {context.EntityAnalysisModel.Counters.MaxActivationWatcherThreshold} and selected via random sampling.");
}
var activationWatcher = new ActivationWatcher
{
BackColor = evaluateActivationRule.ResponseElevationBackColor,
ForeColor = evaluateActivationRule.ResponseElevationForeColor,
ResponseElevation = evaluateActivationRule.ResponseElevation,
ResponseElevationContent = evaluateActivationRule.ResponseElevationContent,
ActivationRuleSummary = evaluateActivationRule.Name,
TenantRegistryId = context.EntityAnalysisModel.Instance.TenantRegistryId,
CreatedDate = DateTime.Now,
Latitude = GetLatitude(context),
Longitude = GetLongitude(context),
Key = "",
KeyValue = ""
};
if (context.EntityAnalysisModelInstanceEntryPayload.Payload.ContainsKey(evaluateActivationRule
.ResponseElevationKey))
{
activationWatcher.Key = evaluateActivationRule.ResponseElevationKey;
activationWatcher.KeyValue =
context.EntityAnalysisModelInstanceEntryPayload.Payload[evaluateActivationRule
.ResponseElevationKey].AsString();
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} found key of {activationWatcher.Key} and key value of {activationWatcher.KeyValue}.");
}
}
else
{
activationWatcher.Key = evaluateActivationRule.ResponseElevationKey;
activationWatcher.KeyValue = "Missing";
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} fallen back to key of {activationWatcher.Key} and key value of {activationWatcher.KeyValue}.");
}
}
var jsonString = JsonConvert.SerializeObject(activationWatcher, context.EntityAnalysisModel.JsonSerializationHelper.DefaultJsonSerializerSettingsSettings);
var bodyBytes = Encoding.UTF8.GetBytes(jsonString);
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} has serialized the Activation Watcher Object to be dispatched.");
}
if (context.Environment.AppSettings("ActivationWatcherAllowPersist")
.Equals("True", StringComparison.OrdinalIgnoreCase))
{
context.EntityAnalysisModel.ConcurrentQueues.PersistToActivationWatcherAsync.Enqueue(activationWatcher);
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} replay is allowed so it has been sent to the database. {context.EntityAnalysisModel.Counters.ActivationWatcherCount}.");
}
}
else
{
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} replay is not allowed so it has not been sent to the database. {context.EntityAnalysisModel.Counters.ActivationWatcherCount}.");
}
}
if (context.Environment.AppSettings("StreamingActivationWatcher")
.Equals("True", StringComparison.OrdinalIgnoreCase))
{
var messaging = new Messaging(context.Environment.AppSettings("ConnectionString"), context.Log);
messaging.SendActivation(bodyBytes);
context.EntityAnalysisModel.Counters.ActivationWatcherCount += 1;
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} streaming is allowed so it has been sent to the database as a notification in the activation channel. {context.EntityAnalysisModel.Counters.ActivationWatcherCount}.");
}
}
else
{
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} streaming is not allowed so it has not been sent to the database as a notification in the activation channel. {context.EntityAnalysisModel.Counters.ActivationWatcherCount}.");
}
}
if (context.Environment.AppSettings("AMQP").Equals("True", StringComparison.OrdinalIgnoreCase))
{
var properties = rabbitMqChannel.CreateBasicProperties();
rabbitMqChannel.BasicPublish("jubeActivations", "", properties, bodyBytes);
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} AMQP is allowed so it has been published to the RabbitMQ.");
}
}
else
{
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} AMQP is not allowed, so publish has been stepped over.");
}
}
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} has sent a message to the watcher as {jsonString} the activation watcher counter has been incremented and is currently {context.EntityAnalysisModel.Counters.ActivationWatcherCount}.");
}
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
if (context.Log.IsInfoEnabled)
{
context.Log.Info(
$"Entity Invoke: GUID {context.EntityAnalysisModelInstanceEntryPayload.EntityAnalysisModelInstanceEntryGuid} and model {context.EntityAnalysisModel.Instance.Id} there has been an error in Activation Watcher processing {ex}.");
}
}
}
private static double GetLongitude(Context context)
{
var longitudeValue = 0d;
var longitudeFieldName = context.EntityAnalysisModel.Collections.EntityAnalysisModelRequestXPaths.FirstOrDefault(f => f.DataTypeId == 7)?.Name;
if (String.IsNullOrEmpty(longitudeFieldName))
{
foreach (var entityAnalysisModelInlineScriptPropertyAttribute in
context.EntityAnalysisModel.Collections.EntityAnalysisModelInlineScripts
.SelectMany(entityAnalysisModelInlineScript => entityAnalysisModelInlineScript.EntityAnalysisModelInlineScriptPropertyAttributes
.Where(entityAnalysisModelInlineScriptPropertyAttribute => entityAnalysisModelInlineScriptPropertyAttribute.Value.Latitude)))
{
longitudeFieldName = entityAnalysisModelInlineScriptPropertyAttribute.Key;
if (String.IsNullOrEmpty(longitudeFieldName))
{
return longitudeValue;
}
}
return longitudeValue;
}
if (context.EntityAnalysisModelInstanceEntryPayload.Payload.TryGetValue(longitudeFieldName, out var value))
{
longitudeValue = value;
}
return longitudeValue;
}
private static double GetLatitude(Context context)
{
var latitudeValue = 0d;
var latitudeFieldName = context.EntityAnalysisModel.Collections.EntityAnalysisModelRequestXPaths.FirstOrDefault(f => f.DataTypeId == 6)?.Name;
if (String.IsNullOrEmpty(latitudeFieldName))
{
foreach (var entityAnalysisModelInlineScriptPropertyAttribute in
context.EntityAnalysisModel.Collections.EntityAnalysisModelInlineScripts
.SelectMany(entityAnalysisModelInlineScript => entityAnalysisModelInlineScript.EntityAnalysisModelInlineScriptPropertyAttributes
.Where(entityAnalysisModelInlineScriptPropertyAttribute => entityAnalysisModelInlineScriptPropertyAttribute.Value.Latitude)))
{
latitudeFieldName = entityAnalysisModelInlineScriptPropertyAttribute.Key;
if (String.IsNullOrEmpty(latitudeFieldName))
{
return latitudeValue;
}
}
return latitudeValue;
}
if (context.EntityAnalysisModelInstanceEntryPayload.Payload.TryGetValue(latitudeFieldName, out var value))
{
latitudeValue = value;
}
return latitudeValue;
}
}
}