Skip to content

Commit d719a0f

Browse files
Add the package Emailing.Graph.
1 parent 67617f5 commit d719a0f

File tree

14 files changed

+509
-7
lines changed

14 files changed

+509
-7
lines changed

Directory.Packages.props

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
<Project>
22
<PropertyGroup>
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
4-
54
<!-- Versions of the packages -->
65
<DotNetVersion>9.0.11</DotNetVersion>
7-
86
</PropertyGroup>
97
<ItemGroup>
108
<PackageVersion Include="Azure.Communication.Email" Version="1.1.0" />
@@ -21,6 +19,7 @@
2119
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(DotNetVersion)" />
2220
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(DotNetVersion)" />
2321
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(DotNetVersion)" />
22+
<PackageVersion Include="Microsoft.Graph" Version="5.96.0" />
2423
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
2524
<PackageVersion Include="Moq" Version="4.20.72" />
2625
<PackageVersion Include="PosInformatique.FluentAssertions.Json" Version="1.6.0" />

PosInformatique.Foundations.slnx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
<Project Path="src/Emailing.Azure/Emailing.Azure.csproj" Id="3bcc0cf7-a90d-4d85-b540-58bf6c11eeee" />
4646
<Project Path="tests/Emailing.Azure.Tests/Emailing.Azure.Tests.csproj" Id="d782c2a5-8434-4cdb-8f53-ed20cdbc546d" />
4747
</Folder>
48+
<Folder Name="/Emailing/Graph/">
49+
<Project Path="src/Emailing.Graph/Emailing.Graph.csproj" Id="5874e6da-a783-4e2d-8317-0e783a6ee511" />
50+
<Project Path="tests/Emailing.Graph.Tests/Emailing.Graph.Tests.csproj" Id="3cace78b-a3ac-4a7a-b1f3-92dce29c5544" />
51+
</Folder>
4852
<Folder Name="/Emailing/Templates/" />
4953
<Folder Name="/Emailing/Templates/Razor/">
5054
<Project Path="src/Emailing.Templates.Razor/Emailing.Templates.Razor.csproj" Id="2d0078a9-305d-43ad-aaa9-9f35f62f0873" />

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ You can install any package using the .NET CLI or NuGet Package Manager.
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) |
3030
|<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) |
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](./src/Emailing/README.md) using **Azure Communication Service**. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.Emailing.Azure)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Azure) |
32+
|<img src="./src/Emailing/Icon.png" alt="PosInformatique.Foundations.Emailing.Graph icon" width="48" height="48" />|[**PosInformatique.Foundations.Emailing.Graph**](./src/Emailing.Graph/README.md) | `IEmailProvider` implementation for [PosInformatique.Foundations.Emailing](./src/Emailing/README.md) using **Microsoft Graph API**. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.Emailing.Graph)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph) |
3233
|<img src="./src/Emailing/Icon.png" alt="PosInformatique.Foundations.Emailing.Templates.Razor icon" width="48" height="48" />|[**PosInformatique.Foundations.Emailing.Templates.Razor**](./src/Emailing.Templates.Razor/README.md) | Helpers to build EmailTemplate instances from Razor components for subject and HTML body, supporting strongly-typed models and reusable layouts. | [![NuGet](https://img.shields.io/nuget/v/PosInformatique.Foundations.Emailing.Templates.Razor)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Templates.Razor) |
3334
|<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) |
3435
|<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) |

src/Emailing.Graph/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 Microsoft Graph Emailing provider.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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 Microsoft Graph API.
8+
Uses Microsoft.Graph.GraphServiceClient to send templated emails through Microsoft 365 mailboxes with Azure AD authentication via TokenCredential.
9+
</Description>
10+
<PackageTags>
11+
email;emailing;graph;microsoftgraph;microsoft365;azure;azuread;tokencredential;provider;dotnet;dependencyinjection;posinformatique
12+
</PackageTags>
13+
<PackageReleaseNotes>
14+
$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/CHANGELOG.md"))
15+
</PackageReleaseNotes>
16+
17+
</PropertyGroup>
18+
19+
<ItemGroup>
20+
<PackageReference Include="Microsoft.Graph" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<ProjectReference Include="..\Emailing\Emailing.csproj" />
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<Content Include="CHANGELOG.md" Pack="false" />
29+
<Content Include="..\Emailing\Icon.png" Pack="true" PackagePath="" />
30+
<Content Include="README.md" Pack="true" PackagePath="" />
31+
</ItemGroup>
32+
33+
</Project>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="GraphEmailProvider.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.Graph
8+
{
9+
using Microsoft.Graph;
10+
using Microsoft.Graph.Models;
11+
using Microsoft.Graph.Users.Item.SendMail;
12+
13+
/// <summary>
14+
/// Implementation of the <see cref="IEmailProvider"/> to send the e-mail using
15+
/// the <c>Graph</c> API.
16+
/// </summary>
17+
public sealed class GraphEmailProvider : IEmailProvider
18+
{
19+
private readonly GraphServiceClient serviceClient;
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="GraphEmailProvider"/> class
23+
/// using the Microsoft <see cref="GraphServiceClient"/>.
24+
/// </summary>
25+
/// <param name="serviceClient"><see cref="GraphServiceClient"/>
26+
/// used to call the <c>Azure Communication Service</c> API.</param>
27+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="serviceClient"/> argument is <see langword="null"/>.</exception>
28+
public GraphEmailProvider(GraphServiceClient serviceClient)
29+
{
30+
ArgumentNullException.ThrowIfNull(serviceClient);
31+
32+
this.serviceClient = serviceClient;
33+
}
34+
35+
/// <inheritdoc />
36+
public async Task SendAsync(EmailMessage message, CancellationToken cancellationToken = default)
37+
{
38+
ArgumentNullException.ThrowIfNull(message);
39+
40+
var graphMessage = new Message()
41+
{
42+
Body = new ItemBody
43+
{
44+
ContentType = BodyType.Html,
45+
Content = message.HtmlContent,
46+
},
47+
Subject = message.Subject,
48+
ToRecipients = new List<Recipient>
49+
{
50+
new()
51+
{
52+
EmailAddress = new EmailAddress
53+
{
54+
Address = message.To.Email.ToString(),
55+
Name = message.To.DisplayName,
56+
},
57+
},
58+
},
59+
};
60+
61+
var body = new SendMailPostRequestBody()
62+
{
63+
Message = graphMessage,
64+
SaveToSentItems = false,
65+
};
66+
67+
await this.serviceClient.Users[message.From.Email.ToString()].SendMail.PostAsync(body, cancellationToken: cancellationToken);
68+
}
69+
}
70+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="GraphEmailingBuilderExtensions.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.Graph
8+
{
9+
using Azure.Core;
10+
using Microsoft.Extensions.DependencyInjection.Extensions;
11+
using Microsoft.Graph;
12+
13+
/// <summary>
14+
/// Extension methods to configure the <c>Azure Communication Service</c> provider
15+
/// for the <see cref="IEmailManager"/>.
16+
/// </summary>
17+
public static class GraphEmailingBuilderExtensions
18+
{
19+
/// <summary>
20+
/// Configure the provider of <see cref="IEmailManager"/> to use <c>Azure Communication Service</c>.
21+
/// </summary>
22+
/// <param name="builder"><see cref="EmailingBuilder"/> which to configure.</param>
23+
/// <param name="tokenCredential">The <see cref="TokenCredential"/> for authenticating to Microsoft Graph API.</param>
24+
/// <param name="baseUrl">The base service URL of the API Graph. If not specified the <c>https://graph.microsoft.com/v1.0</c> will be use.</param>
25+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="builder"/> argument is <see langword="null"/>.</exception>
26+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="tokenCredential"/> argument is <see langword="null"/>.</exception>
27+
public static void UseGraph(this EmailingBuilder builder, TokenCredential tokenCredential, string? baseUrl = null)
28+
{
29+
ArgumentNullException.ThrowIfNull(builder);
30+
ArgumentNullException.ThrowIfNull(tokenCredential);
31+
32+
builder.Services.TryAddSingleton<IEmailProvider>(sp =>
33+
{
34+
var serviceClient = new GraphServiceClient(tokenCredential, null, baseUrl);
35+
36+
return new GraphEmailProvider(serviceClient);
37+
});
38+
}
39+
}
40+
}

src/Emailing.Graph/README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
### PosInformatique.Foundations.Emailing.Graph
2+
3+
[![NuGet version](https://img.shields.io/nuget/v/PosInformatique.Foundations.Emailing.Graph)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph/)
4+
[![NuGet downloads](https://img.shields.io/nuget/dt/PosInformatique.Foundations.Emailing.Graph)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph/)
5+
6+
## Introduction
7+
8+
[PosInformatique.Foundations.Emailing.Graph](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph/)
9+
provides an `IEmailProvider`
10+
implementation for [PosInformatique.Foundations.Emailing](../Emailing/README.md) based on the **Microsoft Graph** API.
11+
12+
It uses [Microsoft.Graph.GraphServiceClient](https://learn.microsoft.com/en-us/graph/sdks/create-client?tabs=csharp)
13+
to send templated emails (created via `IEmailManager`)
14+
through a Microsoft 365 mailbox, using Azure AD authentication.
15+
16+
Authentication is fully driven by an
17+
[Azure.Core.TokenCredential](https://learn.microsoft.com/en-us/dotnet/api/azure.core.tokencredential?view=azure-dotnet)
18+
instance, allowing you to use:
19+
20+
- Managed identity
21+
- Client credentials (client id/secret or certificate)
22+
- Interactive login, device code, etc.
23+
24+
## Install
25+
26+
You can install the package from [NuGet](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph/):
27+
28+
```powershell
29+
dotnet add package PosInformatique.Foundations.Emailing.Graph
30+
```
31+
32+
## Features
33+
34+
- `IEmailProvider` implementation using `Microsoft.Graph.GraphServiceClient`.
35+
- Simple configuration through `AddEmailing().UseGraph(...)`.
36+
- Authentication configured via `TokenCredential`:
37+
- `DefaultAzureCredential` (managed identity, VS, CLI, etc.)
38+
- `ClientSecretCredential`, `ClientCertificateCredential`, etc.
39+
- Optional `baseUrl` parameter to customize the Graph endpoint (defaults to `https://graph.microsoft.com/v1.0`).
40+
- Sends HTML emails using the `EmailMessage` produced by [PosInformatique.Foundations.Emailing](../Emailing/README.md).
41+
42+
## Basic configuration
43+
44+
### Using DefaultAzureCredential (managed identity or local dev)
45+
46+
```csharp
47+
using Azure.Identity;
48+
using Microsoft.Extensions.DependencyInjection;
49+
using PosInformatique.Foundations.EmailAddresses;
50+
using PosInformatique.Foundations.Emailing;
51+
using PosInformatique.Foundations.Emailing.Graph;
52+
53+
var services = new ServiceCollection();
54+
55+
// TokenCredential for Microsoft Graph (for example: managed identity or local dev)
56+
var credential = new DefaultAzureCredential();
57+
58+
services
59+
.AddEmailing(options =>
60+
{
61+
options.SenderEmailAddress = EmailAddress.Parse("sender@yourtenant.onmicrosoft.com");
62+
63+
// Register your templates here...
64+
// options.RegisterTemplate(EmailTemplateIdentifiers.Invitation, invitationTemplate);
65+
})
66+
.UseGraph(credential);
67+
```
68+
69+
### Using client credentials (app registration)
70+
71+
If you want to authenticate with a client id / tenant id / client secret:
72+
73+
```csharp
74+
using Azure.Identity;
75+
using PosInformatique.Foundations.Emailing.Graph;
76+
77+
var tenantId = configuration["AzureAd:TenantId"];
78+
var clientId = configuration["AzureAd:ClientId"];
79+
var clientSecret = configuration["AzureAd:ClientSecret"];
80+
81+
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
82+
83+
services
84+
.AddEmailing(options =>
85+
{
86+
options.SenderEmailAddress = EmailAddress.Parse("sender@yourtenant.onmicrosoft.com");
87+
})
88+
.UseGraph(credential);
89+
```
90+
91+
The `TokenCredential` you provide is responsible for acquiring tokens for the Microsoft Graph API. The provider does not manage scopes or credentials itself; this is entirely delegated to the credential implementation.
92+
93+
### Custom Graph endpoint
94+
95+
You can optionally customize the Graph base URL (for example, for national clouds):
96+
97+
```csharp
98+
var baseUrl = "https://graph.microsoft.com/v1.0/beta";
99+
100+
services
101+
.AddEmailing(options =>
102+
{
103+
options.SenderEmailAddress = EmailAddress.Parse("sender@yourtenant.onmicrosoft.com");
104+
})
105+
.UseGraph(credential, baseUrl);
106+
```
107+
108+
If `baseUrl` is `null`, `https://graph.microsoft.com/v1.0` is used by default.
109+
110+
## Typical end-to-end usage
111+
112+
1. Configure emailing (sender address, templates) with `AddEmailing(...)`.
113+
2. Configure the Graph provider with `UseGraph(TokenCredential, baseUrl?)`.
114+
3. Inject `IEmailManager` and create emails from template identifiers.
115+
4. Add recipients and models.
116+
5. Call `SendAsync(...)` to send emails via Microsoft Graph.
117+
118+
## Links
119+
120+
- [NuGet package: Emailing](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing/)
121+
s- [Microsoft Graph .NET SDK](https://learn.microsoft.com/graph/sdks/sdks-overview)
122+
- [Azure Identity (TokenCredential)](https://learn.microsoft.com/dotnet/azure/sdk/authentication/)
123+
- [Source code](https://github.com/PosInformatique/PosInformatique.Foundations)

src/Emailing/README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ It allows you to:
1313
- Instantiate emails from registered templates via an `IEmailManager`.
1414
- Generate and send templated emails for each recipient through an `IEmailProvider` implementation.
1515

16-
The actual transport (SMTP, Azure Communication Service, etc.) is delegated to a provider implementation.
16+
The actual transport (SMTP, Azure Communication Service, Graph API, etc.) is delegated to a provider implementation.
1717
Existing implementation are available in the following packages:
1818
- Azure Communication Service: [PosInformatique.Foundations.Emailing.Azure](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Azure/).
19+
- Microsoft Graph API: [PosInformatique.Foundations.Emailing.Graph](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph/).
1920

2021
## Install
2122

@@ -133,14 +134,17 @@ services.AddEmailing(options =>
133134
The `AddEmailing()` method returns an `EmailingBuilder` that can be used to continue configuring the emailing infrastructure
134135
(for example, provider registration in other packages).
135136

136-
### Email provider
137+
### Email providers
137138

138139
`IEmailProvider` is responsible for sending the final `EmailMessage`.
139140
This package only defines the abstraction. A typical provider implementation is located in another package, such as:
140141

141142
- [PosInformatique.Foundations.Emailing.Azure](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Azure/).
143+
- [PosInformatique.Foundations.Emailing.Graph](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph/).
142144

143-
See the [PosInformatique.Foundations.Emailing.Azure](../Emailing.Azure/README.md) documentation for an example of provider registration.
145+
See the [PosInformatique.Foundations.Emailing.Azure](../Emailing.Azure/README.md)
146+
or [PosInformatique.Foundations.Emailing.Graph](../Emailing.Graph/README.md)
147+
documentation for an example of provider registration.
144148

145149
## Usage
146150

@@ -231,6 +235,7 @@ The typical flow is:
231235

232236
- [NuGet package: Emailing (core library)](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing/)
233237
- [NuGet package: Emailing.Azure](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Azure/)
238+
- [NuGet package: Emailing.Graph](https://www.nuget.org/packages/PosInformatique.Foundations.Emailing.Graph/)
234239
- [NuGet package: Text.Templating.Razor](https://www.nuget.org/packages/PosInformatique.Foundations.Text.Templating.Razor/)
235240
- [NuGet package: Text.Templating.Scriban](https://www.nuget.org/packages/PosInformatique.Foundations.Text.Templating.Scriban/)
236241
- [Source code](https://github.com/PosInformatique/PosInformatique.Foundations)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
1.0.0
2-
- Initial release with the Razor Text Templating feature.
2+
- Initial release with the Scriban Text Templating feature.

0 commit comments

Comments
 (0)