Fixing send draft issue with OutlookSynchronizer.

This commit is contained in:
Burak Kaan Köse
2024-08-19 19:02:33 +02:00
parent 07bb90dda9
commit a8c39a1587

View File

@@ -4,7 +4,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -660,20 +660,11 @@ namespace Wino.Core.Synchronizers
var mailCopyId = sendDraftPreparationRequest.MailItem.Id; var mailCopyId = sendDraftPreparationRequest.MailItem.Id;
var mimeMessage = sendDraftPreparationRequest.Mime; var mimeMessage = sendDraftPreparationRequest.Mime;
var batchDeleteRequest = new BatchDeleteRequest(
[
new DeleteRequest(sendDraftPreparationRequest.MailItem)
]);
var deleteBundle = Delete(batchDeleteRequest).ElementAt(0);
// Convert mime message to Outlook message. // Convert mime message to Outlook message.
// Outlook synchronizer does not send MIME messages directly anymore. // Outlook synchronizer does not send MIME messages directly anymore.
// Alias support is lacking with direct MIMEs. // Alias support is lacking with direct MIMEs.
// Therefore we convert the MIME message to Outlook message and use proper APIs. // Therefore we convert the MIME message to Outlook message and use proper APIs.
// sendDraftPreparationRequest.MailItem.ThreadId
var outlookMessage = mimeMessage.AsOutlookMessage(false); var outlookMessage = mimeMessage.AsOutlookMessage(false);
// Update draft. // Update draft.
@@ -683,18 +674,24 @@ namespace Wino.Core.Synchronizers
// Send draft. // Send draft.
var sendDraftRequest = _graphClient.Me.Messages[mailCopyId].Send.ToPostRequestInformation(); // POST requests are handled differently in batches in Graph SDK.
// Batch basically ignores the step's coontent-type and body.
// Manually create a POST request with empty body and send it.
var sendDraftRequest = _graphClient.Me.Messages[mailCopyId].Send.ToPostRequestInformation((config) =>
{
config.Headers.Add("Content-Type", "application/json");
});
sendDraftRequest.Headers.Clear();
sendDraftRequest.Content = new MemoryStream(Encoding.UTF8.GetBytes("{}"));
sendDraftRequest.HttpMethod = Method.POST;
sendDraftRequest.Headers.Add("Content-Type", "application/json");
var sendDraftRequestBundle = new HttpRequestBundle<RequestInformation>(sendDraftRequest, request); var sendDraftRequestBundle = new HttpRequestBundle<RequestInformation>(sendDraftRequest, request);
//var sendMailPostRequest = _graphClient.Me.SendMail.ToPostRequestInformation(new SendMailPostRequestBody() return [patchDraftRequestBundle, sendDraftRequestBundle];
//{
// Message = outlookMessage
//});
//var sendMailRequest = new HttpRequestBundle<RequestInformation>(sendMailPostRequest, request);
return [sendDraftRequestBundle];
//return [deleteBundle, sendMailRequest];
} }
public override IEnumerable<IRequestBundle<RequestInformation>> Archive(BatchArchiveRequest request) public override IEnumerable<IRequestBundle<RequestInformation>> Archive(BatchArchiveRequest request)
@@ -756,7 +753,7 @@ namespace Wino.Core.Synchronizers
// Map BundleId to batch request step's key. // Map BundleId to batch request step's key.
// This is how we can identify which step succeeded or failed in the bundle. // This is how we can identify which step succeeded or failed in the bundle.
bundle.BundleId = batchRequestId;//batchContent.BatchRequestSteps.ElementAt(i).Key; bundle.BundleId = batchRequestId;
} }
if (!batchContent.BatchRequestSteps.Any()) if (!batchContent.BatchRequestSteps.Any())
@@ -778,14 +775,14 @@ namespace Wino.Core.Synchronizers
} }
// Execute batch. This will collect responses from network call for each batch step. // Execute batch. This will collect responses from network call for each batch step.
var batchRequestResponse = await _graphClient.Batch.PostAsync(batchContent).ConfigureAwait(false); var batchRequestResponse = await _graphClient.Batch.PostAsync(batchContent, cancellationToken).ConfigureAwait(false);
// Check responses for each bundle id. // Check responses for each bundle id.
// Each bundle id must return some HttpResponseMessage ideally. // Each bundle id must return some HttpResponseMessage ideally.
var bundleIds = batchContent.BatchRequestSteps.Select(a => a.Key); var bundleIds = batchContent.BatchRequestSteps.Select(a => a.Key);
// TODO: Handling responses. They used to work in v1 core, but not in v2. var exceptionBag = new List<string>();
foreach (var bundleId in bundleIds) foreach (var bundleId in bundleIds)
{ {
@@ -796,49 +793,31 @@ namespace Wino.Core.Synchronizers
var httpResponseMessage = await batchRequestResponse.GetResponseByIdAsync(bundleId); var httpResponseMessage = await batchRequestResponse.GetResponseByIdAsync(bundleId);
var codes = await batchRequestResponse.GetResponsesStatusCodesAsync(); if (httpResponseMessage == null)
continue;
using (httpResponseMessage) using (httpResponseMessage)
{ {
await ProcessSingleNativeRequestResponseAsync(bundle, httpResponseMessage, cancellationToken).ConfigureAwait(false); if (!httpResponseMessage.IsSuccessStatusCode)
{
var content = await httpResponseMessage.Content.ReadAsStringAsync();
var errorJson = JObject.Parse(content);
var errorString = $"({httpResponseMessage.StatusCode}) {errorJson["error"]["code"]} - {errorJson["error"]["message"]}";
exceptionBag.Add(errorString);
}
} }
} }
if (exceptionBag.Any())
{
var formattedErrorString = string.Join("\n", exceptionBag.Select((item, index) => $"{index + 1}. {item}"));
throw new SynchronizerException(formattedErrorString);
}
} }
} }
private async Task ProcessSingleNativeRequestResponseAsync(IRequestBundle<RequestInformation> bundle,
HttpResponseMessage httpResponseMessage,
CancellationToken cancellationToken = default)
{
if (httpResponseMessage == null) return;
if (!httpResponseMessage.IsSuccessStatusCode)
{
var content = await httpResponseMessage.Content.ReadAsStringAsync();
var errorJson = JObject.Parse(content);
throw new SynchronizerException($"({httpResponseMessage.StatusCode}) {errorJson["error"]["code"]} - {errorJson["error"]["message"]}");
}
else if (bundle is HttpRequestBundle<RequestInformation, Message> messageBundle)
{
var outlookMessage = await messageBundle.DeserializeBundleAsync(httpResponseMessage, cancellationToken);
if (outlookMessage == null) return;
// TODO: Handle new message added or updated.
}
else if (bundle is HttpRequestBundle<RequestInformation, Microsoft.Graph.Models.MailFolder> folderBundle)
{
var outlookFolder = await folderBundle.DeserializeBundleAsync(httpResponseMessage, cancellationToken);
if (outlookFolder == null) return;
// TODO: Handle new folder added or updated.
}
else if (bundle is HttpRequestBundle<RequestInformation, MimeMessage> mimeBundle)
{
// TODO: Handle mime retrieve message.
}
}
private async Task<MimeMessage> DownloadMimeMessageAsync(string messageId, CancellationToken cancellationToken = default) private async Task<MimeMessage> DownloadMimeMessageAsync(string messageId, CancellationToken cancellationToken = default)
{ {