| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- using Comal.Classes;
- using Expressive;
- using InABox.Core;
- using InABox.Database;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace PRS.Shared.Events;
- public class EventData<T, TDataModel>
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- public T Event { get; set; }
- /// <summary>
- /// A list of triggers for this event. If any of the triggers match, the event runs.
- /// </summary>
- public List<ITrigger<T, TDataModel>> Triggers { get; set; }
- public List<IEventAction<T>> Actions { get; set; }
- public EventData(T eventData)
- {
- Event = eventData;
- Triggers = new List<ITrigger<T, TDataModel>>();
- Actions = new List<IEventAction<T>>();
- }
- public Notification GenerateNotification(TDataModel model) => Event.GenerateNotification(model);
- }
- public static class EventUtils
- {
- private static bool Check<T, TDataModel>(EventData<T, TDataModel> ev, TDataModel dataModel)
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- return ev.Triggers.Any(x => x.Check(dataModel));
- }
- public static void Run<T, TDataModel>(IStore store, Event ev, EventData<T, TDataModel> evData, TDataModel dataModel)
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- if (!Check(evData, dataModel)) return;
- foreach(var action in evData.Actions)
- {
- IEvent.RunAction(evData.Event, dataModel, action);
- }
- NotifySubscribers(store, ev, evData, dataModel);
- }
- private static void NotifySubscribers<T, TDataModel>(IStore store, Event ev, EventData<T, TDataModel> evData, TDataModel dataModel)
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- string? description;
- if (ev.NotificationExpression.IsNullOrWhiteSpace())
- {
- description = null;
- }
- else
- {
- var descriptionExpr = new CoreExpression(ev.NotificationExpression, typeof(string));
- if(!descriptionExpr.TryEvaluate<string>(dataModel).Get(out description, out var error))
- {
- CoreUtils.LogException(store.UserID, error, extra: "Error notifying subscribers", store.Logger);
- return;
- }
- }
- var subscribers = store.Provider.Query(
- new Filter<EventSubscriber>(x => x.Event.ID).IsEqualTo(ev.ID),
- Columns.None<EventSubscriber>().Add(x => x.Employee.ID))
- .ToArray<EventSubscriber>();
- var notifications = new List<Notification>();
- foreach(var subscriber in subscribers)
- {
- var notification = evData.GenerateNotification(dataModel);
- notification.Employee.CopyFrom(subscriber.Employee);
- if(description is not null)
- {
- notification.Description = description;
- }
- notifications.Add(notification);
- }
- store.Provider.Save(notifications);
- }
- }
- #region DataModel Definition
- public interface IEventVariable
- {
- string Name { get; set; }
- Type Type { get; set; }
- }
- public class StandardEventVariable : IEventVariable
- {
- public string Name { get; set; }
- public Type Type { get; set; }
- public StandardEventVariable(string name, Type type)
- {
- Name = name;
- Type = type;
- }
- }
- public class ListEventVariable : IEventVariable
- {
- public string Name { get; set; }
- public Type Type { get; set; }
- public ListEventVariable(string name, Type type)
- {
- Name = name;
- Type = type;
- }
- }
- public interface IEventDataModelDefinition
- {
- IEnumerable<IEventVariable> GetVariables();
- IEventVariable? GetVariable(string name);
- }
- #endregion
- #region DataModel
- public interface IEventDataModel : IVariableProvider
- {
- bool TryGetVariable(string name, out object? value);
- bool IVariableProvider.TryGetValue(string variableName, out object? value)
- {
- return TryGetVariable(variableName, out value);
- }
- T RootModel<T>()
- where T : IEventDataModel
- {
- if(this is IChildEventDataModel child)
- {
- return child.Parent.RootModel<T>();
- }
- else if(this is T root)
- {
- return root;
- }
- else
- {
- throw new Exception($"Root model of wrong type; expected {typeof(T).FullName}; got {GetType().FullName}");
- }
- }
- }
- public interface ITypedEventDataModel : IEventDataModel
- {
- public Type EntityType { get; }
- }
- public interface IChildEventDataModel : IEventDataModel
- {
- IEventDataModel Parent { get; }
- }
- public class ChildEventDataModel : IChildEventDataModel
- {
- public IEventDataModel Parent { get; set; }
- public Dictionary<string, object?> Values { get; set; } = new Dictionary<string, object?>();
- public ChildEventDataModel(IEventDataModel parent)
- {
- Parent = parent;
- }
- public bool TryGetVariable(string name, out object? value)
- {
- return Values.TryGetValue(name, out value)
- || Parent.TryGetVariable(name, out value);
- }
- }
- #endregion
- public interface IEvent
- {
- IEventDataModelDefinition DataModelDefinition();
- public static void RunAction(IEvent ev, IEventDataModel model, IEventAction action)
- {
- var dataModelDef = ev.DataModelDefinition();
- var values = new List<(string name, object? value)[]>() { Array.Empty<(string, object?)>() };
- var vars = action.ReferencedVariables();
- foreach(var variable in vars)
- {
- var varDef = dataModelDef.GetVariable(variable);
- if(varDef is ListEventVariable)
- {
- if (model.TryGetVariable(varDef.Name, out var list) && list is IEnumerable enumerable)
- {
- var oldValues = values;
- values = new List<(string name, object? value)[]>();
- foreach(var item in enumerable)
- {
- foreach(var valueList in oldValues)
- {
- values.Add(valueList.Concatenate(new (string name, object? value)[]
- {
- (varDef.Name, item)
- }));
- }
- }
- }
- else
- {
- values.Clear();
- break;
- }
- }
- }
- if(values.Count > 0 && (values.Count > 1 || values[0].Length > 0))
- {
- var subModel = new ChildEventDataModel(model);
- foreach(var valueSet in values)
- {
- subModel.Values.Clear();
- foreach(var (name, value) in valueSet)
- {
- subModel.Values[name] = value;
- }
- action.Execute(subModel);
- }
- }
- else
- {
- action.Execute(model);
- }
- }
- }
- public interface IEvent<TDataModel> : IEvent
- {
- Notification GenerateNotification(TDataModel model);
- }
- public interface ITrigger<TEvent, TDataModel>
- where TEvent : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- bool Check(TDataModel dataModel);
- }
- public interface IEventAction
- {
- IEnumerable<string> ReferencedVariables();
- object? Execute(IEventDataModel dataModel);
- }
- public interface IEventAction<TEvent> : IEventAction
- where TEvent : IEvent
- {
- }
|