Compare commits

...

4 Commits

Author SHA1 Message Date
Developer 02
aba93f1dd4 NotMapped-Attribut zur AddedWhen-Eigenschaft hinzugefügt, damit MSSQL automatisch zuweisen kann. 2024-06-12 00:26:32 +02:00
Developer 02
e22d030fa2 FillTemplate-Methode als Erweiterung hinzugefügt. Option zum Schreiben von Platzhaltern als Wörterbuch hinzugefügt. 2024-06-10 16:19:48 +02:00
Developer 02
a8b3e88f09 Update test 2024-06-10 15:44:12 +02:00
Developer 02
a2bd369ed1 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.
2024-06-10 14:16:54 +02:00
12 changed files with 271 additions and 26 deletions

View File

@@ -1,8 +1,13 @@
using DigitalData.Core.API; using DigitalData.Core.API;
using DigitalData.EmailProfilerDispatcher.API.Resources;
using DigitalData.EmailProfilerDispatcher.Application;
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 +16,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 = template.FillTemplate(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,21 @@ 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 async Task<DataResult<int>> CreateWithTemplateAsync(EmailOutCreateDto createDto, params object[] models)
{
createDto.EmailSubj = createDto.EmailSubj.FillTemplate(models);
createDto.EmailBody = createDto.EmailBody.FillTemplate(models);
return await base.CreateAsync(createDto);
}
public async Task<DataResult<int>> CreateWithTemplateAsync(EmailOutCreateDto createDto, Dictionary<string, string> placeholders, params object[] models)
{
createDto.EmailSubj = createDto.EmailSubj.FillTemplate(placeholders);
createDto.EmailBody = createDto.EmailBody.FillTemplate(placeholders);
return await CreateWithTemplateAsync(createDto, models: models);
}
}
} }

View File

@@ -0,0 +1,42 @@
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace DigitalData.EmailProfilerDispatcher.Application
{
public static class TemplateExtensions
{
public static string FillTemplate(this 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;
}
public static string FillTemplate(this string template, Dictionary<string, string> placeholders)
{
foreach (var ph in placeholders)
template = template.Replace(ph.Key, ph.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

@@ -73,9 +73,10 @@ namespace DigitalData.EmailProfilerDispatcher.Domain.Entities
[Column("ADDED_WHEN")] [Column("ADDED_WHEN")]
[DefaultValue("getdate()")] [DefaultValue("getdate()")]
[NotMapped]
public DateTime? AddedWhen { get; set; } public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO", TypeName = "varchar(50)")] [Column("CHANGED_WHO", TypeName = "varchar(50)")]
[StringLength(50)] [StringLength(50)]
public string? ChangedWho { get; set; } public string? ChangedWho { get; set; }

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,69 @@
using DigitalData.EmailProfilerDispatcher.Application;
using DigitalData.EmailProfilerDispatcher.Application.Services;
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using DigitalData.EmailProfilerDispatcher.Domain.Entities;
using System.Diagnostics.Tracing;
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();
Dictionary<string, string> placeholders = new()
{
{ @"[NAME_PORTAL]", "DokumentenPortal" }
};
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 = template
.FillTemplate(placeholders)
.FillTemplate(template, mailData1, mailData2);
// Assert
Assert.That(result, Is.EqualTo(expectedOutput));
}
public class Resource {}
public class MailData1
{
[TemplatePlaceholder("[NAME_RECEIVER]")]
public string NameReceiver { get; set; } = "Tom";
}
public record MailData2
{
[TemplatePlaceholder("[DOCUMENT_TITLE]")]
public string DocumentTitle { get; set; } = "Vertragsdokument";
}
}
}