2020-08-17 22:21:11 -04:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using SharedLibraryCore.Database.Models;
|
|
|
|
|
using SharedLibraryCore.Dtos;
|
|
|
|
|
using SharedLibraryCore.Interfaces;
|
|
|
|
|
using SharedLibraryCore.QueryHelper;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
2020-11-11 18:31:26 -05:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
2020-08-17 22:21:11 -04:00
|
|
|
|
|
|
|
|
|
namespace IW4MAdmin.Application.Misc
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// implementation of IMetaService
|
|
|
|
|
/// used to add and retrieve runtime and persistent meta
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class MetaService : IMetaService
|
|
|
|
|
{
|
|
|
|
|
private readonly IDictionary<MetaType, List<dynamic>> _metaActions;
|
|
|
|
|
private readonly IDatabaseContextFactory _contextFactory;
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
2020-11-11 18:31:26 -05:00
|
|
|
|
public MetaService(ILogger<MetaService> logger, IDatabaseContextFactory contextFactory)
|
2020-08-17 22:21:11 -04:00
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_metaActions = new Dictionary<MetaType, List<dynamic>>();
|
|
|
|
|
_contextFactory = contextFactory;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-24 12:47:19 -05:00
|
|
|
|
public async Task AddPersistentMeta(string metaKey, string metaValue, EFClient client, EFMeta linkedMeta = null)
|
2020-08-17 22:21:11 -04:00
|
|
|
|
{
|
|
|
|
|
// this seems to happen if the client disconnects before they've had time to authenticate and be added
|
|
|
|
|
if (client.ClientId < 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-29 17:01:52 -05:00
|
|
|
|
await using var ctx = _contextFactory.CreateContext();
|
2020-08-17 22:21:11 -04:00
|
|
|
|
|
|
|
|
|
var existingMeta = await ctx.EFMeta
|
|
|
|
|
.Where(_meta => _meta.Key == metaKey)
|
|
|
|
|
.Where(_meta => _meta.ClientId == client.ClientId)
|
|
|
|
|
.FirstOrDefaultAsync();
|
|
|
|
|
|
|
|
|
|
if (existingMeta != null)
|
|
|
|
|
{
|
|
|
|
|
existingMeta.Value = metaValue;
|
|
|
|
|
existingMeta.Updated = DateTime.UtcNow;
|
2021-01-24 12:47:19 -05:00
|
|
|
|
existingMeta.LinkedMetaId = linkedMeta?.MetaId;
|
2020-08-17 22:21:11 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ctx.EFMeta.Add(new EFMeta()
|
|
|
|
|
{
|
|
|
|
|
ClientId = client.ClientId,
|
|
|
|
|
Created = DateTime.UtcNow,
|
|
|
|
|
Key = metaKey,
|
2021-01-24 12:47:19 -05:00
|
|
|
|
Value = metaValue,
|
|
|
|
|
LinkedMetaId = linkedMeta?.MetaId
|
2020-08-17 22:21:11 -04:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await ctx.SaveChangesAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-24 12:47:19 -05:00
|
|
|
|
public async Task AddPersistentMeta(string metaKey, string metaValue)
|
|
|
|
|
{
|
|
|
|
|
await using var ctx = _contextFactory.CreateContext();
|
|
|
|
|
|
|
|
|
|
var existingMeta = await ctx.EFMeta
|
|
|
|
|
.Where(meta => meta.Key == metaKey)
|
|
|
|
|
.Where(meta => meta.ClientId == null)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
var matchValues = existingMeta
|
|
|
|
|
.Where(meta => meta.Value == metaValue)
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
if (matchValues.Any())
|
|
|
|
|
{
|
|
|
|
|
foreach (var meta in matchValues)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug("Updating existing meta with key {key} and id {id}", meta.Key, meta.MetaId);
|
|
|
|
|
meta.Value = metaValue;
|
|
|
|
|
meta.Updated = DateTime.UtcNow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await ctx.SaveChangesAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug("Adding new meta with key {key}", metaKey);
|
|
|
|
|
|
|
|
|
|
ctx.EFMeta.Add(new EFMeta()
|
|
|
|
|
{
|
|
|
|
|
Created = DateTime.UtcNow,
|
|
|
|
|
Key = metaKey,
|
|
|
|
|
Value = metaValue
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await ctx.SaveChangesAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task RemovePersistentMeta(string metaKey, EFClient client)
|
|
|
|
|
{
|
|
|
|
|
await using var context = _contextFactory.CreateContext();
|
|
|
|
|
|
|
|
|
|
var existingMeta = await context.EFMeta
|
|
|
|
|
.FirstOrDefaultAsync(meta => meta.Key == metaKey && meta.ClientId == client.ClientId);
|
|
|
|
|
|
|
|
|
|
if (existingMeta == null)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug("No meta with key {key} found for client id {id}", metaKey, client.ClientId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.LogDebug("Removing meta for key {key} with id {id}", metaKey, existingMeta.MetaId);
|
|
|
|
|
context.EFMeta.Remove(existingMeta);
|
|
|
|
|
await context.SaveChangesAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task RemovePersistentMeta(string metaKey, string metaValue = null)
|
|
|
|
|
{
|
|
|
|
|
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
|
|
|
|
var existingMeta = await context.EFMeta
|
|
|
|
|
.Where(meta => meta.Key == metaKey)
|
|
|
|
|
.Where(meta => meta.ClientId == null)
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
if (metaValue == null)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug("Removing all meta for key {key} with ids [{ids}] ", metaKey, string.Join(", ", existingMeta.Select(meta => meta.MetaId)));
|
|
|
|
|
existingMeta.ForEach(meta => context.Remove(existingMeta));
|
|
|
|
|
await context.SaveChangesAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var foundMeta = existingMeta.FirstOrDefault(meta => meta.Value == metaValue);
|
|
|
|
|
|
|
|
|
|
if (foundMeta != null)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug("Removing meta for key {key} with id {id}", metaKey, foundMeta.MetaId);
|
|
|
|
|
context.Remove(foundMeta);
|
|
|
|
|
await context.SaveChangesAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-17 22:21:11 -04:00
|
|
|
|
public async Task<EFMeta> GetPersistentMeta(string metaKey, EFClient client)
|
|
|
|
|
{
|
2020-11-29 17:01:52 -05:00
|
|
|
|
await using var ctx = _contextFactory.CreateContext(enableTracking: false);
|
2020-08-17 22:21:11 -04:00
|
|
|
|
|
|
|
|
|
return await ctx.EFMeta
|
|
|
|
|
.Where(_meta => _meta.Key == metaKey)
|
|
|
|
|
.Where(_meta => _meta.ClientId == client.ClientId)
|
|
|
|
|
.Select(_meta => new EFMeta()
|
|
|
|
|
{
|
|
|
|
|
MetaId = _meta.MetaId,
|
|
|
|
|
Key = _meta.Key,
|
|
|
|
|
ClientId = _meta.ClientId,
|
2021-01-24 12:47:19 -05:00
|
|
|
|
Value = _meta.Value,
|
|
|
|
|
LinkedMetaId = _meta.LinkedMetaId,
|
|
|
|
|
LinkedMeta = _meta.LinkedMetaId != null ? new EFMeta()
|
|
|
|
|
{
|
|
|
|
|
MetaId = _meta.LinkedMeta.MetaId,
|
|
|
|
|
Key = _meta.LinkedMeta.Key,
|
|
|
|
|
Value = _meta.LinkedMeta.Value
|
|
|
|
|
} : null
|
2020-08-17 22:21:11 -04:00
|
|
|
|
})
|
|
|
|
|
.FirstOrDefaultAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-24 12:47:19 -05:00
|
|
|
|
public async Task<IEnumerable<EFMeta>> GetPersistentMeta(string metaKey)
|
|
|
|
|
{
|
|
|
|
|
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
|
|
|
|
return await context.EFMeta
|
|
|
|
|
.Where(meta => meta.Key == metaKey)
|
|
|
|
|
.Where(meta => meta.ClientId == null)
|
|
|
|
|
.Select(meta => new EFMeta
|
|
|
|
|
{
|
|
|
|
|
MetaId = meta.MetaId,
|
|
|
|
|
Key = meta.Key,
|
|
|
|
|
ClientId = meta.ClientId,
|
|
|
|
|
Value = meta.Value,
|
|
|
|
|
})
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-17 22:21:11 -04:00
|
|
|
|
public void AddRuntimeMeta<T, V>(MetaType metaKey, Func<T, Task<IEnumerable<V>>> metaAction) where T : PaginationRequest where V : IClientMeta
|
|
|
|
|
{
|
|
|
|
|
if (!_metaActions.ContainsKey(metaKey))
|
|
|
|
|
{
|
|
|
|
|
_metaActions.Add(metaKey, new List<dynamic>() { metaAction });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_metaActions[metaKey].Add(metaAction);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<IEnumerable<IClientMeta>> GetRuntimeMeta(ClientPaginationRequest request)
|
|
|
|
|
{
|
|
|
|
|
var meta = new List<IClientMeta>();
|
|
|
|
|
|
|
|
|
|
foreach (var (type, actions) in _metaActions)
|
|
|
|
|
{
|
|
|
|
|
// information is not listed chronologically
|
|
|
|
|
if (type != MetaType.Information)
|
|
|
|
|
{
|
|
|
|
|
var metaItems = await actions[0](request);
|
|
|
|
|
meta.AddRange(metaItems);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return meta.OrderByDescending(_meta => _meta.When)
|
|
|
|
|
.Take(request.Count)
|
|
|
|
|
.ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<IEnumerable<T>> GetRuntimeMeta<T>(ClientPaginationRequest request, MetaType metaType) where T : IClientMeta
|
|
|
|
|
{
|
|
|
|
|
IEnumerable<T> meta;
|
|
|
|
|
if (metaType == MetaType.Information)
|
|
|
|
|
{
|
|
|
|
|
var allMeta = new List<T>();
|
|
|
|
|
|
|
|
|
|
foreach (var individualMetaRegistration in _metaActions[metaType])
|
|
|
|
|
{
|
|
|
|
|
allMeta.AddRange(await individualMetaRegistration(request));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ProcessInformationMeta(allMeta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
meta = await _metaActions[metaType][0](request) as IEnumerable<T>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return meta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static IEnumerable<T> ProcessInformationMeta<T>(IEnumerable<T> meta) where T : IClientMeta
|
|
|
|
|
{
|
|
|
|
|
var table = new List<List<T>>();
|
|
|
|
|
var metaWithColumn = meta
|
|
|
|
|
.Where(_meta => _meta.Column != null);
|
|
|
|
|
|
|
|
|
|
var columnGrouping = metaWithColumn
|
|
|
|
|
.GroupBy(_meta => _meta.Column);
|
|
|
|
|
|
|
|
|
|
var metaToSort = meta.Except(metaWithColumn).ToList();
|
|
|
|
|
|
|
|
|
|
foreach (var metaItem in columnGrouping)
|
|
|
|
|
{
|
|
|
|
|
table.Add(new List<T>(metaItem));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (metaToSort.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var sortingMeta = metaToSort.First();
|
|
|
|
|
|
|
|
|
|
int indexOfSmallestColumn()
|
|
|
|
|
{
|
|
|
|
|
int index = 0;
|
|
|
|
|
int smallestColumnSize = int.MaxValue;
|
|
|
|
|
for (int i = 0; i < table.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (table[i].Count < smallestColumnSize)
|
|
|
|
|
{
|
|
|
|
|
smallestColumnSize = table[i].Count;
|
|
|
|
|
index = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int columnIndex = indexOfSmallestColumn();
|
|
|
|
|
|
|
|
|
|
sortingMeta.Column = columnIndex;
|
|
|
|
|
sortingMeta.Order = columnGrouping
|
|
|
|
|
.First(_group => _group.Key == columnIndex)
|
|
|
|
|
.Count();
|
|
|
|
|
|
|
|
|
|
table[columnIndex].Add(sortingMeta);
|
|
|
|
|
|
|
|
|
|
metaToSort.Remove(sortingMeta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return meta;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|