Reworked aliases.
This commit is contained in:
@@ -6,7 +6,6 @@ using Google.Apis.Gmail.v1.Data;
|
||||
using MimeKit;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Extensions;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
@@ -205,22 +204,16 @@ namespace Wino.Core.Extensions
|
||||
};
|
||||
}
|
||||
|
||||
public static List<MailAccountAlias> GetMailAliases(this ListSendAsResponse response, List<MailAccountAlias> currentAliases, MailAccount account)
|
||||
public static List<RemoteAccountAlias> GetRemoteAliases(this ListSendAsResponse response)
|
||||
{
|
||||
if (response == null || response.SendAs == null) return currentAliases;
|
||||
|
||||
var remoteAliases = response.SendAs.Select(a => new MailAccountAlias()
|
||||
return response?.SendAs?.Select(a => new RemoteAccountAlias()
|
||||
{
|
||||
AccountId = account.Id,
|
||||
AliasAddress = a.SendAsEmail,
|
||||
IsRootAlias = a.IsDefault.GetValueOrDefault(),
|
||||
IsPrimary = a.IsPrimary.GetValueOrDefault(),
|
||||
ReplyToAddress = string.IsNullOrEmpty(a.ReplyToAddress) ? account.Address : a.ReplyToAddress,
|
||||
IsVerified = string.IsNullOrEmpty(a.VerificationStatus) ? true : a.VerificationStatus == "accepted",
|
||||
IsRootAlias = account.Address == a.SendAsEmail,
|
||||
Id = Guid.NewGuid()
|
||||
ReplyToAddress = a.ReplyToAddress,
|
||||
IsVerified = a.VerificationStatus == "accepted" || a.IsDefault.GetValueOrDefault(),
|
||||
}).ToList();
|
||||
|
||||
return EntityExtensions.GetFinalAliasList(currentAliases, remoteAliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace Wino.Core.Integration.Processors
|
||||
/// <returns>All folders.</returns>
|
||||
Task<List<MailItemFolder>> GetLocalFoldersAsync(Guid accountId);
|
||||
|
||||
Task<List<MailAccountAlias>> GetAccountAliasesAsync(Guid accountId);
|
||||
|
||||
Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(SynchronizationOptions options);
|
||||
|
||||
@@ -48,7 +47,7 @@ namespace Wino.Core.Integration.Processors
|
||||
Task UpdateFolderLastSyncDateAsync(Guid folderId);
|
||||
|
||||
Task<List<MailItemFolder>> GetExistingFoldersAsync(Guid accountId);
|
||||
Task UpdateAccountAliasesAsync(Guid accountId, List<MailAccountAlias> aliases);
|
||||
Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases);
|
||||
}
|
||||
|
||||
public interface IGmailChangeProcessor : IDefaultChangeProcessor
|
||||
@@ -180,10 +179,7 @@ namespace Wino.Core.Integration.Processors
|
||||
public Task UpdateAccountAsync(MailAccount account)
|
||||
=> AccountService.UpdateAccountAsync(account);
|
||||
|
||||
public Task UpdateAccountAliasesAsync(Guid accountId, List<MailAccountAlias> aliases)
|
||||
=> AccountService.UpdateAccountAliasesAsync(accountId, aliases);
|
||||
|
||||
public Task<List<MailAccountAlias>> GetAccountAliasesAsync(Guid accountId)
|
||||
=> AccountService.GetAccountAliasesAsync(accountId);
|
||||
public Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases)
|
||||
=> AccountService.UpdateRemoteAliasInformationAsync(account, remoteAccountAliases);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,6 +380,69 @@ namespace Wino.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases)
|
||||
{
|
||||
var localAliases = await GetAccountAliasesAsync(account.Id).ConfigureAwait(false);
|
||||
var rootAlias = localAliases.Find(a => a.IsRootAlias);
|
||||
|
||||
foreach (var remoteAlias in remoteAccountAliases)
|
||||
{
|
||||
var existingAlias = localAliases.Find(a => a.AccountId == account.Id && a.AliasAddress == remoteAlias.AliasAddress);
|
||||
|
||||
if (existingAlias == null)
|
||||
{
|
||||
// Create new alias.
|
||||
var newAlias = new MailAccountAlias()
|
||||
{
|
||||
AccountId = account.Id,
|
||||
AliasAddress = remoteAlias.AliasAddress,
|
||||
IsPrimary = remoteAlias.IsPrimary,
|
||||
IsVerified = remoteAlias.IsVerified,
|
||||
ReplyToAddress = remoteAlias.ReplyToAddress,
|
||||
Id = Guid.NewGuid(),
|
||||
IsRootAlias = remoteAlias.IsRootAlias
|
||||
};
|
||||
|
||||
await Connection.InsertAsync(newAlias);
|
||||
localAliases.Add(newAlias);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update existing alias.
|
||||
existingAlias.IsPrimary = remoteAlias.IsPrimary;
|
||||
existingAlias.IsVerified = remoteAlias.IsVerified;
|
||||
existingAlias.ReplyToAddress = remoteAlias.ReplyToAddress;
|
||||
|
||||
await Connection.UpdateAsync(existingAlias);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure there is only 1 root alias and 1 primary alias selected.
|
||||
|
||||
bool shouldUpdatePrimary = localAliases.Count(a => a.IsPrimary) != 1;
|
||||
bool shouldUpdateRoot = localAliases.Count(a => a.IsRootAlias) != 1;
|
||||
|
||||
if (shouldUpdatePrimary)
|
||||
{
|
||||
localAliases.ForEach(a => a.IsPrimary = false);
|
||||
|
||||
var idealPrimaryAlias = localAliases.Find(a => a.AliasAddress == account.Address) ?? localAliases.First();
|
||||
|
||||
idealPrimaryAlias.IsPrimary = true;
|
||||
await Connection.UpdateAsync(idealPrimaryAlias).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (shouldUpdateRoot)
|
||||
{
|
||||
localAliases.ForEach(a => a.IsRootAlias = false);
|
||||
|
||||
var idealRootAlias = localAliases.Find(a => a.AliasAddress == account.Address) ?? localAliases.First();
|
||||
|
||||
idealRootAlias.IsRootAlias = true;
|
||||
await Connection.UpdateAsync(idealRootAlias).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAccountAliasAsync(Guid aliasId)
|
||||
{
|
||||
// Create query to delete alias.
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Wino.Core.Synchronizers
|
||||
/// Refreshes remote mail account profile if possible.
|
||||
/// Profile picture, sender name and mailbox settings (todo) will be handled in this step.
|
||||
/// </summary>
|
||||
public virtual Task<ProfileInformation> SynchronizeProfileInformationAsync() => default;
|
||||
public virtual Task<ProfileInformation> GetProfileInformationAsync() => default;
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the aliases of the account.
|
||||
@@ -110,28 +110,18 @@ namespace Wino.Core.Synchronizers
|
||||
/// <summary>
|
||||
/// Safely updates account's profile information.
|
||||
/// Database changes are reflected after this call.
|
||||
/// Null returns mean that the operation failed.
|
||||
/// </summary>
|
||||
private async Task<ProfileInformation> SynchronizeProfileInformationInternalAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var profileInformation = await SynchronizeProfileInformationAsync();
|
||||
var profileInformation = await GetProfileInformationAsync();
|
||||
|
||||
if (profileInformation != null)
|
||||
{
|
||||
Account.SenderName = profileInformation.SenderName;
|
||||
Account.Base64ProfilePictureData = profileInformation.Base64ProfilePictureData;
|
||||
}
|
||||
|
||||
return profileInformation;
|
||||
}
|
||||
catch (Exception ex)
|
||||
if (profileInformation != null)
|
||||
{
|
||||
Log.Error(ex, "Failed to update profile information for account '{Name}'", Account.Name);
|
||||
Account.SenderName = profileInformation.SenderName;
|
||||
Account.Base64ProfilePictureData = profileInformation.Base64ProfilePictureData;
|
||||
}
|
||||
|
||||
return null;
|
||||
return profileInformation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -173,16 +163,46 @@ namespace Wino.Core.Synchronizers
|
||||
|
||||
await synchronizationSemaphore.WaitAsync(activeSynchronizationCancellationToken);
|
||||
|
||||
// Handle special synchronization types.
|
||||
|
||||
// Profile information sync.
|
||||
if (options.Type == SynchronizationType.UpdateProfile)
|
||||
{
|
||||
// Refresh profile information on full synchronization.
|
||||
// Exceptions here is not critical. Therefore, they are ignored.
|
||||
if (!Account.IsProfileInfoSyncSupported) return SynchronizationResult.Empty;
|
||||
|
||||
var newprofileInformation = await SynchronizeProfileInformationInternalAsync();
|
||||
ProfileInformation newProfileInformation = null;
|
||||
|
||||
if (newprofileInformation == null) return SynchronizationResult.Failed;
|
||||
try
|
||||
{
|
||||
newProfileInformation = await SynchronizeProfileInformationInternalAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to update profile information for {Name}", Account.Name);
|
||||
|
||||
return SynchronizationResult.Completed(null, newprofileInformation);
|
||||
return SynchronizationResult.Failed;
|
||||
}
|
||||
|
||||
return SynchronizationResult.Completed(null, newProfileInformation);
|
||||
}
|
||||
|
||||
// Alias sync.
|
||||
if (options.Type == SynchronizationType.Alias)
|
||||
{
|
||||
if (!Account.IsAliasSyncSupported) return SynchronizationResult.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
await SynchronizeAliasesAsync();
|
||||
|
||||
return SynchronizationResult.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to update aliases for {Name}", Account.Name);
|
||||
|
||||
return SynchronizationResult.Failed;
|
||||
}
|
||||
}
|
||||
|
||||
// Let servers to finish their job. Sometimes the servers doesn't respond immediately.
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Wino.Core.Synchronizers
|
||||
|
||||
public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args) => _googleHttpClient;
|
||||
|
||||
public override async Task<ProfileInformation> SynchronizeProfileInformationAsync()
|
||||
public override async Task<ProfileInformation> GetProfileInformationAsync()
|
||||
{
|
||||
var profileRequest = _peopleService.People.Get("people/me");
|
||||
profileRequest.PersonFields = "names,photos";
|
||||
@@ -92,23 +92,11 @@ namespace Wino.Core.Synchronizers
|
||||
|
||||
protected override async Task SynchronizeAliasesAsync()
|
||||
{
|
||||
// Sync aliases
|
||||
|
||||
var sendAsListRequest = _gmailService.Users.Settings.SendAs.List("me");
|
||||
var sendAsListResponse = await sendAsListRequest.ExecuteAsync();
|
||||
var remoteAliases = sendAsListResponse.GetRemoteAliases();
|
||||
|
||||
var localAliases = await _gmailChangeProcessor.GetAccountAliasesAsync(Account.Id).ConfigureAwait(false);
|
||||
|
||||
var updatedAliases = sendAsListResponse.GetMailAliases(localAliases, Account);
|
||||
|
||||
bool shouldUpdateAliases =
|
||||
localAliases.Any(a => updatedAliases.Any(b => a.Id == b.Id) == false) ||
|
||||
updatedAliases.Any(a => localAliases.Any(b => a.Id == b.Id) == false);
|
||||
|
||||
if (shouldUpdateAliases)
|
||||
{
|
||||
await _gmailChangeProcessor.UpdateAccountAliasesAsync(Account.Id, updatedAliases);
|
||||
}
|
||||
await _gmailChangeProcessor.UpdateRemoteAliasInformationAsync(Account, remoteAliases).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override async Task<SynchronizationResult> SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -922,7 +922,7 @@ namespace Wino.Core.Synchronizers
|
||||
}
|
||||
|
||||
// In case of the high input, we'll batch them by 50 to reflect changes quickly.
|
||||
var batchedMissingMailIds = missingMailIds.Batch(50).Select(a => new UniqueIdSet(a, SortOrder.Descending));
|
||||
var batchedMissingMailIds = missingMailIds.Batch(50).Select(a => new UniqueIdSet(a, SortOrder.Ascending));
|
||||
|
||||
foreach (var batchMissingMailIds in batchedMissingMailIds)
|
||||
{
|
||||
|
||||
@@ -496,7 +496,7 @@ namespace Wino.Core.Synchronizers
|
||||
return userInfo.DisplayName;
|
||||
}
|
||||
|
||||
public override async Task<ProfileInformation> SynchronizeProfileInformationAsync()
|
||||
public override async Task<ProfileInformation> GetProfileInformationAsync()
|
||||
{
|
||||
var profilePictureData = await GetUserProfilePictureAsync().ConfigureAwait(false);
|
||||
var senderName = await GetSenderNameAsync().ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user