feat: E-Mail-Vorlagenverarbeitung hinzufügen

- Methode `FillTemplate` für Platzhalterersetzung hinzugefügt.
- `TemplatePlaceholderAttribute` eingeführt.
- `EmailOutService` mit Vorlagenmethoden aktualisiert.
- Unit-Tests für Vorlagenverarbeitung hinzugefügt.
This commit is contained in:
Developer 02 2024-06-10 14:16:54 +02:00
parent 1adc0c4d93
commit a2bd369ed1
10 changed files with 240 additions and 25 deletions

View File

@ -1,8 +1,12 @@
using DigitalData.Core.API; using DigitalData.Core.API;
using DigitalData.EmailProfilerDispatcher.API.Resources;
using DigitalData.EmailProfilerDispatcher.Application.Contracts; using DigitalData.EmailProfilerDispatcher.Application.Contracts;
using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut; using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Application.Services;
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using DigitalData.EmailProfilerDispatcher.Domain.Entities; using DigitalData.EmailProfilerDispatcher.Domain.Entities;
using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts; using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts;
using Microsoft.AspNetCore.Mvc;
namespace DigitalData.EmailProfilerGateway.API.Controllers namespace DigitalData.EmailProfilerGateway.API.Controllers
{ {
@ -11,5 +15,45 @@ namespace DigitalData.EmailProfilerGateway.API.Controllers
public MailController(ILogger<MailController> logger, IEmailOutService service) : base(logger, service) public MailController(ILogger<MailController> logger, IEmailOutService service) : base(logger, service)
{ {
} }
[HttpGet("fill-out")]
public IActionResult FillOut(string? template = null)
{
template ??= @"Guten Tag [NAME_RECEIVER],<br />
<br />
hiermit bestätigen wir Ihnen die erfolgreiche Signatur für den Vorgang <B><I>'[DOCUMENT_TITLE]'</I></B>.<br />
Wenn alle Vertragspartner unterzeichnet haben, erhalten Sie ebenfalls per email ein unterschriebenes Exemplar mit dem Signierungszertifikat!
<br />
Mit freundlichen Grüßen<br />
<br />
[NAME_PORTAL]";
var mailData = new MailData();
var expectedOutput = @"Guten Tag Tom,<br />
<br />
hiermit bestätigen wir Ihnen die erfolgreiche Signatur für den Vorgang <B><I>'Vertragsdokument'</I></B>.<br />
Wenn alle Vertragspartner unterzeichnet haben, erhalten Sie ebenfalls per email ein unterschriebenes Exemplar mit dem Signierungszertifikat!
<br />
Mit freundlichen Grüßen<br />
<br />
DokumentenPortal";
var result = EmailOutService<Resource>.FillTemplate(template, mailData);
return Ok(result);
}
public class MailData
{
[TemplatePlaceholder("[NAME_RECEIVER]")]
public string NameReceiver { get; set; } = "Tom";
[TemplatePlaceholder("[DOCUMENT_TITLE]")]
public string DocumentTitle { get; set; } = "Vertragsdokument";
[TemplatePlaceholder("[NAME_PORTAL]")]
public string NamePortal { get; set; } = "DokumentenPortal";
}
} }
} }

View File

@ -29,6 +29,9 @@
<Reference Include="DigitalData.Core.API"> <Reference Include="DigitalData.Core.API">
<HintPath>..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.API.dll</HintPath> <HintPath>..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.API.dll</HintPath>
</Reference> </Reference>
<Reference Include="DigitalData.Core.Application">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Application.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Contracts"> <Reference Include="DigitalData.Core.Contracts">
<HintPath>..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.Contracts.dll</HintPath> <HintPath>..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.Contracts.dll</HintPath>
</Reference> </Reference>

View File

@ -12,7 +12,7 @@ var config = builder.Configuration;
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddCookieBasedLocalizer(); builder.Services.AddCookieBasedLocalizer();
builder.Services.AddDispatcher(options => options.UseSqlServer(config.GetConnectionString("Default"))); builder.Services.AddDispatcher<Resource>(options => options.UseSqlServer(config.GetConnectionString("Default")));
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();

View File

@ -1,4 +1,5 @@
using DigitalData.Core.Contracts.Application; using DigitalData.Core.Contracts.Application;
using DigitalData.Core.DTO;
using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut; using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Domain.Entities; using DigitalData.EmailProfilerDispatcher.Domain.Entities;
using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts; using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts;
@ -7,5 +8,6 @@ namespace DigitalData.EmailProfilerDispatcher.Application.Contracts
{ {
public interface IEmailOutService : ICRUDService<IEmailOutRepository, EmailOutCreateDto, EmailOutDto, EmailOutDto, EmailOut, int> public interface IEmailOutService : ICRUDService<IEmailOutRepository, EmailOutCreateDto, EmailOutDto, EmailOutDto, EmailOut, int>
{ {
Task<DataResult<int>> CreateWithTemplateAsync(EmailOutCreateDto createDto, params object[] models);
} }
} }

View File

@ -1,23 +1,25 @@
namespace DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut namespace DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut
{ {
public record EmailOutCreateDto( public record EmailOutCreateDto
int ReminderTypeId, {
int SendingProfile, public int ReminderTypeId { get; set; }
int ReferenceId, public int SendingProfile { get; set; }
string? ReferenceString, public int ReferenceId { get; set; }
int? EntityId, public int WfId { get; set; }
int WfId, public string EmailAddress { get; set; }
string? WfReference, public string EmailSubj { get; set; }
string EmailAddress, public string EmailBody { get; set; }
string EmailSubj, public string? ReferenceString { get; set; } = null;
string EmailBody, public int? EntityId { get; set; } = null;
string? EmailAttmt1, public string? WfReference { get; set; } = null;
DateTime? EmailSent, public string? EmailAttmt1 { get; set; } = null;
string? Comment, public DateTime? EmailSent { get; set; } = null;
string? AddedWho, // Default value will be 'DEFAULT', made nullable public string? Comment { get; set; } = null;
string? ChangedWho, public string? AddedWho { get; set; } = "DEFAULT";
DateTime? ChangedWhen, public string? ChangedWho { get; set; } = null;
DateTime? ErrorTimestamp, public DateTime? ChangedWhen { get; set; } = null;
string? ErrorMsg public DateTime? ErrorTimestamp { get; set; } = null;
); public string? ErrorMsg { get; set; } = null;
}
} }

View File

@ -1,10 +1,13 @@
using AutoMapper; using AutoMapper;
using DigitalData.Core.Application; using DigitalData.Core.Application;
using DigitalData.Core.DTO;
using DigitalData.EmailProfilerDispatcher.Application.Contracts; using DigitalData.EmailProfilerDispatcher.Application.Contracts;
using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut; using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using DigitalData.EmailProfilerDispatcher.Domain.Entities; using DigitalData.EmailProfilerDispatcher.Domain.Entities;
using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts; using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using System.Reflection;
namespace DigitalData.EmailProfilerDispatcher.Application.Services namespace DigitalData.EmailProfilerDispatcher.Application.Services
{ {
@ -13,5 +16,36 @@ namespace DigitalData.EmailProfilerDispatcher.Application.Services
public EmailOutService(IEmailOutRepository repository, IStringLocalizer<TResource> localizer, IMapper mapper) : base(repository, localizer, mapper) public EmailOutService(IEmailOutRepository repository, IStringLocalizer<TResource> localizer, IMapper mapper) : base(repository, localizer, mapper)
{ {
} }
public string ApplyTemplate(string template, params object[] models) => FillTemplate(template: template, models: models);
public async Task<DataResult<int>> CreateWithTemplateAsync(EmailOutCreateDto createDto, params object[] models)
{
createDto.EmailSubj = FillTemplate(createDto.EmailSubj, models);
createDto.EmailBody = FillTemplate(createDto.EmailBody, models);
return await base.CreateAsync(createDto);
}
public static string FillTemplate(string template, params object[] models)
{
foreach(var model in models)
{
var properties = model.GetType().GetProperties();
foreach (var property in properties)
{
var attribute = property.GetCustomAttribute<TemplatePlaceholderAttribute>();
if (attribute != null)
{
var value = property.GetValue(model)?.ToString();
template = template.Replace(attribute.Placeholder, value);
}
}
}
return template;
}
} }
} }

View File

@ -0,0 +1,13 @@
namespace DigitalData.EmailProfilerDispatcher.Domain.Attributes
{
[AttributeUsage(AttributeTargets.Property)]
public class TemplatePlaceholderAttribute : Attribute
{
public readonly string Placeholder;
public TemplatePlaceholderAttribute(string placeholder)
{
Placeholder = placeholder;
}
}
}

View File

@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.EmailProfilerDi
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.EmailProfilerDispatcher.Infrastructure", "DigitalData.EmailProfilerDispatcher.Infrastructure\DigitalData.EmailProfilerDispatcher.Infrastructure.csproj", "{F02B6F5B-FB2F-4E20-996D-BE99E9768039}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.EmailProfilerDispatcher.Infrastructure", "DigitalData.EmailProfilerDispatcher.Infrastructure\DigitalData.EmailProfilerDispatcher.Infrastructure.csproj", "{F02B6F5B-FB2F-4E20-996D-BE99E9768039}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmailProfilerDispatcher.Tests", "EmailProfilerDispatcher.Tests\EmailProfilerDispatcher.Tests.csproj", "{C55114DF-F7C9-47A6-AF36-99F6C8079DE2}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -33,6 +35,10 @@ Global
{F02B6F5B-FB2F-4E20-996D-BE99E9768039}.Debug|Any CPU.Build.0 = Debug|Any CPU {F02B6F5B-FB2F-4E20-996D-BE99E9768039}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F02B6F5B-FB2F-4E20-996D-BE99E9768039}.Release|Any CPU.ActiveCfg = Release|Any CPU {F02B6F5B-FB2F-4E20-996D-BE99E9768039}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F02B6F5B-FB2F-4E20-996D-BE99E9768039}.Release|Any CPU.Build.0 = Release|Any CPU {F02B6F5B-FB2F-4E20-996D-BE99E9768039}.Release|Any CPU.Build.0 = Release|Any CPU
{C55114DF-F7C9-47A6-AF36-99F6C8079DE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C55114DF-F7C9-47A6-AF36-99F6C8079DE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C55114DF-F7C9-47A6-AF36-99F6C8079DE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C55114DF-F7C9-47A6-AF36-99F6C8079DE2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DigitalData.EmailProfilerDispatcher.API\DigitalData.EmailProfilerDispatcher.API.csproj" />
<ProjectReference Include="..\DigitalData.EmailProfilerDispatcher.Application\DigitalData.EmailProfilerDispatcher.Application.csproj" />
<ProjectReference Include="..\DigitalData.EmailProfilerDispatcher.Domain\DigitalData.EmailProfilerDispatcher.Domain.csproj" />
<ProjectReference Include="..\DigitalData.EmailProfilerDispatcher.Infrastructure\DigitalData.EmailProfilerDispatcher.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="DigitalData.Core.Application">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Application.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Contracts">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Contracts.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.DTO">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.DTO.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,68 @@
using DigitalData.EmailProfilerDispatcher.Application.Services;
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using DigitalData.EmailProfilerDispatcher.Domain.Entities;
namespace EmailProfilerDispatcher.Tests
{
[TestFixture]
public class FillTemplateTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void FillTemplate_ShouldReplacePlaceholdersWithPropertyValues()
{
// Arrange
var template = @"Guten Tag [NAME_RECEIVER],<br />
<br />
hiermit bestätigen wir Ihnen die erfolgreiche Signatur für den Vorgang <B><I>'[DOCUMENT_TITLE]'</I></B>.<br />
Wenn alle Vertragspartner unterzeichnet haben, erhalten Sie ebenfalls per email ein unterschriebenes Exemplar mit dem Signierungszertifikat!
<br />
Mit freundlichen Grüßen<br />
<br />
[NAME_PORTAL]";
var mailData1 = new MailData1();
var mailData2 = new MailData2();
var mailData3 = new MailData3();
var expectedOutput = @"Guten Tag Tom,<br />
<br />
hiermit bestätigen wir Ihnen die erfolgreiche Signatur für den Vorgang <B><I>'Vertragsdokument'</I></B>.<br />
Wenn alle Vertragspartner unterzeichnet haben, erhalten Sie ebenfalls per email ein unterschriebenes Exemplar mit dem Signierungszertifikat!
<br />
Mit freundlichen Grüßen<br />
<br />
DokumentenPortal";
// Act
var result = EmailOutService<Resource>.FillTemplate(template, mailData1, mailData2, mailData3);
// Assert
Assert.That(result, Is.EqualTo(expectedOutput));
}
public class Resource {}
public class MailData1
{
[TemplatePlaceholder("[NAME_RECEIVER]")]
public string NameReceiver { get; set; } = "Tom";
}
public class MailData2
{
[TemplatePlaceholder("[DOCUMENT_TITLE]")]
public string DocumentTitle { get; set; } = "Vertragsdokument";
}
public class MailData3
{
[TemplatePlaceholder("[NAME_PORTAL]")]
public string NamePortal { get; set; } = "DokumentenPortal";
}
}
}