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.EmailProfilerDispatcher.API.Resources;
using DigitalData.EmailProfilerDispatcher.Application.Contracts;
using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Application.Services;
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using DigitalData.EmailProfilerDispatcher.Domain.Entities;
using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts;
using Microsoft.AspNetCore.Mvc;
namespace DigitalData.EmailProfilerGateway.API.Controllers
{
@ -11,5 +15,45 @@ namespace DigitalData.EmailProfilerGateway.API.Controllers
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">
<HintPath>..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.API.dll</HintPath>
</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">
<HintPath>..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.Contracts.dll</HintPath>
</Reference>

View File

@ -12,7 +12,7 @@ var config = builder.Configuration;
builder.Services.AddControllers();
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
builder.Services.AddEndpointsApiExplorer();

View File

@ -1,4 +1,5 @@
using DigitalData.Core.Contracts.Application;
using DigitalData.Core.DTO;
using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Domain.Entities;
using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts;
@ -7,5 +8,6 @@ namespace DigitalData.EmailProfilerDispatcher.Application.Contracts
{
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
{
public record EmailOutCreateDto(
int ReminderTypeId,
int SendingProfile,
int ReferenceId,
string? ReferenceString,
int? EntityId,
int WfId,
string? WfReference,
string EmailAddress,
string EmailSubj,
string EmailBody,
string? EmailAttmt1,
DateTime? EmailSent,
string? Comment,
string? AddedWho, // Default value will be 'DEFAULT', made nullable
string? ChangedWho,
DateTime? ChangedWhen,
DateTime? ErrorTimestamp,
string? ErrorMsg
);
}
public record EmailOutCreateDto
{
public int ReminderTypeId { get; set; }
public int SendingProfile { get; set; }
public int ReferenceId { get; set; }
public int WfId { get; set; }
public string EmailAddress { get; set; }
public string EmailSubj { get; set; }
public string EmailBody { get; set; }
public string? ReferenceString { get; set; } = null;
public int? EntityId { get; set; } = null;
public string? WfReference { get; set; } = null;
public string? EmailAttmt1 { get; set; } = null;
public DateTime? EmailSent { get; set; } = null;
public string? Comment { get; set; } = null;
public string? AddedWho { get; set; } = "DEFAULT";
public string? ChangedWho { get; set; } = null;
public DateTime? ChangedWhen { get; set; } = null;
public DateTime? ErrorTimestamp { get; set; } = null;
public string? ErrorMsg { get; set; } = null;
}
}

View File

@ -1,10 +1,13 @@
using AutoMapper;
using DigitalData.Core.Application;
using DigitalData.Core.DTO;
using DigitalData.EmailProfilerDispatcher.Application.Contracts;
using DigitalData.EmailProfilerDispatcher.Application.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using DigitalData.EmailProfilerDispatcher.Domain.Entities;
using DigitalData.EmailProfilerDispatcher.Infrastructure.Contracts;
using Microsoft.Extensions.Localization;
using System.Reflection;
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 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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.EmailProfilerDispatcher.Infrastructure", "DigitalData.EmailProfilerDispatcher.Infrastructure\DigitalData.EmailProfilerDispatcher.Infrastructure.csproj", "{F02B6F5B-FB2F-4E20-996D-BE99E9768039}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmailProfilerDispatcher.Tests", "EmailProfilerDispatcher.Tests\EmailProfilerDispatcher.Tests.csproj", "{C55114DF-F7C9-47A6-AF36-99F6C8079DE2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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}.Release|Any CPU.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
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";
}
}
}