Knocks/BackEnd/Knoks.Core/Logic/Managers/EventManager.cs

255 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>());
}
});
}
}
}