diff --git a/Wino.Core.Domain/Interfaces/IFolderService.cs b/Wino.Core.Domain/Interfaces/IFolderService.cs
index 9b58ce11..fab3fadc 100644
--- a/Wino.Core.Domain/Interfaces/IFolderService.cs
+++ b/Wino.Core.Domain/Interfaces/IFolderService.cs
@@ -49,15 +49,6 @@ namespace Wino.Core.Domain.Interfaces
/// Folders to update.
Task BulkUpdateFolderStructureAsync(Guid accountId, List allFolders);
- ///
- /// Updates Folder's delta synchronization identifier.
- /// Only used in Outlook since it does per-folder sync.
- ///
- /// Folder id
- /// New synchronization identifier.
- /// New identifier if success.
- Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string synchronizationIdentifier);
-
///
/// Deletes the folder for the given account by remote folder id.
///
@@ -87,6 +78,12 @@ namespace Wino.Core.Domain.Interfaces
/// True if Inbox exists, False if not.
Task IsInboxAvailableForAccountAsync(Guid accountId);
+ ///
+ /// Updates folder's LastSynchronizedDate to now.
+ ///
+ /// Folder to update.
+ Task UpdateFolderLastSyncDateAsync(Guid folderId);
+
Task TestAsync();
}
}
diff --git a/Wino.Core.Domain/Interfaces/IMailService.cs b/Wino.Core.Domain/Interfaces/IMailService.cs
index 30f7083f..6c1be462 100644
--- a/Wino.Core.Domain/Interfaces/IMailService.cs
+++ b/Wino.Core.Domain/Interfaces/IMailService.cs
@@ -53,7 +53,6 @@ namespace Wino.Core.Domain.Interfaces
///
///
///
- ///
Task MapLocalDraftAsync(string newMailCopyId, string newDraftId, string newThreadId);
Task CreateDraftMimeMessageAsync(Guid accountId, DraftCreationOptions options);
diff --git a/Wino.Core/CoreContainerSetup.cs b/Wino.Core/CoreContainerSetup.cs
index c2cd5839..f57b66ee 100644
--- a/Wino.Core/CoreContainerSetup.cs
+++ b/Wino.Core/CoreContainerSetup.cs
@@ -22,8 +22,10 @@ namespace Wino.Core
services.AddSingleton();
services.AddSingleton();
- services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
services.AddTransient();
+
services.AddTransient();
services.AddTransient();
services.AddTransient();
diff --git a/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs
index 0bbcb6d4..bc5e0595 100644
--- a/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs
+++ b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs
@@ -14,43 +14,31 @@ namespace Wino.Core.Integration.Processors
/// Database change processor that handles common operations for all synchronizers.
/// When a synchronizer detects a change, it should call the appropriate method in this class to reflect the change in the database.
/// Different synchronizers might need additional implementations.
- /// and
+ /// , and
/// None of the synchronizers can directly change anything in the database.
///
public interface IDefaultChangeProcessor
{
Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string deltaSynchronizationIdentifier);
- Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string deltaSynchronizationIdentifier);
-
Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId);
Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId);
-
Task ChangeMailReadStatusAsync(string mailCopyId, bool isRead);
Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged);
-
Task CreateMailAsync(Guid AccountId, NewMailItemPackage package);
Task DeleteMailAsync(Guid accountId, string mailId);
-
- Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId);
- Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId);
-
Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds);
-
Task SaveMimeFileAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId);
-
- // For gmail.
Task UpdateFolderStructureAsync(Guid accountId, List allFolders);
-
Task DeleteFolderAsync(Guid accountId, string remoteFolderId);
Task> GetSynchronizationFoldersAsync(SynchronizationOptions options);
Task InsertFolderAsync(MailItemFolder folder);
-
- Task> GetKnownUidsForFolderAsync(Guid folderId);
+ Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId);
+ Task UpdateFolderLastSyncDateAsync(Guid folderId);
}
public interface IGmailChangeProcessor : IDefaultChangeProcessor
{
-
+ Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId);
}
public interface IOutlookChangeProcessor : IDefaultChangeProcessor
@@ -62,6 +50,24 @@ namespace Wino.Core.Integration.Processors
/// MailCopyId of the message.
/// Whether the mime has b
Task IsMailExistsAsync(string messageId);
+
+ ///
+ /// Updates Folder's delta synchronization identifier.
+ /// Only used in Outlook since it does per-folder sync.
+ ///
+ /// Folder id
+ /// New synchronization identifier.
+ /// New identifier if success.
+ Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string deltaSynchronizationIdentifier);
+ }
+
+ public interface IImapChangeProcessor : IDefaultChangeProcessor
+ {
+ ///
+ /// Returns all known uids for the given folder.
+ ///
+ /// Folder id to retrieve uIds for.
+ Task> GetKnownUidsForFolderAsync(Guid folderId);
}
public class DefaultChangeProcessor(IDatabaseService databaseService,
@@ -72,7 +78,7 @@ namespace Wino.Core.Integration.Processors
{
protected IMailService MailService = mailService;
- private readonly IFolderService _folderService = folderService;
+ protected IFolderService FolderService = folderService;
private readonly IAccountService _accountService = accountService;
private readonly IMimeFileService _mimeFileService = mimeFileService;
@@ -99,33 +105,31 @@ namespace Wino.Core.Integration.Processors
// Folder methods
public Task UpdateFolderStructureAsync(Guid accountId, List allFolders)
- => _folderService.BulkUpdateFolderStructureAsync(accountId, allFolders);
+ => FolderService.BulkUpdateFolderStructureAsync(accountId, allFolders);
public Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId)
=> MailService.MapLocalDraftAsync(accountId, localDraftCopyUniqueId, newMailCopyId, newDraftId, newThreadId);
- public Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId)
- => MailService.MapLocalDraftAsync(mailCopyId, newDraftId, newThreadId);
+
public Task> GetSynchronizationFoldersAsync(SynchronizationOptions options)
- => _folderService.GetSynchronizationFoldersAsync(options);
-
- public Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string deltaSynchronizationIdentifier)
- => _folderService.UpdateFolderDeltaSynchronizationIdentifierAsync(folderId, deltaSynchronizationIdentifier);
+ => FolderService.GetSynchronizationFoldersAsync(options);
public Task DeleteFolderAsync(Guid accountId, string remoteFolderId)
- => _folderService.DeleteFolderAsync(accountId, remoteFolderId);
+ => FolderService.DeleteFolderAsync(accountId, remoteFolderId);
public Task InsertFolderAsync(MailItemFolder folder)
- => _folderService.InsertFolderAsync(folder);
+ => FolderService.InsertFolderAsync(folder);
public Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds)
=> MailService.GetDownloadedUnreadMailsAsync(accountId, downloadedMailCopyIds);
- public Task> GetKnownUidsForFolderAsync(Guid folderId)
- => _folderService.GetKnownUidsForFolderAsync(folderId);
+
public Task SaveMimeFileAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId)
=> _mimeFileService.SaveMimeMessageAsync(fileId, mimeMessage, accountId);
+
+ public Task UpdateFolderLastSyncDateAsync(Guid folderId)
+ => FolderService.UpdateFolderLastSyncDateAsync(folderId);
}
}
diff --git a/Wino.Core/Integration/Processors/GmailChangeProcessor.cs b/Wino.Core/Integration/Processors/GmailChangeProcessor.cs
new file mode 100644
index 00000000..72eb1dee
--- /dev/null
+++ b/Wino.Core/Integration/Processors/GmailChangeProcessor.cs
@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using Wino.Core.Domain.Interfaces;
+using Wino.Core.Services;
+
+namespace Wino.Core.Integration.Processors
+{
+ public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcessor
+ {
+ public GmailChangeProcessor(IDatabaseService databaseService, IFolderService folderService, IMailService mailService, IAccountService accountService, IMimeFileService mimeFileService) : base(databaseService, folderService, mailService, accountService, mimeFileService)
+ {
+ }
+
+ public Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId)
+ => MailService.MapLocalDraftAsync(mailCopyId, newDraftId, newThreadId);
+ }
+}
diff --git a/Wino.Core/Integration/Processors/ImapChangeProcessor.cs b/Wino.Core/Integration/Processors/ImapChangeProcessor.cs
new file mode 100644
index 00000000..f4bbdee2
--- /dev/null
+++ b/Wino.Core/Integration/Processors/ImapChangeProcessor.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Wino.Core.Domain.Interfaces;
+using Wino.Core.Services;
+
+namespace Wino.Core.Integration.Processors
+{
+ public class ImapChangeProcessor : DefaultChangeProcessor, IImapChangeProcessor
+ {
+ public ImapChangeProcessor(IDatabaseService databaseService,
+ IFolderService folderService,
+ IMailService mailService,
+ IAccountService accountService,
+ IMimeFileService mimeFileService) : base(databaseService, folderService, mailService, accountService, mimeFileService)
+ {
+ }
+
+ public Task> GetKnownUidsForFolderAsync(Guid folderId)
+ => FolderService.GetKnownUidsForFolderAsync(folderId);
+ }
+}
diff --git a/Wino.Core/Integration/Processors/OutlookChangeProcessor.cs b/Wino.Core/Integration/Processors/OutlookChangeProcessor.cs
index c36d3991..7c28f494 100644
--- a/Wino.Core/Integration/Processors/OutlookChangeProcessor.cs
+++ b/Wino.Core/Integration/Processors/OutlookChangeProcessor.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Services;
@@ -12,5 +13,8 @@ namespace Wino.Core.Integration.Processors
{
public Task IsMailExistsAsync(string messageId)
=> MailService.IsMailExistsAsync(messageId);
+
+ public Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string synchronizationIdentifier)
+ => Connection.ExecuteAsync("UPDATE MailItemFolder SET DeltaToken = ? WHERE Id = ?", synchronizationIdentifier, folderId);
}
}
diff --git a/Wino.Core/Services/FolderService.cs b/Wino.Core/Services/FolderService.cs
index 07449f1b..32e6a33a 100644
--- a/Wino.Core/Services/FolderService.cs
+++ b/Wino.Core/Services/FolderService.cs
@@ -513,23 +513,7 @@ namespace Wino.Core.Services
}
}
- public async Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string synchronizationIdentifier)
- {
- var folder = await GetFolderAsync(folderId).ConfigureAwait(false);
- if (folder == null)
- {
- _logger.Warning("Folder with id {FolderId} does not exist.", folderId);
-
- return string.Empty;
- }
-
- folder.DeltaToken = synchronizationIdentifier;
-
- await UpdateFolderAsync(folder).ConfigureAwait(false);
-
- return synchronizationIdentifier;
- }
public async Task DeleteFolderAsync(Guid accountId, string remoteFolderId)
{
@@ -588,5 +572,8 @@ namespace Wino.Core.Services
=> (await Connection.Table()
.Where(a => a.SpecialFolderType == SpecialFolderType.Inbox && a.MailAccountId == accountId)
.CountAsync()) == 1;
+
+ public Task UpdateFolderLastSyncDateAsync(Guid folderId)
+ => Connection.ExecuteAsync("UPDATE MailItemFolder SET LastSynchronizedDate = ? WHERE Id = ?", DateTime.UtcNow, folderId);
}
}
diff --git a/Wino.Core/Synchronizers/GmailSynchronizer.cs b/Wino.Core/Synchronizers/GmailSynchronizer.cs
index 917e3493..5abc5e00 100644
--- a/Wino.Core/Synchronizers/GmailSynchronizer.cs
+++ b/Wino.Core/Synchronizers/GmailSynchronizer.cs
@@ -41,12 +41,12 @@ namespace Wino.Core.Synchronizers
private readonly ConfigurableHttpClient _gmailHttpClient;
private readonly GmailService _gmailService;
private readonly IAuthenticator _authenticator;
- private readonly IDefaultChangeProcessor _gmailChangeProcessor;
+ private readonly IGmailChangeProcessor _gmailChangeProcessor;
private readonly ILogger _logger = Log.ForContext();
public GmailSynchronizer(MailAccount account,
IAuthenticator authenticator,
- IDefaultChangeProcessor gmailChangeProcessor) : base(account)
+ IGmailChangeProcessor gmailChangeProcessor) : base(account)
{
var messageHandler = new GmailClientMessageHandler(() => _authenticator.GetTokenAsync(Account));
diff --git a/Wino.Core/Synchronizers/ImapSynchronizer.cs b/Wino.Core/Synchronizers/ImapSynchronizer.cs
index 05227106..9c357970 100644
--- a/Wino.Core/Synchronizers/ImapSynchronizer.cs
+++ b/Wino.Core/Synchronizers/ImapSynchronizer.cs
@@ -33,7 +33,7 @@ namespace Wino.Core.Synchronizers
private readonly ILogger _logger = Log.ForContext();
private readonly ImapClientPool _clientPool;
- private readonly IDefaultChangeProcessor _imapChangeProcessor;
+ private readonly IImapChangeProcessor _imapChangeProcessor;
// Minimum summary items to Fetch for mail synchronization from IMAP.
private readonly MessageSummaryItems mailSynchronizationFlags =
@@ -61,7 +61,7 @@ namespace Wino.Core.Synchronizers
public override uint BatchModificationSize => 1000;
public override uint InitialMessageDownloadCountPerFolder => 500;
- public ImapSynchronizer(MailAccount account, IDefaultChangeProcessor imapChangeProcessor) : base(account)
+ public ImapSynchronizer(MailAccount account, IImapChangeProcessor imapChangeProcessor) : base(account)
{
_clientPool = new ImapClientPool(Account.ServerInformation);
@@ -830,10 +830,9 @@ namespace Wino.Core.Synchronizers
await _imapChangeProcessor.InsertFolderAsync(folder);
}
- // Update last synchronization identifier.
- // This will update last sync date for the folder.
+ // Update last synchronization date for the folder..
- await _imapChangeProcessor.UpdateFolderDeltaSynchronizationIdentifierAsync(folder.Id, string.Empty).ConfigureAwait(false);
+ await _imapChangeProcessor.UpdateFolderLastSyncDateAsync(folder.Id).ConfigureAwait(false);
return downloadedMessageIds;
}
diff --git a/Wino.Core/Synchronizers/OutlookSynchronizer.cs b/Wino.Core/Synchronizers/OutlookSynchronizer.cs
index 8947dbb7..073c84b0 100644
--- a/Wino.Core/Synchronizers/OutlookSynchronizer.cs
+++ b/Wino.Core/Synchronizers/OutlookSynchronizer.cs
@@ -93,7 +93,6 @@ namespace Wino.Core.Synchronizers
try
{
-
options.ProgressListener?.AccountProgressUpdated(Account.Id, 1);
await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false);
@@ -102,6 +101,9 @@ namespace Wino.Core.Synchronizers
{
var synchronizationFolders = await _outlookChangeProcessor.GetSynchronizationFoldersAsync(options).ConfigureAwait(false);
+ _logger.Information("Found {Count} folders to synchronize.", synchronizationFolders.Count);
+ _logger.Information(string.Format("Folders: {0}", string.Join(",", synchronizationFolders.Select(a => a.FolderName))));
+
for (int i = 0; i < synchronizationFolders.Count; i++)
{
var folder = synchronizationFolders[i];
@@ -149,6 +151,8 @@ namespace Wino.Core.Synchronizers
if (isInitialSync)
{
+ _logger.Debug("No sync identifier for Folder {FolderName}. Performing initial sync.", folder.FolderName);
+
// No delta link. Performing initial sync.
messageCollectionPage = await _graphClient.Me.MailFolders[folder.RemoteFolderId].Messages.Delta.GetAsDeltaGetResponseAsync((config) =>
@@ -162,6 +166,9 @@ namespace Wino.Core.Synchronizers
{
var currentDeltaToken = folder.DeltaToken;
+ _logger.Debug("Sync identifier found for Folder {FolderName}. Performing delta sync.", folder.FolderName);
+ _logger.Debug("Current delta token: {CurrentDeltaToken}", currentDeltaToken);
+
var requestInformation = _graphClient.Me.MailFolders[folder.RemoteFolderId].Messages.Delta.ToGetRequestInformation((config) =>
{
config.QueryParameters.Top = (int)InitialMessageDownloadCountPerFolder;
@@ -186,6 +193,14 @@ namespace Wino.Core.Synchronizers
latestDeltaLink = messageIteratorAsync.Deltalink;
+ if (downloadedMessageIds.Any())
+ {
+ _logger.Debug("Downloaded {Count} messages for folder {FolderName}", downloadedMessageIds.Count, folder.FolderName);
+ }
+
+ _logger.Debug("Iterator completed for folder {FolderName}", folder.FolderName);
+ _logger.Debug("Extracted latest delta link is {LatestDeltaLink}", latestDeltaLink);
+
//Store delta link for tracking new changes.
if (!string.IsNullOrEmpty(latestDeltaLink))
{
@@ -196,6 +211,8 @@ namespace Wino.Core.Synchronizers
await _outlookChangeProcessor.UpdateFolderDeltaSynchronizationIdentifierAsync(folder.Id, deltaToken).ConfigureAwait(false);
}
+ await _outlookChangeProcessor.UpdateFolderLastSyncDateAsync(folder.Id).ConfigureAwait(false);
+
return downloadedMessageIds;
}
diff --git a/Wino.Core/WinoSynchronizerFactory.cs b/Wino.Core/WinoSynchronizerFactory.cs
index 2ca63a47..d01b7c86 100644
--- a/Wino.Core/WinoSynchronizerFactory.cs
+++ b/Wino.Core/WinoSynchronizerFactory.cs
@@ -37,8 +37,9 @@ namespace Wino.Core
private readonly ISignatureService _signatureService;
private readonly IDatabaseService _databaseService;
private readonly IMimeFileService _mimeFileService;
- private readonly IDefaultChangeProcessor _defaultChangeProcessor;
private readonly IOutlookChangeProcessor _outlookChangeProcessor;
+ private readonly IGmailChangeProcessor _gmailChangeProcessor;
+ private readonly IImapChangeProcessor _imapChangeProcessor;
public WinoSynchronizerFactory(INativeAppService nativeAppService,
ITokenService tokenService,
@@ -49,8 +50,9 @@ namespace Wino.Core
ISignatureService signatureService,
IDatabaseService databaseService,
IMimeFileService mimeFileService,
- IDefaultChangeProcessor defaultChangeProcessor,
- IOutlookChangeProcessor outlookChangeProcessor)
+ IOutlookChangeProcessor outlookChangeProcessor,
+ IGmailChangeProcessor gmailChangeProcessor,
+ IImapChangeProcessor imapChangeProcessor)
{
_contactService = contactService;
_notificationBuilder = notificationBuilder;
@@ -61,8 +63,9 @@ namespace Wino.Core
_signatureService = signatureService;
_databaseService = databaseService;
_mimeFileService = mimeFileService;
- _defaultChangeProcessor = defaultChangeProcessor;
_outlookChangeProcessor = outlookChangeProcessor;
+ _gmailChangeProcessor = gmailChangeProcessor;
+ _imapChangeProcessor = imapChangeProcessor;
}
public IBaseSynchronizer GetAccountSynchronizer(Guid accountId)
@@ -80,13 +83,13 @@ namespace Wino.Core
case Domain.Enums.MailProviderType.Gmail:
var gmailAuthenticator = new GmailAuthenticator(_tokenService, _nativeAppService);
- return new GmailSynchronizer(mailAccount, gmailAuthenticator, _defaultChangeProcessor);
+ return new GmailSynchronizer(mailAccount, gmailAuthenticator, _gmailChangeProcessor);
case Domain.Enums.MailProviderType.Office365:
break;
case Domain.Enums.MailProviderType.Yahoo:
break;
case Domain.Enums.MailProviderType.IMAP4:
- return new ImapSynchronizer(mailAccount, _defaultChangeProcessor);
+ return new ImapSynchronizer(mailAccount, _imapChangeProcessor);
default:
break;
}