Skip to content

Commit a3386e3

Browse files
Add emailing implementation.
1 parent 9d1bfe2 commit a3386e3

File tree

64 files changed

+2913
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2913
-2
lines changed

Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<!-- Add the default using directive for all the code -->
4848
<ItemGroup>
4949
<Using Include="System" />
50+
<Using Include="System.Collections.ObjectModel" />
5051
<Using Include="System.Threading.Tasks" />
5152
</ItemGroup>
5253

Directory.Packages.props

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
</PropertyGroup>
55
<ItemGroup>
6+
<PackageVersion Include="Azure.Communication.Email" Version="1.1.0" />
67
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
78
<PackageVersion Include="FluentAssertions" Version="7.2.0" />
89
<PackageVersion Include="FluentValidation" Version="12.1.0" />
10+
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="9.0.11" />
911
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.11" />
1012
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.11" />
13+
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.13.0" />
14+
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.11" />
15+
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.11" />
16+
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.11" />
17+
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.11" />
1118
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
1219
<PackageVersion Include="Moq" Version="4.20.72" />
1320
<PackageVersion Include="PosInformatique.FluentAssertions.Json" Version="1.6.0" />

PosInformatique.Foundations.slnx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@
3737
<Project Path="src/EmailAddresses.Json/EmailAddresses.Json.csproj" />
3838
<Project Path="tests/EmailAddresses.Json.Tests/EmailAddresses.Json.Tests.csproj" />
3939
</Folder>
40+
<Folder Name="/Emailing/">
41+
<Project Path="src/Emailing/Emailing.csproj" Id="bc2b6c86-880a-46ab-b7d2-f0e0b1b93c7b" />
42+
<Project Path="tests/Emailing.Tests/Emailing.Tests.csproj" Id="0a3bb727-7b61-47cc-bdf1-76e3659b3e5b" />
43+
</Folder>
44+
<Folder Name="/Emailing/Azure/">
45+
<Project Path="src/Emailing.Azure/Emailing.Azure.csproj" Id="3bcc0cf7-a90d-4d85-b540-58bf6c11eeee" />
46+
<Project Path="tests/Emailing.Azure.Tests/Emailing.Azure.Tests.csproj" Id="d782c2a5-8434-4cdb-8f53-ed20cdbc546d" />
47+
</Folder>
4048
<Folder Name="/MediaTypes/">
4149
<Project Path="src/MediaTypes/MediaTypes.csproj" Id="31039dbf-ee6f-414c-8345-b92a6bc3e60a" />
4250
<Project Path="tests/MediaTypes.Tests/MediaTypes.Tests.csproj" Id="1cee2c26-ceb5-43c8-a5c9-98ced675385c" />
@@ -73,4 +81,12 @@
7381
<Project Path="src/People.Json/People.Json.csproj" />
7482
<Project Path="tests/People.Json.Tests/People.Json.Tests.csproj" />
7583
</Folder>
84+
<Folder Name="/Text/" />
85+
<Folder Name="/Text/Templating/">
86+
<Project Path="src/Text.Templating/Text.Templating.csproj" Id="25d9a572-5f28-4e35-9836-8f8d70617e0d" />
87+
</Folder>
88+
<Folder Name="/Text/Templating/Razor/">
89+
<Project Path="src/Text.Templating.Razor/Text.Templating.Razor.csproj" Id="aab32a1c-3774-4216-8558-76073bc8ac1f" />
90+
<Project Path="tests/Text.Templating.Razor.Tests/Text.Templating.Razor.Tests.csproj" Id="c6778dd0-32fa-4725-9416-6f6d6862c988" />
91+
</Folder>
7692
</Solution>

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ You can install any package using the .NET CLI or NuGet Package Manager.
2727
|<img src="./src/EmailAddresses/Icon.png" alt="PosInformatique.Foundations.EmailAddresses.EntityFramework icon" width="48" height="48" />|[**PosInformatique.Foundations.EmailAddresses.EntityFramework**](./src/EmailAddresses.EntityFramework/README.md) | Entity Framework Core integration for the `EmailAddress` value object, including property configuration and value converter for seamless database persistence. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.EmailAddresses.EntityFramework)](https://www.nuget.org/packages/PosInformatique.Foundations.EmailAddresses.EntityFramework) |
2828
|<img src="./src/EmailAddresses/Icon.png" alt="PosInformatique.Foundations.EmailAddresses.FluentValidation icon" width="48" height="48" />|[**PosInformatique.Foundations.EmailAddresses.FluentValidation**](./src/EmailAddresses.FluentValidation/README.md) | FluentValidation integration for the `EmailAddress` value object, providing dedicated validators and rules to ensure RFC 5322 compliant email addresses. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.EmailAddresses.FluentValidation)](https://www.nuget.org/packages/PosInformatique.Foundations.EmailAddresses.FluentValidation) |
2929
|<img src="./src/EmailAddresses/Icon.png" alt="PosInformatique.Foundations.EmailAddresses.Json icon" width="48" height="48" />|[**PosInformatique.Foundations.EmailAddresses.Json**](./src/EmailAddresses.Json/README.md) | `System.Text.Json` converter for the `EmailAddress` value object, enabling seamless serialization and deserialization of RFC 5322 compliant email addresses. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.EmailAddresses.Json)](https://www.nuget.org/packages/PosInformatique.Foundations.EmailAddresses.Json) |
30+
|<img src="./src/Emailing/Icon.png" alt="PosInformatique.Foundations.Emailing icon" width="48" height="48" />|[**PosInformatique.Foundations.Emailing**](./src/Emailing/README.md) | Template-based emailing infrastructure for .NET that lets you register strongly-typed email templates, create emails from models, and send them through pluggable providers. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.Emailing)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing) |
31+
|<img src="./src/Emailing/Icon.png" alt="PosInformatique.Foundations.Emailing.Azure icon" width="48" height="48" />|[**PosInformatique.Foundations.Emailing.Azure**](./src/Emailing.Azure/README.md) | `IEmailProvider` implementation for `PosInformatique.Foundations.Emailing` using **Azure Communication Service**. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.Emailing.Azure)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Azure) |
3032
|<img src="./src/MediaTypes/Icon.png" alt="PosInformatique.Foundations.MediaTypes icon" width="48" height="48" />|[**PosInformatique.Foundations.MediaTypes**](./src/MediaTypes/README.md) | Immutable `MimeType` value object with well-known media types and helpers to map between media types and file extensions. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.MediaTypes)](https://www.nuget.org/packages/PosInformatique.Foundations.MediaTypes) |
3133
|<img src="./src/MediaTypes/Icon.png" alt="PosInformatique.Foundations.MediaTypes.EntityFramework icon" width="48" height="48" />|[**PosInformatique.Foundations.MediaTypes.EntityFramework**](./src/MediaTypes.EntityFramework/README.md) | Entity Framework Core integration for the `MimeType` value object, including property configuration and value converter for seamless database persistence. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.MediaTypes.EntityFramework)](https://www.nuget.org/packages/PosInformatique.Foundations.MediaTypes.EntityFramework) |
3234
|<img src="./src/MediaTypes/Icon.png" alt="PosInformatique.Foundations.MediaTypes.Json icon" width="48" height="48" />|[**PosInformatique.Foundations.MediaTypes.Json**](./src/MediaTypes.Json/README.md) | `System.Text.Json` converter for the `MimeType` value object, enabling seamless serialization and deserialization of MIME types within JSON documents. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.MediaTypes.Json)](https://www.nuget.org/packages/PosInformatique.Foundations.MediaTypes.Json) |
@@ -36,6 +38,8 @@ You can install any package using the .NET CLI or NuGet Package Manager.
3638
|<img src="./src/People/Icon.png" alt="PosInformatique.Foundations.People.FluentAssertions icon" width="48" height="48" />|[**PosInformatique.Foundations.People.FluentAssertions**](./src/People.FluentAssertions/README.md) | [FluentAssertions](https://fluentassertions.com/) extensions for `FirstName` and `LastName` to avoid ambiguity and provide `Should().Be(string)` assertions (case-sensitive on normalized values). | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.People.FluentAssertions)](https://www.nuget.org/packages/PosInformatique.Foundations.People.FluentAssertions) |
3739
|<img src="./src/People/Icon.png" alt="PosInformatique.Foundations.People.FluentValidation icon" width="48" height="48" />|[**PosInformatique.Foundations.People.FluentValidation**](./src/People.FluentValidation/README.md) | [FluentValidation](https://fluentvalidation.net/) extensions for `FirstName` and `LastName` value objects. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.People.FluentValidation)](https://www.nuget.org/packages/PosInformatique.Foundations.People.FluentValidation) |
3840
|<img src="./src/People/Icon.png" alt="PosInformatique.Foundations.People.Json icon" width="48" height="48" />|[**PosInformatique.Foundations.People.Json**](./src/People.Json/README.md) | `System.Text.Json` converters for `FirstName` and `LastName`, with validation and easy registration via `AddPeopleConverters()`. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.People.Json)](https://www.nuget.org/packages/PosInformatique.Foundations.People.Json) |
41+
|<img src="./src/Text.Templating/Icon.png" alt="PosInformatique.Foundations.Text.Templating icon" width="48" height="48" />|[**PosInformatique.Foundations.Text.Templating**](./src/Text.Templating/README.md) | Abstractions for text templating, including the `TextTemplate<TModel>` base class and `ITextTemplateRenderContext` interface, to be used by concrete templating engine implementations such as Razor-based text templates. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.Text.Templating)](https://www.nuget.org/packages/PosInformatique.Foundations.Text.Templating) |
42+
|<img src="./src/Text.Templating/Icon.png" alt="PosInformatique.Foundations.Text.Templating.Razor icon" width="48" height="48" />|[**PosInformatique.Foundations.Text.Templating.Razor**](./src/Text.Templating.Razor/README.md) | Razor-based text templating using Blazor components, allowing generation of text from Razor views with a strongly-typed Model parameter and full dependency injection integration. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.Text.Templating.Razor)](https://www.nuget.org/packages/PosInformatique.Foundations.Text.Templating.Razor) |
3943

4044
> Note: Each package is completely independent. You install only what you need.
4145
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="AzureEmailProvider.cs" company="P.O.S Informatique">
3+
// Copyright (c) P.O.S Informatique. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace PosInformatique.Foundations.Emailing.Azure
8+
{
9+
/// <summary>
10+
/// Implementation of the <see cref="IEmailProvider"/> to send the e-mail using
11+
/// <c>Azure Communication Service</c>.
12+
/// </summary>
13+
public sealed class AzureEmailProvider : IEmailProvider
14+
{
15+
private readonly global::Azure.Communication.Email.EmailClient client;
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="AzureEmailProvider"/> class
19+
/// using the Microsoft <see cref="global::Azure.Communication.Email.EmailClient"/>.
20+
/// </summary>
21+
/// <param name="client"><see cref="global::Azure.Communication.Email.EmailClient"/>
22+
/// used to call the <c>Azure Communication Service</c> API.</param>
23+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="client"/> argument is <see langword="null"/>.</exception>
24+
public AzureEmailProvider(global::Azure.Communication.Email.EmailClient client)
25+
{
26+
ArgumentNullException.ThrowIfNull(client);
27+
28+
this.client = client;
29+
}
30+
31+
/// <inheritdoc />
32+
public async Task SendAsync(EmailMessage message, CancellationToken cancellationToken = default)
33+
{
34+
ArgumentNullException.ThrowIfNull(message);
35+
36+
var receipients = new global::Azure.Communication.Email.EmailRecipients()
37+
{
38+
To =
39+
{
40+
new global::Azure.Communication.Email.EmailAddress(message.To.Email, message.To.DisplayName),
41+
},
42+
};
43+
44+
var content = new global::Azure.Communication.Email.EmailContent(message.Subject)
45+
{
46+
Html = message.HtmlContent,
47+
};
48+
49+
var azureMessage = new global::Azure.Communication.Email.EmailMessage(message.From.Email, receipients, content);
50+
51+
await this.client.SendAsync(global::Azure.WaitUntil.Started, azureMessage, cancellationToken);
52+
}
53+
}
54+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="AzureEmailingBuilderExtensions.cs" company="P.O.S Informatique">
3+
// Copyright (c) P.O.S Informatique. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace PosInformatique.Foundations.Emailing.Azure
8+
{
9+
using global::Azure.Communication.Email;
10+
using global::Azure.Core.Extensions;
11+
using Microsoft.Extensions.Azure;
12+
using Microsoft.Extensions.DependencyInjection.Extensions;
13+
14+
/// <summary>
15+
/// Extension methods to configure the <c>Azure Communication Service</c> provider
16+
/// for the <see cref="IEmailManager"/>.
17+
/// </summary>
18+
public static class AzureEmailingBuilderExtensions
19+
{
20+
/// <summary>
21+
/// Configure the provider of <see cref="IEmailManager"/> to use <c>Azure Communication Service</c>.
22+
/// </summary>
23+
/// <param name="builder"><see cref="EmailingBuilder"/> which to configure.</param>
24+
/// <param name="uri">Uri to the the <c>Azure Communication Service</c> instance.</param>
25+
/// <param name="clientBuilder">Allows to configure the <see cref="EmailClient"/> used by the provider.</param>
26+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="builder"/> argument is <see langword="null"/>.</exception>
27+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="uri"/> argument is <see langword="null"/>.</exception>
28+
public static void UseAzureCommunicationService(this EmailingBuilder builder, Uri uri, Action<IAzureClientBuilder<EmailClient, EmailClientOptions>>? clientBuilder = null)
29+
{
30+
ArgumentNullException.ThrowIfNull(builder);
31+
ArgumentNullException.ThrowIfNull(uri);
32+
33+
builder.Services.TryAddSingleton<IEmailProvider, AzureEmailProvider>();
34+
35+
builder.Services.AddAzureClients(builder =>
36+
{
37+
var emailClientBuilder = builder.AddEmailClient(uri);
38+
39+
if (clientBuilder is not null)
40+
{
41+
clientBuilder(emailClientBuilder);
42+
}
43+
});
44+
}
45+
46+
/// <summary>
47+
/// Configure the provider of <see cref="IEmailManager"/> to use <c>Azure Communication Service</c>.
48+
/// </summary>
49+
/// <param name="builder"><see cref="EmailingBuilder"/> which to configure.</param>
50+
/// <param name="connectionString">Connection string to the <c>Azure Communication Service</c> instance.</param>
51+
/// <param name="clientBuilder">Allows to configure the <see cref="EmailClient"/> used by the provider.</param>
52+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="builder"/> argument is <see langword="null"/>.</exception>
53+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="connectionString"/> argument is <see langword="null"/>.</exception>
54+
public static void UseAzureCommunicationService(this EmailingBuilder builder, string connectionString, Action<IAzureClientBuilder<EmailClient, EmailClientOptions>>? clientBuilder = null)
55+
{
56+
ArgumentNullException.ThrowIfNull(builder);
57+
ArgumentNullException.ThrowIfNull(connectionString);
58+
59+
builder.Services.TryAddSingleton<IEmailProvider, AzureEmailProvider>();
60+
61+
builder.Services.AddAzureClients(builder =>
62+
{
63+
var emailClientBuilder = builder.AddEmailClient(connectionString);
64+
65+
if (clientBuilder is not null)
66+
{
67+
clientBuilder(emailClientBuilder);
68+
}
69+
});
70+
}
71+
}
72+
}

src/Emailing.Azure/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1.0.0
2+
- Initial release with Azure Communication Service Emailing provider.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<IsPackable>true</IsPackable>
5+
6+
<Description>
7+
Provides an IEmailProvider implementation for PosInformatique.Foundations.Emailing using Azure Communication Service.
8+
Uses Azure.Communication.Email.EmailClient to send templated emails and can be configured via AddEmailing().UseAzureCommunicationService(), including EmailClient and EmailClientOptions customization.
9+
</Description>
10+
<PackageTags>email;emailing;azure;azurecommunicationservice;communication;emailclient;provider;dotnet;dependencyinjection;posinformatique</PackageTags>
11+
<PackageReleaseNotes>
12+
$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/CHANGELOG.md"))
13+
</PackageReleaseNotes>
14+
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<PackageReference Include="Azure.Communication.Email" />
19+
<PackageReference Include="Microsoft.Extensions.Azure" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<ProjectReference Include="..\Emailing\Emailing.csproj" />
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<Content Include="CHANGELOG.md" Pack="false" />
28+
<Content Include="..\Emailing\Icon.png" Pack="true" PackagePath="" />
29+
<Content Include="README.md" Pack="true" PackagePath="" />
30+
</ItemGroup>
31+
32+
</Project>

0 commit comments

Comments
 (0)