Improved outlook online search even more and removed redundant methods from ChangeProcessor (#586)
This commit is contained in:
@@ -40,16 +40,6 @@ public interface IDefaultChangeProcessor
|
|||||||
Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId);
|
Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId);
|
||||||
Task UpdateFolderLastSyncDateAsync(Guid folderId);
|
Task UpdateFolderLastSyncDateAsync(Guid folderId);
|
||||||
Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases);
|
Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interrupted initial synchronization may cause downloaded mails to be saved in the database twice.
|
|
||||||
/// Since downloading mime is costly in Outlook, we need to check if the actual copy of the message has been saved before.
|
|
||||||
/// This is also used in online search to prevent duplicate mails.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="messageId">MailCopyId of the message.</param>
|
|
||||||
/// <returns>Whether mail exists or not.</returns>
|
|
||||||
Task<bool> IsMailExistsAsync(string messageId);
|
|
||||||
|
|
||||||
// Calendar
|
// Calendar
|
||||||
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
|
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
|
||||||
|
|
||||||
@@ -60,7 +50,6 @@ public interface IDefaultChangeProcessor
|
|||||||
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);
|
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);
|
||||||
|
|
||||||
Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken);
|
Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken);
|
||||||
Task<MailCopy> GetMailCopyAsync(string mailCopyId);
|
|
||||||
Task<List<MailCopy>> GetMailCopiesAsync(IEnumerable<string> mailCopyIds);
|
Task<List<MailCopy>> GetMailCopiesAsync(IEnumerable<string> mailCopyIds);
|
||||||
Task CreateMailRawAsync(MailAccount account, MailItemFolder mailItemFolder, NewMailItemPackage package);
|
Task CreateMailRawAsync(MailAccount account, MailItemFolder mailItemFolder, NewMailItemPackage package);
|
||||||
Task DeleteUserMailCacheAsync(Guid accountId);
|
Task DeleteUserMailCacheAsync(Guid accountId);
|
||||||
@@ -138,15 +127,9 @@ public class DefaultChangeProcessor(IDatabaseService databaseService,
|
|||||||
public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged)
|
public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged)
|
||||||
=> MailService.ChangeFlagStatusAsync(mailCopyId, isFlagged);
|
=> MailService.ChangeFlagStatusAsync(mailCopyId, isFlagged);
|
||||||
|
|
||||||
public Task<bool> IsMailExistsAsync(string messageId)
|
|
||||||
=> MailService.IsMailExistsAsync(messageId);
|
|
||||||
|
|
||||||
public Task<List<string>> AreMailsExistsAsync(IEnumerable<string> mailCopyIds)
|
public Task<List<string>> AreMailsExistsAsync(IEnumerable<string> mailCopyIds)
|
||||||
=> MailService.AreMailsExistsAsync(mailCopyIds);
|
=> MailService.AreMailsExistsAsync(mailCopyIds);
|
||||||
|
|
||||||
public Task<MailCopy> GetMailCopyAsync(string mailCopyId)
|
|
||||||
=> MailService.GetSingleMailItemAsync(mailCopyId);
|
|
||||||
|
|
||||||
public Task<List<MailCopy>> GetMailCopiesAsync(IEnumerable<string> mailCopyIds)
|
public Task<List<MailCopy>> GetMailCopiesAsync(IEnumerable<string> mailCopyIds)
|
||||||
=> MailService.GetMailItemsAsync(mailCopyIds);
|
=> MailService.GetMailItemsAsync(mailCopyIds);
|
||||||
|
|
||||||
|
|||||||
@@ -967,12 +967,10 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
|||||||
|
|
||||||
public override async Task<List<MailCopy>> OnlineSearchAsync(string queryText, List<IMailItemFolder> folders, CancellationToken cancellationToken = default)
|
public override async Task<List<MailCopy>> OnlineSearchAsync(string queryText, List<IMailItemFolder> folders, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
bool isFoldersIncluded = folders?.Any() ?? false;
|
List<Message> messagesReturnedByApi = [];
|
||||||
|
|
||||||
var messagesToDownload = new List<Message>();
|
|
||||||
|
|
||||||
// Perform search for each folder separately.
|
// Perform search for each folder separately.
|
||||||
if (isFoldersIncluded)
|
if (folders?.Count > 0)
|
||||||
{
|
{
|
||||||
var folderIds = folders.Select(a => a.RemoteFolderId);
|
var folderIds = folders.Select(a => a.RemoteFolderId);
|
||||||
|
|
||||||
@@ -990,9 +988,9 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
|||||||
|
|
||||||
if (result?.Value != null)
|
if (result?.Value != null)
|
||||||
{
|
{
|
||||||
lock (messagesToDownload)
|
lock (messagesReturnedByApi)
|
||||||
{
|
{
|
||||||
messagesToDownload.AddRange(result.Value);
|
messagesReturnedByApi.AddRange(result.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1008,59 +1006,60 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
|||||||
requestConfig.QueryParameters.Search = $"\"{queryText}\"";
|
requestConfig.QueryParameters.Search = $"\"{queryText}\"";
|
||||||
requestConfig.QueryParameters.Select = ["Id, ParentFolderId"];
|
requestConfig.QueryParameters.Select = ["Id, ParentFolderId"];
|
||||||
requestConfig.QueryParameters.Top = 1000;
|
requestConfig.QueryParameters.Top = 1000;
|
||||||
});
|
}, cancellationToken);
|
||||||
|
|
||||||
var result = await mailQuery;
|
var result = await mailQuery;
|
||||||
|
|
||||||
if (result?.Value != null)
|
if (result?.Value != null)
|
||||||
{
|
{
|
||||||
lock (messagesToDownload)
|
messagesReturnedByApi.AddRange(result.Value);
|
||||||
{
|
|
||||||
messagesToDownload.AddRange(result.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not download messages that exists, but return them for listing.
|
if (messagesReturnedByApi.Count == 0) return [];
|
||||||
|
|
||||||
var localFolders = await _outlookChangeProcessor.GetLocalFoldersAsync(Account.Id).ConfigureAwait(false);
|
var localFolders = (await _outlookChangeProcessor.GetLocalFoldersAsync(Account.Id).ConfigureAwait(false))
|
||||||
|
.ToDictionary(x => x.RemoteFolderId);
|
||||||
|
|
||||||
var existingMessageIds = new List<string>();
|
var messagesDictionary = messagesReturnedByApi.ToDictionary(a => a.Id);
|
||||||
|
|
||||||
//Download missing messages.
|
// Contains a list of message ids that potentially can be downloaded.
|
||||||
foreach (var message in messagesToDownload)
|
List<string> messageIdsWithKnownFolder = [];
|
||||||
|
|
||||||
|
// Validate that all messages are in a known folder.
|
||||||
|
foreach (var message in messagesReturnedByApi)
|
||||||
{
|
{
|
||||||
var messageId = message.Id;
|
if (!localFolders.ContainsKey(message.ParentFolderId))
|
||||||
var parentFolderId = message.ParentFolderId;
|
|
||||||
|
|
||||||
if (!localFolders.Any(a => a.RemoteFolderId == parentFolderId))
|
|
||||||
{
|
{
|
||||||
Log.Warning($"Search result returned a message from a folder that is not synchronized.");
|
Log.Warning("Search result returned a message from a folder that is not synchronized.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
existingMessageIds.Add(messageId);
|
messageIdsWithKnownFolder.Add(message.Id);
|
||||||
|
}
|
||||||
|
|
||||||
var exists = await _outlookChangeProcessor.IsMailExistsAsync(messageId).ConfigureAwait(false);
|
var locallyExistingMails = await _outlookChangeProcessor.AreMailsExistsAsync(messageIdsWithKnownFolder).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!exists)
|
// Find messages that are not downloaded yet.
|
||||||
{
|
List<Message> messagesToDownload = [];
|
||||||
// Check if folder exists. We can't download a mail without existing folder.
|
foreach (var id in messagesDictionary.Keys.Except(locallyExistingMails))
|
||||||
|
{
|
||||||
|
messagesToDownload.Add(messagesDictionary[id]);
|
||||||
|
}
|
||||||
|
|
||||||
var localFolder = localFolders.Find(a => a.RemoteFolderId == parentFolderId);
|
foreach (var message in messagesToDownload)
|
||||||
|
{
|
||||||
await DownloadSearchResultMessageAsync(messageId, localFolder, cancellationToken).ConfigureAwait(false);
|
await DownloadSearchResultMessageAsync(message.Id, localFolders[message.ParentFolderId], cancellationToken).ConfigureAwait(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get results from database and return.
|
// Get results from database and return.
|
||||||
return await _outlookChangeProcessor.GetMailCopiesAsync(existingMessageIds);
|
return await _outlookChangeProcessor.GetMailCopiesAsync(messageIdsWithKnownFolder).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MimeMessage> DownloadMimeMessageAsync(string messageId, CancellationToken cancellationToken = default)
|
private async Task<MimeMessage> DownloadMimeMessageAsync(string messageId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var mimeContentStream = await _graphClient.Me.Messages[messageId].Content.GetAsync(null, cancellationToken).ConfigureAwait(false);
|
var mimeContentStream = await _graphClient.Me.Messages[messageId].Content.GetAsync(null, cancellationToken).ConfigureAwait(false);
|
||||||
return await MimeMessage.LoadAsync(mimeContentStream).ConfigureAwait(false);
|
return await MimeMessage.LoadAsync(mimeContentStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<List<NewMailItemPackage>> CreateNewMailPackagesAsync(Message message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default)
|
public override async Task<List<NewMailItemPackage>> CreateNewMailPackagesAsync(Message message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default)
|
||||||
|
|||||||
Reference in New Issue
Block a user