LANCommander/LANCommander.Server.ImportExport/ImportContext.cs

349 lines
No EOL
17 KiB
C#

using System.IO.Compression;
using AutoMapper;
using LANCommander.SDK.Enums;
using LANCommander.SDK.Helpers;
using LANCommander.Server.Data.Models;
using LANCommander.Server.ImportExport.Importers;
using LANCommander.Server.ImportExport.Models;
using LANCommander.Server.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using ZipArchive = SharpCompress.Archives.Zip.ZipArchive;
namespace LANCommander.Server.ImportExport;
public class ImportContext(
GameImporter gameImporter,
RedistributableImporter redistributableImporter,
ServerImporter serverImporter,
ActionImporter actionImporter,
ArchiveImporter archiveImporter,
CollectionImporter collectionImporter,
CustomFieldImporter customFieldImporter,
DeveloperImporter developerImporter,
PublisherImporter publisherImporter,
EngineImporter engineImporter,
GenreImporter genreImporter,
KeyImporter keyImporter,
MediaImporter mediaImporter,
MultiplayerModeImporter multiplayerModeImporter,
PlatformImporter platformImporter,
PlaySessionImporter playSessionImporter,
SaveImporter saveImporter,
SavePathImporter savePathImporter,
ScriptImporter scriptImporter,
ServerConsoleImporter serverConsoleImporter,
ServerHttpPathImporter serverHttpPathImporter,
TagImporter tagImporter,
StorageLocationService storageLocationService,
IMapper mapper,
ILogger<ImportContext> logger) : IDisposable
{
public object Manifest { get; private set; }
public BaseModel DataRecord { get; private set; }
public StorageLocation ArchiveStorageLocation { get; set; }
public ZipArchive Archive { get; private set; }
public GameImporter Games = gameImporter;
public RedistributableImporter Redistributables = redistributableImporter;
public ServerImporter Servers = serverImporter;
public ActionImporter Actions = actionImporter;
public ArchiveImporter Archives = archiveImporter;
public CollectionImporter Collections = collectionImporter;
public CustomFieldImporter CustomFields = customFieldImporter;
public DeveloperImporter Developers = developerImporter;
public EngineImporter Engines = engineImporter;
public GenreImporter Genres = genreImporter;
public KeyImporter Keys = keyImporter;
public MediaImporter Media = mediaImporter;
public MultiplayerModeImporter MultiplayerModes = multiplayerModeImporter;
public PlatformImporter Platforms = platformImporter;
public PlaySessionImporter PlaySessions = playSessionImporter;
public PublisherImporter Publishers = publisherImporter;
public SaveImporter Saves = saveImporter;
public SavePathImporter SavePaths = savePathImporter;
public ScriptImporter Scripts = scriptImporter;
public ServerConsoleImporter ServerConsoles = serverConsoleImporter;
public ServerHttpPathImporter ServerHttpPaths = serverHttpPathImporter;
public TagImporter Tags = tagImporter;
public int Remaining => _queue.Count;
public int Processed => _queue.Count(qi => qi.Processed);
public int Total => _queue.Count;
private List<ImportQueueItem> _queue { get; } = new();
private IEnumerable<Guid> _selectedRecordIds { get; set; }
public Dictionary<ImportQueueItem, string> Errored { get; } = new();
public EventHandler<ImportQueueItem> OnRecordAdded;
public EventHandler<ImportQueueItem> OnRecordProcessed;
public EventHandler<ImportQueueItem> OnRecordError;
private void UseContext(ImportContext context)
{
Games.UseContext(context);
Redistributables.UseContext(context);
Servers.UseContext(context);
Actions.UseContext(context);
Archives.UseContext(context);
Collections.UseContext(context);
CustomFields.UseContext(context);
Developers.UseContext(context);
Engines.UseContext(context);
Genres.UseContext(context);
Keys.UseContext(context);
Media.UseContext(context);
MultiplayerModes.UseContext(context);
Platforms.UseContext(context);
PlaySessions.UseContext(context);
Publishers.UseContext(context);
Saves.UseContext(context);
SavePaths.UseContext(context);
Scripts.UseContext(context);
ServerConsoles.UseContext(context);
ServerHttpPaths.UseContext(context);
Tags.UseContext(context);
}
#region Initialize Import
public async Task<IEnumerable<ImportItemInfo>> InitializeImportAsync(string archivePath)
{
UseContext(this);
Archive = ZipArchive.Open(archivePath);
var manifestEntry = Archive.Entries.FirstOrDefault(e => e.Key == ManifestHelper.ManifestFilename);
using (var reader = new StreamReader(manifestEntry.OpenEntryStream()))
{
var manifestContents = await reader.ReadToEndAsync();
if (ManifestHelper.TryDeserialize<SDK.Models.Manifest.Game>(manifestContents, out var gameManifest))
return await InitializeGameImportAsync(gameManifest);
if (ManifestHelper.TryDeserialize<SDK.Models.Manifest.Redistributable>(manifestContents, out var redistributableManifest))
return await InitializeRedistributableImportAsync(redistributableManifest);
if (ManifestHelper.TryDeserialize<SDK.Models.Manifest.Server>(manifestContents, out var serverManifest))
return await InitializeServerImportAsync(serverManifest);
throw new InvalidOperationException("Unknown manifest file");
}
}
private async Task<IEnumerable<ImportItemInfo>> InitializeGameImportAsync(SDK.Models.Manifest.Game gameManifest)
{
Manifest = gameManifest;
var importItemInfo = new List<ImportItemInfo>();
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Actions, Actions).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Archives, Archives).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Collections, Collections).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.CustomFields, CustomFields).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Developers, Developers).ToListAsync());
if (gameManifest.Engine != null)
importItemInfo.AddRange(await GetImportItemInfoAsync([gameManifest.Engine], Engines).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Genres, Genres).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Keys, Keys).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Media, Media).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.MultiplayerModes, MultiplayerModes).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Platforms, Platforms).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.PlaySessions, PlaySessions).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Publishers, Publishers).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Saves, Saves).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.SavePaths, SavePaths).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Scripts, Scripts).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(gameManifest.Tags, Tags).ToListAsync());
return importItemInfo;
}
private async Task<IEnumerable<ImportItemInfo>> InitializeRedistributableImportAsync(SDK.Models.Manifest.Redistributable redistributableManifest)
{
Manifest = redistributableManifest;
var importItemInfo = new List<ImportItemInfo>();
importItemInfo.AddRange(await GetImportItemInfoAsync(redistributableManifest.Archives, Archives).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(redistributableManifest.Scripts, Scripts).ToListAsync());
return importItemInfo;
}
private async Task<IEnumerable<ImportItemInfo>> InitializeServerImportAsync(SDK.Models.Manifest.Server serverManifest)
{
Manifest = serverManifest;
var importItemInfo = new List<ImportItemInfo>();
importItemInfo.AddRange(await GetImportItemInfoAsync(serverManifest.Actions, Actions).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(serverManifest.Scripts, Scripts).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(serverManifest.ServerConsoles, ServerConsoles).ToListAsync());
importItemInfo.AddRange(await GetImportItemInfoAsync(serverManifest.HttpPaths, ServerHttpPaths).ToListAsync());
return importItemInfo;
}
#endregion
#region Prepare Import Queue
public async Task PrepareImportQueueAsync(IEnumerable<Guid> selectedRecordIds, Guid storageLocationId)
{
_selectedRecordIds = selectedRecordIds;
ArchiveStorageLocation = await storageLocationService.GetAsync(storageLocationId);
if (Manifest is SDK.Models.Manifest.Game gameManifest)
await PrepareGameImportQueueAsync(gameManifest);
if (Manifest is SDK.Models.Manifest.Redistributable redistributableManifest)
await PrepareRedistributableImportQueueAsync(redistributableManifest);
if (Manifest is SDK.Models.Manifest.Server serverManifest)
await PrepareServerImportQueueAsync(serverManifest);
}
public async Task PrepareGameImportQueueAsync(SDK.Models.Manifest.Game gameManifest)
{
if (!(await Games.ExistsAsync(gameManifest)))
DataRecord = await Games.AddAsync(gameManifest);
else
DataRecord = await Games.UpdateAsync(gameManifest);
await AddToImportQueueAsync(ImportExportRecordType.Action, gameManifest.Actions);
await AddToImportQueueAsync(ImportExportRecordType.Archive, gameManifest.Archives);
await AddToImportQueueAsync(ImportExportRecordType.Collection, gameManifest.Collections);
await AddToImportQueueAsync(ImportExportRecordType.CustomField, gameManifest.CustomFields);
await AddToImportQueueAsync(ImportExportRecordType.Developer, gameManifest.Developers);
await AddToImportQueueAsync(ImportExportRecordType.Engine, [gameManifest.Engine]);
await AddToImportQueueAsync(ImportExportRecordType.Genre, gameManifest.Genres);
await AddToImportQueueAsync(ImportExportRecordType.Key, gameManifest.Keys);
await AddToImportQueueAsync(ImportExportRecordType.Media, gameManifest.Media);
await AddToImportQueueAsync(ImportExportRecordType.MultiplayerMode, gameManifest.MultiplayerModes);
await AddToImportQueueAsync(ImportExportRecordType.Platform, gameManifest.Platforms);
await AddToImportQueueAsync(ImportExportRecordType.PlaySession, gameManifest.PlaySessions);
await AddToImportQueueAsync(ImportExportRecordType.Publisher, gameManifest.Publishers);
await AddToImportQueueAsync(ImportExportRecordType.Save, gameManifest.Saves);
await AddToImportQueueAsync(ImportExportRecordType.SavePath, gameManifest.SavePaths);
await AddToImportQueueAsync(ImportExportRecordType.Script, gameManifest.Scripts);
await AddToImportQueueAsync(ImportExportRecordType.Tag, gameManifest.Tags);
}
public async Task PrepareRedistributableImportQueueAsync(SDK.Models.Manifest.Redistributable redistributableManifest)
{
if (!(await Redistributables.ExistsAsync(redistributableManifest)))
DataRecord = await Redistributables.AddAsync(redistributableManifest);
else
DataRecord = await Redistributables.UpdateAsync(redistributableManifest);
await AddToImportQueueAsync(ImportExportRecordType.Archive, redistributableManifest.Archives);
await AddToImportQueueAsync(ImportExportRecordType.Script, redistributableManifest.Scripts);
}
public async Task PrepareServerImportQueueAsync(SDK.Models.Manifest.Server serverManifest)
{
if (!(await Servers.ExistsAsync(serverManifest)))
DataRecord = await Servers.AddAsync(serverManifest);
else
DataRecord = await Servers.UpdateAsync(serverManifest);
await AddToImportQueueAsync(ImportExportRecordType.Action, serverManifest.Actions);
await AddToImportQueueAsync(ImportExportRecordType.Script, serverManifest.Scripts);
await AddToImportQueueAsync(ImportExportRecordType.ServerConsole, serverManifest.ServerConsoles);
await AddToImportQueueAsync(ImportExportRecordType.ServerHttpPath, serverManifest.HttpPaths);
}
#endregion
private async Task AddToImportQueueAsync<TRecord>(ImportExportRecordType type, IEnumerable<TRecord> records) where TRecord : SDK.Models.Manifest.BaseModel
{
if (records != null)
_queue.AddRange(records.Select(r => new ImportQueueItem(type, r)));
}
public async Task ImportQueueAsync()
{
foreach (var queueItem in _queue)
{
if (queueItem.Type == ImportExportRecordType.Action)
await ImportRecordAsync(queueItem, Actions);
else if (queueItem.Type == ImportExportRecordType.Archive)
await ImportRecordAsync(queueItem, Archives);
else if (queueItem.Type == ImportExportRecordType.Collection)
await ImportRecordAsync(queueItem, Collections);
else if (queueItem.Type == ImportExportRecordType.CustomField)
await ImportRecordAsync(queueItem, CustomFields);
else if (queueItem.Type == ImportExportRecordType.Developer)
await ImportRecordAsync(queueItem, Developers);
else if (queueItem.Type == ImportExportRecordType.Publisher)
await ImportRecordAsync(queueItem, Publishers);
else if (queueItem.Type == ImportExportRecordType.Engine)
await ImportRecordAsync(queueItem, Engines);
else if (queueItem.Type == ImportExportRecordType.Genre)
await ImportRecordAsync(queueItem, Genres);
else if (queueItem.Type == ImportExportRecordType.Key)
await ImportRecordAsync(queueItem, Keys);
else if (queueItem.Type == ImportExportRecordType.Media)
await ImportRecordAsync(queueItem, Media);
else if (queueItem.Type == ImportExportRecordType.MultiplayerMode)
await ImportRecordAsync(queueItem, MultiplayerModes);
else if (queueItem.Type == ImportExportRecordType.Platform)
await ImportRecordAsync(queueItem, Platforms);
else if (queueItem.Type == ImportExportRecordType.PlaySession)
await ImportRecordAsync(queueItem, PlaySessions);
else if (queueItem.Type == ImportExportRecordType.Save)
await ImportRecordAsync(queueItem, Saves);
else if (queueItem.Type == ImportExportRecordType.SavePath)
await ImportRecordAsync(queueItem, SavePaths);
else if (queueItem.Type == ImportExportRecordType.Script)
await ImportRecordAsync(queueItem, Scripts);
else if (queueItem.Type == ImportExportRecordType.ServerConsole)
await ImportRecordAsync(queueItem, ServerConsoles);
else if (queueItem.Type == ImportExportRecordType.ServerHttpPath)
await ImportRecordAsync(queueItem, ServerHttpPaths);
else if (queueItem.Type == ImportExportRecordType.Tag)
await ImportRecordAsync(queueItem, Tags);
}
}
private async IAsyncEnumerable<ImportItemInfo> GetImportItemInfoAsync<TModel, TEntity>(IEnumerable<TModel> records,
BaseImporter<TModel, TEntity> importer)
{
if (records != null)
foreach (var record in records)
{
if (record != null && record.GetType() == typeof(TModel))
yield return await importer.GetImportInfoAsync(record);
}
}
private async Task ImportRecordAsync<TRecord, TEntity>(ImportQueueItem queueItem, BaseImporter<TRecord, TEntity> importer) where TRecord : class
{
var record = queueItem.Record as TRecord;
try
{
if (await importer.ExistsAsync(record))
await importer.UpdateAsync(record);
else
await importer.AddAsync(record);
queueItem.Processed = true;
OnRecordProcessed?.Invoke(this, queueItem);
}
catch (Exception ex)
{
Errored.Add(queueItem, ex.Message);
OnRecordError?.Invoke(this, queueItem);
}
}
public void Dispose()
{
if (Archive != null)
Archive.Dispose();
}
}