using MessengerApi.Configuration.Model; using MessengerApi.Contracts.Factories; using MessengerApi.Db.Entities; using MessengerApi.Models; using Microsoft.EntityFrameworkCore; using System.Text; namespace MessengerApi.Handlers { public class UserConfigHandler { private readonly MessengerConfiguration configuration; private readonly ILogger logger; private readonly IDbContextFactory dbContextFactory; public UserConfigHandler( MessengerConfiguration configuration, ILogger logger, IDbContextFactory dbContextFactory) { this.configuration = configuration; this.logger = logger; this.dbContextFactory = dbContextFactory; } public async Task GetItemsFromFile(FileInfo file) { if (file == null) { throw new InvalidOperationException("No file provided."); } else if (File.Exists(file.FullName) == false) { throw new FileNotFoundException($"Userfile doesn't exist: {this.configuration.UsersConfig.FullName}."); } var lines = await File.ReadAllLinesAsync(file.FullName, Encoding.UTF8); var collection = new List>(); foreach (var line in lines) { var columns = line.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); var item = new UserIngestionItem(); item.UserName = columns[0]; item.IsEnabled = bool.Parse(columns[1]); item.Id = Guid.Parse(columns[2]); item.ApiKey = Guid.Parse(columns[3]); var recipients = columns.Length == 5 ? columns[4] : null; collection.Add(new Tuple(item, recipients)); this.logger.Trace($"Reading user {item.UserName} with ID {item.Id}"); } foreach (var item in collection) { if(item.Item2 == null) { item.Item1.CanSendTo = []; continue; } var names = item.Item2.Split( ',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); item.Item1.CanSendTo = names .Select(x => collection.Single(c => c.Item1.UserName == x).Item1) .ToArray(); this.logger.Trace($"User {item.Item1.UserName} has buddies {item.Item2}."); } return collection.Select(x => x.Item1).ToArray(); } public Task GetOperationsFromItems(UserIngestionItem[] fileItems) { if(fileItems == null) { throw new ArgumentNullException(nameof(fileItems)); } using var context = this.dbContextFactory.CreateDbContext(); var existingUsers = context.Users.ToList(); var existingRoutes = context.UserRoutes.Include(x => x.From).Include(x => x.To).ToList(); var collection = new List(); foreach (var item in fileItems) { var dbItem = context.Users.SingleOrDefault(x => x.Id == item.Id); if (dbItem == null) { collection.Add(new UserIngestionOperation { Type = UserIngestionOperationTypes.Create, Subtype = UserIngestionOperationSubtypes.AddUser, Username = item.UserName, Operation = (c) => { c.Users.Local.Add(new User { Id = item.Id, ApiKey = item.ApiKey, IsEnabled = item.IsEnabled, Name = item.UserName, }); } }); } else { if(dbItem.ApiKey != item.ApiKey) { collection.Add(new UserIngestionOperation { Type = UserIngestionOperationTypes.Update, Subtype = UserIngestionOperationSubtypes.ChangeUserApiKey, Username = item.UserName, Operation = (c) => { c.Users.Single(x => x.Id == item.ApiKey).ApiKey = item.ApiKey; } }); } if(dbItem.IsEnabled != item.IsEnabled) { collection.Add(new UserIngestionOperation { Type = UserIngestionOperationTypes.Update, Subtype = UserIngestionOperationSubtypes.ChangeUserIsEnabled, Username = item.UserName, Operation = (c) => { c.Users.Single(x => x.Id == item.ApiKey).IsEnabled = item.IsEnabled; } }); } if (dbItem.Name != item.UserName) { collection.Add(new UserIngestionOperation { Type = UserIngestionOperationTypes.Update, Subtype = UserIngestionOperationSubtypes.ChangeUserName, Username = item.UserName, Operation = (c) => { c.Users.Single(x => x.Id == item.ApiKey).Name = item.UserName; } }); } } this.logger.Trace($"Processed line with {item.UserName}."); } this.logger.Debug("Finished evaluating add/update users, doing routes."); foreach(var item in fileItems) { foreach(var com in item.CanSendTo) { this.logger.Trace($"Validating if {item.UserName} can send to {com.UserName}."); var dbRoute = context.UserRoutes .Include(x => x.From) .Include(x => x.To) .SingleOrDefault(x => x.From.Id == item.Id && x.To.Id == com.Id); if(dbRoute == null) { collection.Add(new UserIngestionOperation { Type = UserIngestionOperationTypes.Create, Subtype = UserIngestionOperationSubtypes.AddUserRoute, Username = item.UserName, Operation = (c) => { var from = c.Users .SingleOrDefault(x => x.Id == item.Id) ?? c.Users.Local.Single(x => x.Id == item.Id); var to = c.Users .SingleOrDefault(x => x.Id == com.Id) ?? c.Users.Local.Single(x => x.Id == com.Id); c.Add(new UserRoute { From = from, To = to }); } }); } } } this.logger.Debug("Finished evaluating add userroutes, doing user deletes."); foreach(var existingDbUser in existingUsers) { if(fileItems.Any(x=>x.Id == existingDbUser.Id) == false) { collection.Add(new UserIngestionOperation { Username = existingDbUser.Name, Type = UserIngestionOperationTypes.Delete, Subtype = UserIngestionOperationSubtypes.RemoveUser, Operation = (c) => { c.Remove(c.Users.Single(x => x.Id == existingDbUser.Id)); } }); } } foreach (var existingRoute in existingRoutes) { if (fileItems.Any(fi => fi.CanSendTo.Any(fic => fi.Id == existingRoute.From.Id && fic.Id == existingRoute.To.Id)) == false) { collection.Add(new UserIngestionOperation { Username = existingRoute.From.Name, Type = UserIngestionOperationTypes.Delete, Subtype = UserIngestionOperationSubtypes.RemoveUserRoute, Operation = (c) => { c.Remove(c.UserRoutes.Single(x => x.Id == existingRoute.Id)); } }); } } return Task.FromResult(collection.ToArray()); } public async Task UpdateFromFile(FileInfo file) { var fileItems = await this.GetItemsFromFile(file); var operations = await this.GetOperationsFromItems(fileItems); using var context = this.dbContextFactory.CreateDbContext(); foreach(var operation in operations) { this.logger.Info(operation.ToString()); operation.Operation(context); } if(operations.Any()) { await context.SaveChangesAsync(); } } } }