SHA256
feat StayExport - Refactor Export Services.
- Created new service: IStayExportService" for common export operations. - Updated older services to be "IStayExportFormatProvider". This services will only implement the export format. - Renamed "IStayExportServiceFactory" to "IStayExportFormatProviderFactory" as this factory now is used to get the format providers for the given format.
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
@attribute [Route($"{GuestConsts.Page_StayExport_List}")]
|
||||
|
||||
@using FluentStorage;
|
||||
@using FluentStorage.Storage;
|
||||
@using System.Text.Json;
|
||||
@using Microsoft.Extensions.Options;
|
||||
@using Somnia.GuestLog.Models.DTOs;
|
||||
@using Somnia.GuestLog.Models.Configuration;
|
||||
@using Somnia.GuestLog.Services;
|
||||
@using Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
@inject IStayExportFileEntityService stayExportEntityService
|
||||
@inject NavigationManager Navigation
|
||||
@@ -15,7 +12,7 @@
|
||||
@inject IDataListService dataListService;
|
||||
@inject ILocalizerService Localizer;
|
||||
|
||||
@inject IOptions<StayExportConfig> exportConfig
|
||||
@inject IStayExportService ExportService;
|
||||
@inject IJSRuntime JS
|
||||
|
||||
|
||||
@@ -126,7 +123,7 @@
|
||||
|
||||
private async Task BtnDownloadFile(StayExportFileDTO item)
|
||||
{
|
||||
if(item.ExportResult == Enums.Models.StayExportFileResult.NoData)
|
||||
if (item.ExportResult == Enums.Models.StayExportFileResult.NoData)
|
||||
{
|
||||
userMessage.ShowMessage("A Export without data can't be downloaded!", MessageKind.Warning);
|
||||
return;
|
||||
@@ -138,10 +135,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
IStore store = StorageFactory.FromConnectionString(exportConfig.Value.ExportStorageConnectionString);
|
||||
|
||||
using Stream objectStream = new MemoryStream();
|
||||
await store.GetObject(item.ExportedPath, objectStream);
|
||||
Result loadResult = await ExportService.GetExportedFileAsync(item, objectStream);
|
||||
if (!loadResult.IsSuccess)
|
||||
{
|
||||
userMessage.ShowResultWithMessage(loadResult, "Exported File can't be downloaded");
|
||||
return;
|
||||
}
|
||||
|
||||
objectStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using var streamRef = new DotNetStreamReference(stream: objectStream);
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
@attribute [Route($"{GuestConsts.Page_StayExport_New}")]
|
||||
|
||||
@using FluentValidation;
|
||||
@using FluentStorage;
|
||||
@using FluentStorage.Storage;
|
||||
@using Microsoft.Extensions.Options;
|
||||
@using Somnia.GuestLog.Models.DTOs;
|
||||
@using Somnia.GuestLog.Models.Configuration;
|
||||
@using Somnia.GuestLog.Enums.Models;
|
||||
@using Somnia.GuestLog.Services;
|
||||
@using Somnia.GuestLog.Validators;
|
||||
@@ -17,9 +13,8 @@
|
||||
@inject NavigationManager Navigation
|
||||
@inject IValidator<StayExportParamsDTO> ExportParamsValidator;
|
||||
@inject ILocalizerService Localizer;
|
||||
@inject IStayExportServiceFactory ExportServiceFactory;
|
||||
@inject IStayExportService ExportService;
|
||||
|
||||
@inject IOptions<StayExportConfig> exportConfig
|
||||
@inject IJSRuntime JS
|
||||
|
||||
|
||||
@@ -121,9 +116,7 @@
|
||||
|
||||
var loadingDialog = await DialogService.ShowAsync<LoadingDialog>();
|
||||
|
||||
var exportService = ExportServiceFactory.GetService(exportFormat.Value);
|
||||
var exportResult = await exportService.ExportStaysByParams(exportParams);
|
||||
|
||||
var exportResult = await ExportService.ExportStaysByParamsAsync(exportFormat.Value, exportParams);
|
||||
exportedFile = exportResult.Value;
|
||||
loadingDialog.Close();
|
||||
|
||||
@@ -141,7 +134,6 @@
|
||||
private async Task BtnDownloadExportedFile()
|
||||
{
|
||||
Debug.Assert(exportedFile != null);
|
||||
Debug.Assert(exportConfig.Value.ExportStorageConnectionString != null);
|
||||
|
||||
if (string.IsNullOrEmpty(exportedFile.ExportedPath))
|
||||
{
|
||||
@@ -149,16 +141,19 @@
|
||||
return;
|
||||
}
|
||||
|
||||
IStore store = StorageFactory.FromConnectionString(exportConfig.Value.ExportStorageConnectionString);
|
||||
using Stream dataStream = new MemoryStream();
|
||||
|
||||
using Stream objectStream = new MemoryStream();
|
||||
await store.GetObject(exportedFile.ExportedPath, objectStream);
|
||||
objectStream.Seek(0, SeekOrigin.Begin);
|
||||
Result loadResult = await ExportService.GetExportedFileAsync(exportedFile, dataStream);
|
||||
if (!loadResult.IsSuccess)
|
||||
{
|
||||
userMessage.ShowResultWithMessage(loadResult, "Exported File can't be downloaded");
|
||||
return;
|
||||
}
|
||||
|
||||
dataStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: dataStream);
|
||||
|
||||
var fileName = Path.GetFileName(exportedFile.ExportedPath);
|
||||
|
||||
using var streamRef = new DotNetStreamReference(stream: objectStream);
|
||||
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
|
||||
|
||||
userMessage.ShowMessage("Exported File downloaded!", MessageKind.Success);
|
||||
|
||||
@@ -66,10 +66,11 @@ builder.Services.AddSingleton<IStayExportFileEntityService, StayExportFileEntity
|
||||
builder.Services.AddSingleton<IDataListService, StaticDataListService>();
|
||||
builder.Services.AddScoped<IUserMessageService, UserMessageService>();
|
||||
builder.Services.AddSingleton<ILocalizerService, LocalizerService>();
|
||||
builder.Services.AddSingleton<IStayExportService, StayExportService>();
|
||||
|
||||
// Add Factories (and it's services)
|
||||
builder.Services.AddFactoryDI<IAddressQueryFactory, ConfigFileAddressQueryFactory>();
|
||||
builder.Services.AddFactoryDI<IStayExportServiceFactory, ConfigFileStaytExportServiceFactory>();
|
||||
builder.Services.AddFactoryDI<IStayExportFormatProviderFactory, StayExportFormatProviderFactory>();
|
||||
|
||||
// Add Options config
|
||||
builder.Services.AddOptions<AddressQueryConfig>()
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Somnia.GuestLog.Enums.Models;
|
||||
using Somnia.GuestLog.Models.DTOs;
|
||||
|
||||
namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public interface IStayExportFormatProvider
|
||||
{
|
||||
public StayExportFormat GetFormat();
|
||||
public string GetFilename(DateTime exportTime);
|
||||
|
||||
/// <summary>
|
||||
/// Export Data with the corresponding format.
|
||||
/// Data is copied to given stream (no other operations are performed to the stream).
|
||||
/// </summary>
|
||||
/// <param name="stayList">List of Stays to export</param>
|
||||
/// <param name="dataStream">Stream to copy exported data.</param>
|
||||
public Task<StayExportFileResult> ExportDataAsync(IEnumerable<StayEntryDTO> stayList, Stream dataStream);
|
||||
|
||||
}
|
||||
+2
-2
@@ -2,10 +2,10 @@ using Somnia.GuestLog.Enums.Models;
|
||||
|
||||
namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public interface IStayExportServiceFactory
|
||||
public interface IStayExportFormatProviderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the StayExport service implementation given current configuration.
|
||||
/// </summary>
|
||||
public IStayExportService GetService(StayExportFormat exportFormat);
|
||||
public IStayExportFormatProvider GetProvider(StayExportFormat exportFormat);
|
||||
}
|
||||
@@ -5,7 +5,10 @@ namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public interface IStayExportService
|
||||
{
|
||||
public StayExportFormat GetFormat();
|
||||
public Task<Result<StayExportFileDTO>> ExportStaysByParamsAsync(StayExportFormat format, StayExportParamsDTO exportParams);
|
||||
|
||||
public Task<Result<StayExportFileDTO>> ExportStaysByParams(StayExportParamsDTO exportParams);
|
||||
|
||||
public Task<Result> GetExportedFileAsync(StayExportFileDTO item, Stream dataStream);
|
||||
|
||||
public Task<Result> DeleteExportedFileAsync(StayExportFileDTO item);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Somnia.GuestLog.Enums.Models;
|
||||
using Somnia.GuestLog.Models.DTOs;
|
||||
|
||||
namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public class SpainParteViajerosStayExportService(
|
||||
|
||||
) : IStayExportService
|
||||
{
|
||||
public StayExportFormat GetFormat() => StayExportFormat.SpainParteViajeros;
|
||||
|
||||
|
||||
public Task<Result<StayExportFileDTO>> ExportStaysByParams(StayExportParamsDTO exportParams)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -5,26 +5,26 @@ using Somnia.GuestLog.Utils.DI;
|
||||
|
||||
namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public class ConfigFileStaytExportServiceFactory(
|
||||
public class StayExportFormatProviderFactory(
|
||||
IServiceProvider serviceProvider,
|
||||
IOptions<StayExportConfig> config
|
||||
) : IStayExportServiceFactory, IFactoryDI
|
||||
) : IStayExportFormatProviderFactory, IFactoryDI
|
||||
{
|
||||
public static void RegisterFactoryItems(IServiceCollection services)
|
||||
{
|
||||
services.AddKeyedSingleton<IStayExportService, JsonStayExportService>(StayExportFormat.GuestLogJson);
|
||||
services.AddKeyedSingleton<IStayExportService, SpainParteViajerosStayExportService>(StayExportFormat.SpainParteViajeros);
|
||||
services.AddKeyedSingleton<IStayExportFormatProvider, StayExportProviderJson>(StayExportFormat.GuestLogJson);
|
||||
services.AddKeyedSingleton<IStayExportFormatProvider, StayExportProviderSpainParteViajeros>(StayExportFormat.SpainParteViajeros);
|
||||
}
|
||||
|
||||
|
||||
public IStayExportService GetService(StayExportFormat exportFormat)
|
||||
public IStayExportFormatProvider GetProvider(StayExportFormat exportFormat)
|
||||
{
|
||||
if (config.Value.EnableStayExport == false)
|
||||
{
|
||||
throw new InvalidOperationException("StayExport is disabled. Enable and configure it first!");
|
||||
}
|
||||
|
||||
return serviceProvider.GetKeyedService<IStayExportService>(exportFormat)
|
||||
return serviceProvider.GetKeyedService<IStayExportFormatProvider>(exportFormat)
|
||||
?? throw new InvalidOperationException($"StayExport service not found for ExportFormat: '{exportFormat}'");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Text.Json;
|
||||
using Somnia.GuestLog.Enums.Models;
|
||||
using Somnia.GuestLog.Models.DTOs;
|
||||
|
||||
namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public class StayExportProviderJson : IStayExportFormatProvider
|
||||
{
|
||||
private const string fileExtension = "json";
|
||||
|
||||
public StayExportFormat GetFormat() => StayExportFormat.GuestLogJson;
|
||||
public string GetFilename(DateTime exportTime)
|
||||
=> $"{Enum.GetName(GetFormat())}_{exportTime.ToFileTimeUtc()}.{fileExtension}";
|
||||
|
||||
|
||||
public async Task<StayExportFileResult> ExportDataAsync(IEnumerable<StayEntryDTO> stayList, Stream dataStream)
|
||||
{
|
||||
if (!stayList.Any())
|
||||
{
|
||||
return StayExportFileResult.NoData;
|
||||
}
|
||||
|
||||
// Convert to JSON.
|
||||
await JsonSerializer.SerializeAsync(dataStream, stayList);
|
||||
return StayExportFileResult.OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Somnia.GuestLog.Enums.Models;
|
||||
using Somnia.GuestLog.Models.DTOs;
|
||||
|
||||
namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public class StayExportProviderSpainParteViajeros(
|
||||
|
||||
) : IStayExportFormatProvider
|
||||
{
|
||||
private const string fileExtension = "xml";
|
||||
|
||||
public StayExportFormat GetFormat() => StayExportFormat.SpainParteViajeros;
|
||||
public string GetFilename(DateTime exportTime)
|
||||
=> $"{Enum.GetName(GetFormat())}_{exportTime.ToFileTimeUtc()}.{fileExtension}";
|
||||
|
||||
public Task<StayExportFileResult> ExportDataAsync(IEnumerable<StayEntryDTO> stayList, Stream dataStream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
+54
-37
@@ -1,4 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using System.Diagnostics;
|
||||
using FluentStorage;
|
||||
using FluentStorage.Storage;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -11,23 +11,21 @@ using Somnia.GuestLog.Services.Entity;
|
||||
|
||||
namespace Somnia.GuestLog.Services.StayExport;
|
||||
|
||||
public class JsonStayExportService(
|
||||
ILogger<JsonStayExportService> logger,
|
||||
public class StayExportService(
|
||||
ILogger<StayExportProviderJson> logger,
|
||||
IOptions<StayExportConfig> exportConfig,
|
||||
IStayExportFileEntityService stayExportEntityService,
|
||||
IStayEntryEntityService stayEntityService,
|
||||
IOptions<StayExportConfig> exportConfig
|
||||
IStayExportFormatProviderFactory providerFactory
|
||||
) : IStayExportService
|
||||
{
|
||||
private const string fileExtension = "json";
|
||||
|
||||
private static readonly StayEntryQueryConfig queryConfig =
|
||||
new(IncludeGuests: true, UseSplitQueries: true, DisableTracking: true);
|
||||
private readonly StayExportConfig config = exportConfig.Value;
|
||||
|
||||
public StayExportFormat GetFormat() => StayExportFormat.GuestLogJson;
|
||||
|
||||
|
||||
public async Task<Result<StayExportFileDTO>> ExportStaysByParams(StayExportParamsDTO exportParams)
|
||||
public async Task<Result<StayExportFileDTO>> ExportStaysByParamsAsync(StayExportFormat format, StayExportParamsDTO exportParams)
|
||||
{
|
||||
//Get data to export.
|
||||
IEnumerable<StayEntryDTO>? stayList;
|
||||
@@ -45,7 +43,7 @@ public class JsonStayExportService(
|
||||
DateTime exportTime = DateTime.UtcNow;
|
||||
StayExportFileDTO stayExport = new()
|
||||
{
|
||||
ExportFormat = GetFormat(),
|
||||
ExportFormat = format,
|
||||
ExportParams = exportParams,
|
||||
ExportTime = exportTime,
|
||||
};
|
||||
@@ -54,13 +52,18 @@ public class JsonStayExportService(
|
||||
using Stream exportedDataStream = new MemoryStream();
|
||||
|
||||
//Generate data to export.
|
||||
StayExportFileResult? exportResult;
|
||||
StayExportFileResult exportResult;
|
||||
string? exportFilename = null;
|
||||
try
|
||||
{
|
||||
exportResult = await ExportDataToStream(stayList, exportedDataStream);
|
||||
//Create Format provider and export data to stream.
|
||||
var provider = providerFactory.GetProvider(format);
|
||||
exportResult = await provider.ExportDataAsync(stayList, exportedDataStream);
|
||||
|
||||
// "Rewind" stream so it can be read from the begining when uploading.
|
||||
exportedDataStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
exportFilename = provider.GetFilename(exportTime);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -73,7 +76,14 @@ public class JsonStayExportService(
|
||||
{
|
||||
try
|
||||
{
|
||||
string exportPath = GetExportPath(exportTime);
|
||||
Debug.Assert(exportFilename != null);
|
||||
|
||||
string exportPathSubdir = "";
|
||||
if (config.UseSubdirectoryByFormat)
|
||||
{
|
||||
exportPathSubdir = Enum.GetName(format) ?? "";
|
||||
}
|
||||
string exportPath = Path.Combine(exportPathSubdir, exportFilename);
|
||||
|
||||
//Store data.
|
||||
using IStore exportStore = GetExportStore();
|
||||
@@ -128,6 +138,38 @@ public class JsonStayExportService(
|
||||
}
|
||||
|
||||
|
||||
public async Task<Result> GetExportedFileAsync(StayExportFileDTO item, Stream dataStream)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.ExportedPath))
|
||||
{
|
||||
logger.LogWarning("Trying to get exported file without path!! Exported file can't be loaded!");
|
||||
return Result.Fail(ResultCode.NotFound);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IStore store = GetExportStore();
|
||||
await store.GetObject(item.ExportedPath, dataStream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error getting exported file '{id}' from path '{path}'", item.Id, item.ExportedPath);
|
||||
return Result.FailUnknownError();
|
||||
}
|
||||
|
||||
return Result.Ok();
|
||||
}
|
||||
|
||||
public Task<Result> DeleteExportedFileAsync(StayExportFileDTO item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
//TODO:
|
||||
// - Delete file from "Store" if exists.
|
||||
// - Delete entry from DB.
|
||||
}
|
||||
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
private async Task<IEnumerable<StayEntryDTO>> GetStaysToExportByParamsAsync(StayExportParamsDTO exportParams)
|
||||
@@ -152,31 +194,6 @@ public class JsonStayExportService(
|
||||
throw new InvalidOperationException($"StayExportFile - Error getting stays using params: export type invalid: {exportParams.ExportType}");
|
||||
}
|
||||
|
||||
private async Task<StayExportFileResult> ExportDataToStream(IEnumerable<StayEntryDTO> stayList, Stream exportStream)
|
||||
{
|
||||
if (!stayList.Any())
|
||||
{
|
||||
return StayExportFileResult.NoData;
|
||||
}
|
||||
|
||||
// Convert to JSON.
|
||||
await JsonSerializer.SerializeAsync(exportStream, stayList);
|
||||
return StayExportFileResult.OK;
|
||||
}
|
||||
|
||||
private string GetExportPath(DateTime time)
|
||||
{
|
||||
string formatName = GetFormatName();
|
||||
string exportPathSubdir = config.UseSubdirectoryByFormat ? formatName : "";
|
||||
string exportFileName = $"{formatName}_{time.ToFileTimeUtc()}.{fileExtension}";
|
||||
return Path.Combine(exportPathSubdir, exportFileName);
|
||||
}
|
||||
|
||||
private string GetFormatName()
|
||||
{
|
||||
return Enum.GetName(GetFormat()) ?? throw new InvalidOperationException("Can't get StayExportFormat name!");
|
||||
}
|
||||
|
||||
private IStore GetExportStore()
|
||||
{
|
||||
string? storageConnString = config.ExportStorageConnectionString;
|
||||
Reference in New Issue
Block a user