diff --git a/src/FluentEmail.Graph/FluentEmail.Graph.csproj b/src/FluentEmail.Graph/FluentEmail.Graph.csproj
index 375f7c1..a5539af 100644
--- a/src/FluentEmail.Graph/FluentEmail.Graph.csproj
+++ b/src/FluentEmail.Graph/FluentEmail.Graph.csproj
@@ -20,9 +20,11 @@
0.0.1
MIT
detailed
- 2.3
- v2.2 Added support for Headers
-v2.1 Added support for Inline images
+ 2.4
+ v2.4 Updated Microsoft.Graph to v5
+v2.2 Added support for Headers
+v2.1 Added support for Inline images
+
@@ -38,16 +40,16 @@ v2.1 Added support for Inline images
-
+
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/FluentEmail.Graph/FluentEmailServicesBuilderExtensions.cs b/src/FluentEmail.Graph/FluentEmailServicesBuilderExtensions.cs
index 3aa9538..afe9eb4 100644
--- a/src/FluentEmail.Graph/FluentEmailServicesBuilderExtensions.cs
+++ b/src/FluentEmail.Graph/FluentEmailServicesBuilderExtensions.cs
@@ -1,44 +1,43 @@
-namespace FluentEmail.Graph
-{
- using FluentEmail.Core.Interfaces;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.DependencyInjection.Extensions;
- using Microsoft.Graph;
+namespace FluentEmail.Graph;
+
+using FluentEmail.Core.Interfaces;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Graph;
- ///
- /// Contains extension methods to register the with the FluentEmailServicesBuilder from FluentEmail.Core.
- ///
- public static class FluentEmailServicesBuilderExtensions
+///
+/// Contains extension methods to register the with the FluentEmailServicesBuilder from FluentEmail.Core.
+///
+public static class FluentEmailServicesBuilderExtensions
+{
+ public static FluentEmailServicesBuilder AddGraphSender(
+ this FluentEmailServicesBuilder builder,
+ GraphSenderOptions options)
{
- public static FluentEmailServicesBuilder AddGraphSender(
- this FluentEmailServicesBuilder builder,
- GraphSenderOptions options)
- {
- builder.Services.TryAdd(ServiceDescriptor.Scoped(_ => new GraphSender(options)));
- return builder;
- }
+ builder.Services.TryAdd(ServiceDescriptor.Scoped(_ => new GraphSender(options)));
+ return builder;
+ }
- public static FluentEmailServicesBuilder AddGraphSender(
- this FluentEmailServicesBuilder builder,
- string graphEmailClientId,
- string graphEmailTenantId,
- string graphEmailSecret)
+ public static FluentEmailServicesBuilder AddGraphSender(
+ this FluentEmailServicesBuilder builder,
+ string graphEmailClientId,
+ string graphEmailTenantId,
+ string graphEmailSecret)
+ {
+ var options = new GraphSenderOptions
{
- var options = new GraphSenderOptions
- {
- ClientId = graphEmailClientId,
- TenantId = graphEmailTenantId,
- Secret = graphEmailSecret,
- };
- return builder.AddGraphSender(options);
- }
+ ClientId = graphEmailClientId,
+ TenantId = graphEmailTenantId,
+ Secret = graphEmailSecret,
+ };
+ return builder.AddGraphSender(options);
+ }
- public static FluentEmailServicesBuilder AddGraphSender(
- this FluentEmailServicesBuilder builder,
- GraphServiceClient graphClient)
- {
- builder.Services.TryAdd(ServiceDescriptor.Scoped(_ => new GraphSender(graphClient)));
- return builder;
- }
+ public static FluentEmailServicesBuilder AddGraphSender(
+ this FluentEmailServicesBuilder builder,
+ GraphServiceClient graphClient)
+ {
+ builder.Services.TryAdd(ServiceDescriptor.Scoped(_ => new GraphSender(graphClient)));
+ return builder;
}
-}
+}
\ No newline at end of file
diff --git a/src/FluentEmail.Graph/GraphSender.cs b/src/FluentEmail.Graph/GraphSender.cs
index 2881c31..0ea60ef 100644
--- a/src/FluentEmail.Graph/GraphSender.cs
+++ b/src/FluentEmail.Graph/GraphSender.cs
@@ -1,175 +1,190 @@
-namespace FluentEmail.Graph
+namespace FluentEmail.Graph;
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Azure.Identity;
+using FluentEmail.Core;
+using FluentEmail.Core.Interfaces;
+using FluentEmail.Core.Models;
+using Microsoft.Graph;
+using Microsoft.Graph.Models;
+using Microsoft.Graph.Models.ODataErrors;
+using Microsoft.Graph.Users.Item.Messages.Item.Attachments.CreateUploadSession;
+using Microsoft.Graph.Users.Item.SendMail;
+using Attachment = FluentEmail.Core.Models.Attachment;
+
+///
+/// Implementation of ISender for the Microsoft Graph API.
+/// See .
+///
+public class GraphSender : ISender
{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Azure.Identity;
- using FluentEmail.Core;
- using FluentEmail.Core.Interfaces;
- using FluentEmail.Core.Models;
- using Microsoft.Graph;
-
- ///
- /// Implementation of ISender for the Microsoft Graph API.
- /// See .
- ///
- public class GraphSender : ISender
- {
- private const int ThreeMbLimit = 3145728;
+ private const int ThreeMbLimit = 3145728;
- private readonly GraphServiceClient graphClient;
+ private readonly GraphServiceClient graphClient;
- public GraphSender(GraphSenderOptions options)
- {
- ClientSecretCredential spn = new (
- options.TenantId,
- options.ClientId,
- options.Secret);
- this.graphClient = new (spn);
- }
+ public GraphSender(GraphSenderOptions options)
+ {
+ ClientSecretCredential spn = new (
+ options.TenantId,
+ options.ClientId,
+ options.Secret);
+ this.graphClient = new (spn);
+ }
- public GraphSender(GraphServiceClient graphClient)
- {
- this.graphClient = graphClient;
- }
+ public GraphSender(GraphServiceClient graphClient)
+ {
+ this.graphClient = graphClient;
+ }
- public SendResponse Send(IFluentEmail email, CancellationToken? token = null)
- {
- return this.SendAsync(email, token)
- .GetAwaiter()
- .GetResult();
- }
+ public SendResponse Send(IFluentEmail email, CancellationToken? token = default)
+ {
+ return this.SendAsync(email, token)
+ .GetAwaiter()
+ .GetResult();
+ }
- public async Task SendAsync(IFluentEmail email, CancellationToken? token = null)
+ public async Task SendAsync(IFluentEmail email, CancellationToken? token = default)
+ {
+ try
{
- try
+ token ??= default;
+ if (email.Data.Attachments?.Any() == true)
{
- if (email.Data.Attachments?.Any() == true)
- {
- var draftMessage = await this.SendWithAttachments(email);
-
- return new SendResponse { MessageId = draftMessage.Id, };
- }
+ var draftMessage = await this.SendWithAttachments(email, token.Value);
- var message = await this.SendWithoutAttachments(email);
-
- return new SendResponse { MessageId = message.Id, };
- }
- catch (Exception ex)
- {
- return new SendResponse { ErrorMessages = new List { ex.Message }, };
+ return new SendResponse { MessageId = draftMessage.Id };
}
- }
- private static byte[] GetAttachmentBytes(Stream stream)
- {
- using var m = new MemoryStream();
- stream.CopyTo(m);
+ var message = await this.SendWithoutAttachments(email, token.Value);
- return m.ToArray();
+ // message.Id is empty
+ return new SendResponse { MessageId = message.Id };
}
-
- private async Task SendWithoutAttachments(IFluentEmail email)
+ catch (ODataError odataError)
{
- // https://docs.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http
- var message = MessageCreation.CreateGraphMessageFromFluentEmail(email);
- await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
- .SendMail(message)
- .Request()
- .PostAsync();
-
- return message;
+ return new SendResponse
+ {
+ ErrorMessages = new List { $"[{odataError.Error.Code}] {odataError.Error.Message}" },
+ };
}
-
- private async Task SendWithAttachments(IFluentEmail email)
+ catch (Exception ex)
{
- // https://docs.microsoft.com/en-us/graph/api/user-post-messages?view=graph-rest-1.0&tabs=csharp
- var draftMessage = await this.CreateDraftMessage(email);
- await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
- .Messages[draftMessage.Id]
- .Send()
- .Request()
- .PostAsync();
-
- return draftMessage;
+ return new SendResponse { ErrorMessages = new List { ex.Message } };
}
+ }
- private async Task CreateDraftMessage(IFluentEmail email)
- {
- var template = MessageCreation.CreateGraphMessageFromFluentEmail(email);
+ private static byte[] GetAttachmentBytes(Stream stream)
+ {
+ using var m = new MemoryStream();
+ stream.CopyTo(m);
- var request = this.graphClient.Users[email.Data.FromAddress.EmailAddress]
- .Messages.Request();
+ return m.ToArray();
+ }
- var draftMessage = await request.AddAsync(template);
- foreach (var attachment in email.Data.Attachments)
- {
- if (attachment.Data.Length < ThreeMbLimit)
- {
- await this.UploadAttachmentUnder3Mb(
- email,
- draftMessage,
- attachment);
- }
- else
- {
- await this.UploadAttachment3MbOrOver(
- email,
- draftMessage,
- attachment);
- }
- }
+ private async Task SendWithoutAttachments(
+ IFluentEmail email,
+ CancellationToken cancellationToken = default)
+ {
+ // https://docs.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http
+ var message = MessageCreation.CreateGraphMessageFromFluentEmail(email);
+ await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
+ .SendMail.PostAsync(
+ new SendMailPostRequestBody { Message = message },
+ requestConfiguration: null,
+ cancellationToken: cancellationToken);
+
+ return message;
+ }
- return draftMessage;
- }
+ private async Task SendWithAttachments(IFluentEmail email, CancellationToken cancellationToken)
+ {
+ // https://docs.microsoft.com/en-us/graph/api/user-post-messages?view=graph-rest-1.0&tabs=csharp
+ var message = MessageCreation.CreateGraphMessageFromFluentEmail(email);
+
+ var draftMessage = await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
+ .Messages.PostAsync(message, cancellationToken: cancellationToken);
- private async Task UploadAttachmentUnder3Mb(IFluentEmail email, Message draft, Core.Models.Attachment a)
+ // upload attachments in the draft message
+ foreach (var attachment in email.Data.Attachments)
{
- var attachment = new FileAttachment
+ if (attachment.Data.Length < ThreeMbLimit)
{
- Name = a.Filename,
- ContentType = a.ContentType,
- ContentBytes = GetAttachmentBytes(a.Data),
- ContentId = a.ContentId,
- IsInline = a.IsInline,
-
- // can never be bigger than 3MB, so it is safe to cast to int
- Size = (int)a.Data.Length,
- };
+ await this.UploadAttachmentUnder3Mb(
+ email,
+ draftMessage,
+ attachment,
+ cancellationToken);
- await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
- .Messages[draft.Id]
- .Attachments.Request()
- .AddAsync(attachment);
+ continue;
+ }
+
+ await this.UploadAttachment3MbOrOver(
+ email,
+ draftMessage,
+ attachment,
+ cancellationToken);
}
- private async Task UploadAttachment3MbOrOver(
- IFluentEmail email,
- Message draft,
- Core.Models.Attachment attachment)
- {
- var attachmentItem = new AttachmentItem
- {
- AttachmentType = AttachmentType.File,
- Name = attachment.Filename,
- Size = attachment.Data.Length,
- ContentType = attachment.ContentType,
- ContentId = attachment.ContentId,
- IsInline = attachment.IsInline,
- };
+ await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
+ .Messages[draftMessage.Id]
+ .Send.PostAsync(cancellationToken: cancellationToken);
- var uploadSession = await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
- .Messages[draft.Id]
- .Attachments.CreateUploadSession(attachmentItem)
- .Request()
- .PostAsync();
+ return draftMessage;
+ }
- var fileUploadTask = new LargeFileUploadTask(uploadSession, attachment.Data);
+ private async Task UploadAttachmentUnder3Mb(
+ IFluentEmail email,
+ Message draft,
+ Attachment attachment,
+ CancellationToken cancellationToken)
+ {
+ var fileAttachment = new FileAttachment
+ {
+ Name = attachment.Filename,
+ ContentType = attachment.ContentType,
+ ContentBytes = GetAttachmentBytes(attachment.Data),
+ ContentId = attachment.ContentId,
+ IsInline = attachment.IsInline,
+
+ // can never be bigger than 3MB, so it is safe to cast to int
+ Size = (int)attachment.Data.Length,
+ };
+
+ await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
+ .Messages[draft.Id]
+ .Attachments.PostAsync(fileAttachment, cancellationToken: cancellationToken);
+ }
- await fileUploadTask.UploadAsync();
- }
+ private async Task UploadAttachment3MbOrOver(
+ IFluentEmail email,
+ Message draft,
+ Attachment attachment,
+ CancellationToken cancellationToken)
+ {
+ var attachmentItem = new AttachmentItem
+ {
+ AttachmentType = AttachmentType.File,
+ Name = attachment.Filename,
+ Size = attachment.Data.Length,
+ ContentType = attachment.ContentType,
+ ContentId = attachment.ContentId,
+ IsInline = attachment.IsInline,
+ };
+
+ var uploadSession = await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
+ .Messages[draft.Id]
+ .Attachments.CreateUploadSession.PostAsync(
+ new CreateUploadSessionPostRequestBody { AttachmentItem = attachmentItem },
+ requestConfiguration: null,
+ cancellationToken: cancellationToken);
+
+ var fileUploadTask = new LargeFileUploadTask(uploadSession, attachment.Data);
+
+ await fileUploadTask.UploadAsync(cancellationToken: cancellationToken);
}
}
diff --git a/src/FluentEmail.Graph/GraphSenderOptions.cs b/src/FluentEmail.Graph/GraphSenderOptions.cs
index c846e72..2fcf99b 100644
--- a/src/FluentEmail.Graph/GraphSenderOptions.cs
+++ b/src/FluentEmail.Graph/GraphSenderOptions.cs
@@ -1,23 +1,22 @@
-namespace FluentEmail.Graph
+namespace FluentEmail.Graph;
+
+///
+/// Contains settings needed for the .
+///
+public class GraphSenderOptions
{
///
- /// Contains settings needed for the .
+ /// Gets or sets the Client ID (also known as App ID) of the application as registered in the application registration portal.
///
- public class GraphSenderOptions
- {
- ///
- /// Gets or sets the Client ID (also known as App ID) of the application as registered in the application registration portal.
- ///
- public string ClientId { get; set; }
+ public string ClientId { get; set; }
- ///
- /// Gets or sets the Tenant Id of the organization from which the application will let users sign-in. This is classically a GUID or a domain name.
- ///
- public string TenantId { get; set; }
+ ///
+ /// Gets or sets the Tenant Id of the organization from which the application will let users sign-in. This is classically a GUID or a domain name.
+ ///
+ public string TenantId { get; set; }
- ///
- /// Gets or sets the secret string previously shared with AAD at application registration to prove the identity of the application (the client) requesting the tokens.
- ///
- public string Secret { get; set; }
- }
-}
+ ///
+ /// Gets or sets the secret string previously shared with AAD at application registration to prove the identity of the application (the client) requesting the tokens.
+ ///
+ public string Secret { get; set; }
+}
\ No newline at end of file
diff --git a/src/FluentEmail.Graph/MessageCreation.cs b/src/FluentEmail.Graph/MessageCreation.cs
index d4905ff..3a9ac85 100644
--- a/src/FluentEmail.Graph/MessageCreation.cs
+++ b/src/FluentEmail.Graph/MessageCreation.cs
@@ -6,7 +6,7 @@
using FluentEmail.Core;
using FluentEmail.Core.Models;
using JetBrains.Annotations;
-using Microsoft.Graph;
+using Microsoft.Graph.Models;
internal static class MessageCreation
{
@@ -34,7 +34,7 @@ internal static Message CreateGraphMessageFromFluentEmail(IFluentEmail email)
return message;
}
- private static IList CreateRecipientList(IEnumerable addressList)
+ private static List CreateRecipientList(IEnumerable addressList)
{
if (addressList == null)
{
@@ -54,7 +54,7 @@ private static Recipient ConvertToRecipient([NotNull] Address address)
return new Recipient
{
- EmailAddress = new EmailAddress { Address = address.EmailAddress, Name = address.Name, },
+ EmailAddress = new EmailAddress { Address = address.EmailAddress, Name = address.Name },
};
}
@@ -92,7 +92,7 @@ private static void AddHeaders(IFluentEmail email, Message message)
}
var headers = email.Data.Headers
- .Select(header => new InternetMessageHeader { Name = header.Key, Value = header.Value, })
+ .Select(header => new InternetMessageHeader { Name = header.Key, Value = header.Value })
.ToList();
message.InternetMessageHeaders = headers;
}
diff --git a/tests/FluentEmail.Graph.Tests/UnitTest1.cs b/tests/FluentEmail.Graph.Tests/UnitTest1.cs
index 82b0d99..50713cd 100644
--- a/tests/FluentEmail.Graph.Tests/UnitTest1.cs
+++ b/tests/FluentEmail.Graph.Tests/UnitTest1.cs
@@ -1,23 +1,22 @@
-namespace FluentEmail.Graph.Tests
-{
- using Microsoft.VisualStudio.TestTools.UnitTesting;
+namespace FluentEmail.Graph.Tests;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
- [TestClass]
- public class UnitTest1
+[TestClass]
+public class UnitTest1
+{
+ [TestMethod]
+ public void TestMethod1()
{
- [TestMethod]
- public void TestMethod1()
- {
- // no tests yet, bit hard because you need actual appid, tenantid and secret for integration test
- ////// arrange
- ////const int Expected = 4;
- ////var calculator = new Class1();
+ // no tests yet, bit hard because you need actual appid, tenantid and secret for integration test
+ ////// arrange
+ ////const int Expected = 4;
+ ////var calculator = new Class1();
- ////// act
- ////var actual = calculator.Add(2, 2);
+ ////// act
+ ////var actual = calculator.Add(2, 2);
- ////// assert
- ////actual.Should().Be(Expected);
- }
+ ////// assert
+ ////actual.Should().Be(Expected);
}
-}
+}
\ No newline at end of file