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.EmailProfilerDispatcher.API.Resources;
using DigitalData.EmailProfilerDispatcher.Application;
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 +16,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 = 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">
<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,21 @@ namespace DigitalData.EmailProfilerDispatcher.Application.Services
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")]
[DefaultValue("getdate()")]
[NotMapped]
public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO", TypeName = "varchar(50)")]
[Column("CHANGED_WHO", TypeName = "varchar(50)")]
[StringLength(50)]
public string? ChangedWho { get; set; }

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,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";
}
}
}