255 lines
9.8 KiB
C#
255 lines
9.8 KiB
C#
using Knoks.Core.Entities;
|
||
using Knoks.Core.Logic.Interfaces;
|
||
using Knoks.Framework.Content;
|
||
using Knoks.Framework.Services;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.Logging;
|
||
using Newtonsoft.Json.Linq;
|
||
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Knoks.Core.Logic.Managers
|
||
{
|
||
public class EventManager : IEventManager, IInitializable, IDisposable
|
||
{
|
||
private class EventInfo
|
||
{
|
||
public object Source { get; set; }
|
||
public EventAction EventAction { get; set; }
|
||
public object[] EventData { get; set; }
|
||
public DateTime Timestamp { get; set; }
|
||
public JObject EventArgs { get; set; }
|
||
|
||
public EventInfo(object source, EventAction eventAction, object[] eventData, DateTime timestamp)
|
||
{
|
||
Source = source;
|
||
EventAction = eventAction;
|
||
EventData = eventData;
|
||
Timestamp = timestamp;
|
||
}
|
||
}
|
||
|
||
private readonly Dictionary<Type, Action<object, JObject>> _ojectParsers = new Dictionary<Type, Action<object, JObject>>();
|
||
private readonly Dictionary<EventAction, IList<Action<JObject>>> _eventDelegates = new Dictionary<EventAction, IList<Action<JObject>>>();
|
||
private readonly BlockingCollection<EventInfo> _eventQueue = new BlockingCollection<EventInfo>();
|
||
|
||
private Task _eventConsumingTask;
|
||
|
||
private readonly ILogger<EventManager> _logger;
|
||
|
||
private readonly IServiceProvider _serviceProvider;
|
||
private T Get<T>() { return _serviceProvider.GetRequiredService<T>(); }
|
||
|
||
public EventManager(ILogger<EventManager> logger, IServiceProvider serviceProvider)
|
||
{
|
||
_logger = logger;
|
||
|
||
//Due to cross references (for example: UserManager) was decided to using DI on demand via IServiceProvider.
|
||
//So, don't try to call IServiceProvider.GetService<T> in constructot,
|
||
//It will lead you to "Process is terminated due to StackOverflowException."
|
||
//Instead use T Get<T>()
|
||
_serviceProvider = serviceProvider;
|
||
}
|
||
|
||
public Task Initialize()
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
InitObjectParsers();
|
||
InitEventDelegates();
|
||
|
||
_eventConsumingTask = Task.Factory.StartNew(() =>
|
||
{
|
||
foreach (var eventInfo in _eventQueue.GetConsumingEnumerable())
|
||
{
|
||
ProcessEvent(eventInfo);
|
||
}
|
||
}, TaskCreationOptions.LongRunning);
|
||
});
|
||
}
|
||
|
||
private void ProcessEvent(EventInfo eventInfo)
|
||
{
|
||
try
|
||
{
|
||
if (_eventDelegates.TryGetValue(eventInfo.EventAction, out IList<Action<JObject>> actions))
|
||
{
|
||
if (actions.Count > 0)
|
||
{
|
||
eventInfo.EventArgs = JObject.FromObject(new
|
||
{
|
||
EventActionId = (int)eventInfo.EventAction,
|
||
EventActionName = eventInfo.EventAction.ToString(),
|
||
eventInfo.Timestamp
|
||
});
|
||
|
||
foreach (var obj in eventInfo.EventData)
|
||
{
|
||
//if (!_ojectParsers.Keys.Contains(obj.GetType()))
|
||
//{
|
||
// _ojectParsers.Add(obj.GetType(), null);
|
||
//}
|
||
//var o = (obj, eventInfo.EventArgs);
|
||
_ojectParsers[obj.GetType()](obj, eventInfo.EventArgs);
|
||
}
|
||
}
|
||
|
||
foreach (var action in actions)
|
||
{
|
||
Task.Run(() =>
|
||
{
|
||
try
|
||
{
|
||
action(eventInfo.EventArgs);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError($"Process action - {eventInfo.EventAction} error.{Environment.NewLine}{ex}");
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError($"Process event - {eventInfo.EventAction} error.{Environment.NewLine}{ex}");
|
||
}
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
try
|
||
{
|
||
if (_eventQueue != null)
|
||
_eventQueue.CompleteAdding();
|
||
|
||
if (_eventConsumingTask != null)
|
||
_eventConsumingTask.Wait();
|
||
}
|
||
finally
|
||
{
|
||
if (_eventQueue != null)
|
||
_eventQueue.Dispose();
|
||
}
|
||
}
|
||
|
||
public async Task FireЕvent(object source, EventAction eventAction, params object[] eventData)
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
var timestamp = Get<ISystemConfigurationManager>().SystemDateTime;
|
||
|
||
if (!FireЕventParams[eventAction].SequenceEqual(eventData.Select(i => i.GetType())))
|
||
throw new InvalidOperationException($"Unexpected parameters sequence or type(s). The expected format for '{eventAction}' is [{ string.Join(", ", FireЕventParams[eventAction].Select(i => i.FullName)) }]");
|
||
|
||
_eventQueue.Add(new EventInfo(source, eventAction, eventData, timestamp));
|
||
});
|
||
}
|
||
|
||
//-- Example for multiple methods per EventAction variation --
|
||
//private static Dictionary<EventAction, Type[][]> FireЕventParams = new Dictionary<EventAction, Type[][]>
|
||
//{
|
||
// [EventAction.LeadCreated] = new[]
|
||
// {
|
||
// new [] { typeof(LoanRequest), typeof(LoanRequest) },
|
||
// new [] { typeof(LoanRequest), typeof(LoanRequest) }
|
||
// }
|
||
//};
|
||
|
||
private static Dictionary<EventAction, Type[]> FireЕventParams = new Dictionary<EventAction, Type[]>
|
||
{
|
||
[EventAction.UserCreated] = new[] { typeof(User) },
|
||
[EventAction.ResetPassword] = new[] { typeof(UserPasswordReset) }
|
||
};
|
||
|
||
private void ParseUser(long userId, JObject eventArgs)
|
||
{
|
||
var user = Get<IUserManager>().GetUsers(userId).Result.Single();
|
||
_ojectParsers[user.GetType()](user, eventArgs);
|
||
}
|
||
|
||
private void ParseUser(string email, JObject eventArgs)
|
||
{
|
||
var user = Get<IUserManager>().GetUserByEmail(email).Result;
|
||
_ojectParsers[user.GetType()](user, eventArgs);
|
||
}
|
||
|
||
private void InitObjectParsers()
|
||
{
|
||
_ojectParsers.Add(typeof(User), (obj, eventArgs) =>
|
||
{
|
||
var user = (User)obj;
|
||
eventArgs["User"] = new JObject
|
||
{
|
||
["UserId"] = user.UserId,
|
||
["Email"] = user.Email,
|
||
["FirstName"] = user.FirstName,
|
||
["LastName"] = user.LastName,
|
||
["UserEmail"] = user.Email,
|
||
["Phone"] = user.Phone,
|
||
["CountryId"] = user.CountryId,
|
||
["LanguageId"] = user.LanguageId,
|
||
["LanguageCode"] = "en"
|
||
};
|
||
});
|
||
|
||
_ojectParsers.Add(typeof(UserPasswordReset), (obj, eventArgs) =>
|
||
{
|
||
var userPasswordReset = (UserPasswordReset)obj;
|
||
if (!string.IsNullOrEmpty(userPasswordReset.Email))
|
||
{
|
||
ParseUser(userPasswordReset.Email, eventArgs);
|
||
}
|
||
eventArgs["UserPasswordReset"] = new JObject
|
||
{
|
||
["Email"] = userPasswordReset.Email,
|
||
["Link"] = userPasswordReset.Link
|
||
};
|
||
});
|
||
}
|
||
|
||
private void InitEventDelegates()
|
||
{
|
||
//_eventDelegates.Add(EventAction.UserCreated, new List<Action<JObject>>
|
||
//{
|
||
// (eventArgs) => //send email welcome
|
||
// {
|
||
// _logger.LogInformation($"############### Event type: {eventArgs["EventActionName"]} ##################");
|
||
// dynamic args = eventArgs;
|
||
// var contentResult = Get<IContentGenerator>().GenerateContent(new ContentSettings
|
||
// {
|
||
// Category = "Email",
|
||
// Group = "welcome",
|
||
// LanguageCode = args.User.LanguageCode,
|
||
// Data = eventArgs
|
||
// });
|
||
// }
|
||
//});
|
||
|
||
_eventDelegates.Add(EventAction.ResetPassword, new List<Action<JObject>>
|
||
{
|
||
(eventArgs) => //send password reset email
|
||
{
|
||
_logger.LogInformation($"############### Event type: {eventArgs["EventActionName"]} ##################");
|
||
dynamic args = eventArgs;
|
||
var contentResult = Get<IContentGenerator>().GenerateContent(new ContentSettings
|
||
{
|
||
Category = "Email",
|
||
Group = "resetPassword",
|
||
LanguageCode = args.User.LanguageCode,
|
||
Data = eventArgs
|
||
});
|
||
Get<IEmailService>().SendHtml(
|
||
contentResult.Model["Subject"],
|
||
contentResult.Content,
|
||
null,
|
||
eventArgs["UserPasswordReset"]["Email"].Value<string>());
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|