Problems moving a sent message to another folder

Richard Deeming 0 Reputation points
2024-05-03T13:29:41.5033333+00:00

I'm trying to update an application which was using IMAP and SMTP to use MS Graph instead, since the IMAP/SMTP access to Office365 is now slowing to a crawl.

The application needs to "file" sent messages into specific folders, rather than dumping them all in the "sent items" folder.

In the old version, this was simply a case of sending the message with SMTP, then connecting with IMAP, finding the target folder, and appending the message to it.

With MS Graph, things seem somewhat more complicated. The current process I'm using is:

  1. Create a draft message - either a new message, a forward, or a reply;
  2. Update the draft message if necessary;
  3. Send the draft message;
  4. Wait for the draft message to appear in the "Sent Items" folder;
  5. Move the draft message to the target folder;

This mostly works, using the "prefer immutable IDs" request option. But occasionally, we end up with a blank draft message in the target folder, and no sign of the actual sent message in the "sent items" folder.

Am I doing something wrong?

Some code, for reference:

// 1:
var draftMessage = await mailbox.Messages.PostAsync(newMessage, DefaultRequest, cancellationToken);

// 3:
await mailbox.Messages[draftMessage.Id].Send.PostAsync(DefaultRequest, cancellationToken);

// 4:
var sentItemsFolder = await mailbox.MailFolders[KnownFolders.SentItems].GetAsync(DefaultRequest, cancellationToken);

bool draftExists = false;
for (int attempt = 0; attempt < 5; attempt++)
{
    var sentItem = await mailbox.Messages[draftMessage.Id].GetAsync(DefaultRequest, cancellationToken);
    if (sentItem is not null)
    {
        if (string.Equals(sentItem.ParentFolderId, destinationFolder.Id, StringComparison.Ordinal))
        {
            // Sent item is already in the destination folder
            return;
        }
        if (string.Equals(sentItem.ParentFolderId, sentItemsFolder.Id, StringComparison.Ordinal))
        {
            draftExists = true;
            break;
        }
    }

    await Task.Delay(500, cancellationToken);
}

// 5:
if (draftExists)
{
    await mailbox.Messages[draftMessage.Id].Move.PostAsync(new() { DestinationId = destinationFolder.Id }, DefaultRequest, cancellationToken);
}

where DefaultRequest is:

private static readonly RetryHandlerOption RetryOption = new()
{
    ShouldRetry = (delay, attempt, message) => message.StatusCode is HttpStatusCode.Conflict
        or (HttpStatusCode)423  // Locked
        or HttpStatusCode.ServiceUnavailable
        or HttpStatusCode.GatewayTimeout
        or (HttpStatusCode)429, // TooManyRequests
};

private static void DefaultRequest<T>([NotNull] RequestConfiguration<T> request) where T : class, new()
{
    request.Options.Add(RetryOption);
    request.Headers.Add("Prefer", "IdType=\"ImmutableId\"");
}
Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
11,452 questions
{count} votes