diff --git a/Frends.Community.Email.Tests/SendExchangeEmailTests.cs b/Frends.Community.Email.Tests/SendExchangeEmailTests.cs index 6e21b7a..0354aec 100644 --- a/Frends.Community.Email.Tests/SendExchangeEmailTests.cs +++ b/Frends.Community.Email.Tests/SendExchangeEmailTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using File = System.IO.File; @@ -18,6 +19,7 @@ public class SendExchangeEmailTests private readonly string _password = Environment.GetEnvironmentVariable("Exchange_User_Password"); private readonly string _applicationID = Environment.GetEnvironmentVariable("Exchange_Application_ID"); private readonly string _tenantID = Environment.GetEnvironmentVariable("Exchange_Tenant_ID"); + private readonly string _attachmentDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "testAttachments"); private static ExchangeServer _server; [OneTimeSetUp] @@ -37,6 +39,23 @@ public void OneTimeSetup() AppId = _applicationID, TenantId = _tenantID }; + + if (System.IO.Directory.Exists(_attachmentDirectory)) + { + DeleteAttachmentDirectory(); + } + } + + [SetUp] + public void CreateAttachmentDirectory() + { + System.IO.Directory.CreateDirectory(_attachmentDirectory); + } + + [TearDown] + public void DeleteAttachmentDirectory() + { + System.IO.Directory.Delete(_attachmentDirectory, true); } [Test] @@ -54,8 +73,8 @@ public async Task SendEmailWithPlainTextTest() var result = await EmailTask.SendEmailToExchangeServer(input, null, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.AreEqual(email[0].BodyText, message); await DeleteMessages(subject); } @@ -74,8 +93,8 @@ public async Task SendEmailToMultipleUsingSemicolonTest() var result = await EmailTask.SendEmailToExchangeServer(input, null, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.AreEqual(_username + ", " + _username, email[0].To); await DeleteMessages(subject); } @@ -94,8 +113,8 @@ public async Task SendEmailToMultipleUsingCommaTest() var result = await EmailTask.SendEmailToExchangeServer(input, null, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.AreEqual(_username + ", " + _username, email[0].To); await DeleteMessages(subject); } @@ -115,8 +134,8 @@ public async Task SendEmailWithCCTest() var result = await EmailTask.SendEmailToExchangeServer(input, null, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.AreEqual(_username, email[0].Cc); await DeleteMessages(subject); } @@ -137,8 +156,8 @@ public async Task SendEmailWithHtmlTest() var result = await EmailTask.SendEmailToExchangeServer(input, null, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.IsTrue(email[0].BodyHtml.Contains(message)); await DeleteMessages(subject); } @@ -157,8 +176,8 @@ public async Task SendEmailNordicLettersTest() var result = await EmailTask.SendEmailToExchangeServer(input, null, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails with correct subject found in inbox."); Assert.AreEqual("Tämä testimaili tuo yöllä ålannista.", email[0].BodyText); Assert.AreEqual(subject, email[0].Subject); await DeleteMessages(subject); @@ -168,7 +187,7 @@ public async Task SendEmailNordicLettersTest() public async Task SendEmailWithFileAttachmentTest() { var subject = "Email test - FileAttachment"; - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../attachmentFile.txt"); + var filePath = Path.Combine(_attachmentDirectory, "attachmentFile.txt"); File.WriteAllText(filePath, "This is a test attachment file."); var input = new ExchangeInput { @@ -191,8 +210,8 @@ public async Task SendEmailWithFileAttachmentTest() var result = await EmailTask.SendEmailToExchangeServer(input, attachmentArray, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); File.Delete(filePath); - Thread.Sleep(2000); // Give the email some time to get through. - await ReadTestEmailWithAttachment(subject); + var email = await ReadTestEmailWithAttachment(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.IsTrue(File.Exists(filePath)); File.Delete(filePath); await DeleteMessages(subject); @@ -202,9 +221,9 @@ public async Task SendEmailWithFileAttachmentTest() public async Task SendEmailWithMultipleFileAttachmentTest() { var subject = "Email test - MultiFileAttachment"; - var filePath1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../first.txt"); + var filePath1 = Path.Combine(_attachmentDirectory, "first.txt"); File.WriteAllText(filePath1, "This is a test attachment file."); - var filePath2 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../second.txt"); + var filePath2 = Path.Combine(_attachmentDirectory, "second.txt"); File.WriteAllText(filePath2, "This is a test attachment file."); var input = new ExchangeInput { @@ -217,7 +236,7 @@ public async Task SendEmailWithMultipleFileAttachmentTest() var attachment = new Attachment { AttachmentType = AttachmentType.FileAttachment, - FilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../*.txt"), + FilePath = Path.Combine(_attachmentDirectory, "*.txt"), ThrowExceptionIfAttachmentNotFound = false, SendIfNoAttachmentsFound = false }; @@ -228,7 +247,8 @@ public async Task SendEmailWithMultipleFileAttachmentTest() Assert.IsTrue(result.EmailSent); File.Delete(filePath1); File.Delete(filePath2); - Thread.Sleep(2000); // Give the email some time to get through. + var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); await DeleteMessages(subject); } @@ -236,7 +256,7 @@ public async Task SendEmailWithMultipleFileAttachmentTest() public async Task SendEmailWithBigFileAttachmentTest() { var subject = "Email test - BigFileAttachment"; - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../BigAttachmentFile.txt"); + var filePath = Path.Combine(_attachmentDirectory, "BigAttachmentFile.txt"); // Write 9MB file. var stream = new FileStream(filePath, FileMode.CreateNew); @@ -265,8 +285,8 @@ public async Task SendEmailWithBigFileAttachmentTest() var result = await EmailTask.SendEmailToExchangeServer(input, attachmentArray, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); File.Delete(filePath); - Thread.Sleep(2000); // Give the email some time to get through. - await ReadTestEmailWithAttachment(subject); + var email = await ReadTestEmailWithAttachment(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.IsTrue(File.Exists(filePath)); File.Delete(filePath); await DeleteMessages(subject); @@ -275,7 +295,7 @@ public async Task SendEmailWithBigFileAttachmentTest() [Test] public async Task SendEmailWithStringAttachmentTest() { - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../stringAttachmentFile.txt"); + var filePath = Path.Combine(_attachmentDirectory, "stringAttachmentFile.txt"); var subject = "Email test - StringAttachment"; var input = new ExchangeInput { @@ -301,13 +321,65 @@ public async Task SendEmailWithStringAttachmentTest() var result = await EmailTask.SendEmailToExchangeServer(input, attachmentArray, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. - await ReadTestEmailWithAttachment(subject); + var email = await ReadTestEmailWithAttachment(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.IsTrue(File.Exists(filePath)); File.Delete(filePath); await DeleteMessages(subject); } + [Test] + public async Task SendEmailWithTwoStringAttachmentsTest() + { + var attachments = new Attachment[] + { + new Attachment + { + AttachmentType = AttachmentType.AttachmentFromString, + StringAttachment = new AttachmentFromString + { + FileContent = "Test content 1.", + FileName = "stringAttachmentFile1.txt" + } + }, + new Attachment + { + AttachmentType = AttachmentType.AttachmentFromString, + StringAttachment = new AttachmentFromString + { + FileContent = "Test file content 2.", + FileName = "stringAttachmentFile2.txt" + } + } + }; + var subject = "Email test - StringAttachment"; + + var result = await EmailTask.SendEmailToExchangeServer( + new ExchangeInput + { + To = _username, + Message = "This email has two attachments written from a string.", + IsMessageHtml = false, + Subject = subject + }, + attachments, + _server, + new CancellationToken() + ); + Assert.IsTrue(result.EmailSent); + var email = await ReadTestEmailWithAttachment(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); + foreach (var attachment in attachments) + { + var fileName = attachment.StringAttachment.FileName; + var filePath = Path.Combine(_attachmentDirectory, fileName); + Assert.IsTrue(File.Exists(filePath), $"File {fileName} not found in attachments."); + Assert.AreEqual(attachment.StringAttachment.FileContent, File.ReadAllText(filePath), $"Attachment {fileName} contents do not match with what was sent."); + File.Delete(filePath); + } + await DeleteMessages(subject); + } + [Test] public async Task SendEmailWithEmptyAttachmentTest() { @@ -325,8 +397,8 @@ public async Task SendEmailWithEmptyAttachmentTest() var result = await EmailTask.SendEmailToExchangeServer(input, attachment.ToArray(), _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.AreEqual(email[0].BodyText, message); await DeleteMessages(subject); } @@ -347,8 +419,8 @@ public async Task SendEmailAsAnotherUserTest() var result = await EmailTask.SendEmailToExchangeServer(input, null, _server, new CancellationToken()); Assert.IsTrue(result.EmailSent); - Thread.Sleep(2000); // Give the email some time to get through. var email = await ReadTestEmail(subject); + Assert.IsTrue(email.Count > 0, "No emails matching subject in inbox."); Assert.AreEqual("frends_exchange_test_user_2@frends.com", email[0].From); await DeleteMessages(subject); } @@ -389,13 +461,20 @@ private async Task> ReadTestEmail(string subject) EmailSubjectFilter = subject }; - var result = await ReadEmailTask.ReadEmailFromExchangeServer(settings, options, new CancellationToken()); - return result; + for (var attempt = 0; attempt < 5; ++attempt) + { + var result = await ReadEmailTask.ReadEmailFromExchangeServer(settings, options, new CancellationToken()); + if (result.Any()) + { + return result; + } + await Task.Delay(2000); // Give the email some time to get through. + } + return new List(); } private async Task> ReadTestEmailWithAttachment(string subject) { - var dirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../.."); var settings = new ExchangeSettings { TenantId = _tenantID, @@ -411,12 +490,21 @@ private async Task> ReadTestEmailWithAttachment(string GetOnlyUnreadEmails = false, MarkEmailsAsRead = false, IgnoreAttachments = false, - AttachmentSaveDirectory = dirPath, - EmailSubjectFilter = subject + AttachmentSaveDirectory = _attachmentDirectory, + EmailSubjectFilter = subject, + FileExistsAction = FileExists.Error }; - var result = await ReadEmailTask.ReadEmailFromExchangeServer(settings, options, new CancellationToken()); - return result; + for (var attempt = 0; attempt < 5; ++attempt) + { + var result = await ReadEmailTask.ReadEmailFromExchangeServer(settings, options, new CancellationToken()); + if (result.Any()) + { + return result; + } + await Task.Delay(2000); // Give the email some time to get through. + } + return new List(); } #endregion diff --git a/Frends.Community.Email/EmailTask.cs b/Frends.Community.Email/EmailTask.cs index 0ea548c..fb88661 100644 --- a/Frends.Community.Email/EmailTask.cs +++ b/Frends.Community.Email/EmailTask.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Azure.Identity; using Microsoft.Graph; -using System.Text; using File = System.IO.File; using Directory = System.IO.Directory; using System.Linq; @@ -210,9 +209,6 @@ public static async Task SendEmailToExchangeServer([PropertyTab] Exchang private static async Task SendExchangeEmailWithAttachments(Attachment[] attachments, GraphServiceClient graphClient, string subject, ItemBody messageBody, List to, List cc, List bcc, CancellationToken cancellationToken) { - var attachmentList = new MessageAttachmentsCollectionPage(); - var allAttachmentFilePaths = new List(); - var message = new Message { Subject = subject, @@ -223,64 +219,67 @@ private static async Task SendExchangeEmailWithAttachments(Attachment[] }; var msgResult = await graphClient.Me.Messages.Request().AddAsync(message, cancellationToken); - + var graphAttachments = graphClient.Me.Messages[msgResult.Id].Attachments; foreach (var attachment in attachments) { - - string tempFilePath = ""; if (attachment.AttachmentType == AttachmentType.AttachmentFromString) { // Create attachment only if content is not empty. if (!string.IsNullOrEmpty(attachment.StringAttachment.FileContent)) { - tempFilePath = CreateTemporaryFile(attachment); - var attachmentContent = File.ReadAllBytes(tempFilePath); - var oneAttachment = new FileAttachment + using (var stream = new MemoryStream()) { - ODataType = "#microsoft.graph.fileAttachment", - ContentBytes = attachmentContent, - ContentType = MimeTypes.GetMimeType(tempFilePath), - Name = Path.GetFileName(tempFilePath) - }; - allAttachmentFilePaths.Add(tempFilePath); + using (var writer = new StreamWriter(stream)) + { + writer.Write(attachment.StringAttachment.FileContent); + writer.Flush(); + stream.Position = 0; + var attachmentItem = new AttachmentItem + { + AttachmentType = Microsoft.Graph.AttachmentType.File, + Name = attachment.StringAttachment.FileName, + Size = stream.Length + }; + var uploadSession = await graphAttachments.CreateUploadSession(attachmentItem).Request().PostAsync(cancellationToken); + var largeFileUploadTask = new LargeFileUploadTask(uploadSession, stream); + await largeFileUploadTask.UploadAsync(); + } + } } } else - allAttachmentFilePaths = GetAttachmentFiles(attachment.FilePath); - - if (attachment.ThrowExceptionIfAttachmentNotFound && allAttachmentFilePaths.Count == 0) throw new FileNotFoundException($"The given filepath \"{attachment.FilePath}\" had no matching files"); - - if (allAttachmentFilePaths.Count == 0 && !attachment.SendIfNoAttachmentsFound) { - return new Output + var filePaths = GetAttachmentFiles(attachment.FilePath); + if (attachment.ThrowExceptionIfAttachmentNotFound && filePaths.Count == 0) { - EmailSent = false, - StatusString = $"No attachments found matching path \"{attachmentList[0].Name}\". No email sent." - }; - } - - foreach (var filePath in allAttachmentFilePaths) - { - cancellationToken.ThrowIfCancellationRequested(); - var info = new FileInfo(filePath); - var attachmentItem = new AttachmentItem + throw new FileNotFoundException($"The given filepath \"{attachment.FilePath}\" had no matching files"); + } + if (filePaths.Count == 0 && !attachment.SendIfNoAttachmentsFound) { - AttachmentType = Microsoft.Graph.AttachmentType.File, - Name = Path.GetFileName(filePath), - Size = info.Length - }; - - var uploadSession = await graphClient.Me.Messages[msgResult.Id].Attachments.CreateUploadSession(attachmentItem).Request().PostAsync(cancellationToken); - var allBytes = File.ReadAllBytes(filePath); - - using (var stream = new MemoryStream(allBytes)) + return new Output + { + EmailSent = false, + StatusString = $"No attachments found matching path \"{attachment.FilePath}\". No email sent." + }; + } + foreach (var filePath in filePaths) { - stream.Position = 0; - var largeFileUploadTask = new LargeFileUploadTask(uploadSession, stream); - await largeFileUploadTask.UploadAsync(); + var info = new FileInfo(filePath); + var attachmentItem = new AttachmentItem + { + AttachmentType = Microsoft.Graph.AttachmentType.File, + Name = Path.GetFileName(filePath), + Size = info.Length + }; + var uploadSession = await graphAttachments.CreateUploadSession(attachmentItem).Request().PostAsync(cancellationToken); + var allBytes = File.ReadAllBytes(filePath); + using (var stream = new MemoryStream(allBytes)) + { + stream.Position = 0; + var largeFileUploadTask = new LargeFileUploadTask(uploadSession, stream); + await largeFileUploadTask.UploadAsync(); + } } - - if (attachment.AttachmentType == AttachmentType.AttachmentFromString) CleanUpTempWorkDir(tempFilePath); } } diff --git a/Frends.Community.Email/Frends.Community.Email.csproj b/Frends.Community.Email/Frends.Community.Email.csproj index 2099087..a0e9954 100644 --- a/Frends.Community.Email/Frends.Community.Email.csproj +++ b/Frends.Community.Email/Frends.Community.Email.csproj @@ -10,7 +10,7 @@ true Frends true - 4.2.1 + 4.2.2 false diff --git a/README.md b/README.md index 1ba3160..dd9cc58 100644 --- a/README.md +++ b/README.md @@ -321,3 +321,4 @@ NOTE: Be sure to merge the latest from "upstream" before making a pull request! | 4.1.3 | ReadEmailFromExchangeServer: Ignore ItemAttachments to prevent failing of the Task if attachment is ItemAttachment instead of FileAttachment. | | 4.2.0 | SendEmail: Added feature to add custom headers to email sending. | | 4.2.1 | SendEmailToExchangeServer: Fixed issue which prevented adding multiple attachment to email. | +| 4.2.2 | SendEmailToExchangeServer: Fixed issue which duplicated string attachments. |