Synchronizer error factory implementation (#645)
* Added sync error factories for outlook and gmail. * Implement ObjectCannotBeDeletedHandler for OutlookSynchronizer. * Remove debug code. * Implement del key to delete on mail list. * Revert debug code.
This commit is contained in:
@@ -4,6 +4,7 @@ using Wino.Authentication;
|
|||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Integration.Processors;
|
using Wino.Core.Integration.Processors;
|
||||||
using Wino.Core.Services;
|
using Wino.Core.Services;
|
||||||
|
using Wino.Core.Synchronizers.Errors.Outlook;
|
||||||
using Wino.Core.Synchronizers.ImapSync;
|
using Wino.Core.Synchronizers.ImapSync;
|
||||||
|
|
||||||
namespace Wino.Core;
|
namespace Wino.Core;
|
||||||
@@ -34,5 +35,11 @@ public static class CoreContainerSetup
|
|||||||
services.AddTransient<CondstoreSynchronizer>();
|
services.AddTransient<CondstoreSynchronizer>();
|
||||||
services.AddTransient<QResyncSynchronizer>();
|
services.AddTransient<QResyncSynchronizer>();
|
||||||
services.AddTransient<UidBasedSynchronizer>();
|
services.AddTransient<UidBasedSynchronizer>();
|
||||||
|
|
||||||
|
// Register error factory handlers
|
||||||
|
services.AddTransient<ObjectCannotBeDeletedHandler>();
|
||||||
|
|
||||||
|
services.AddTransient<IOutlookSynchronizerErrorHandlerFactory, OutlookSynchronizerErrorHandlingFactory>();
|
||||||
|
services.AddTransient<IGmailSynchronizerErrorHandlerFactory, GmailSynchronizerErrorHandlingFactory>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
Wino.Core/Domain/Interfaces/ISynchronizerErrorHandler.cs
Normal file
32
Wino.Core/Domain/Interfaces/ISynchronizerErrorHandler.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Wino.Core.Domain.Models.Errors;
|
||||||
|
|
||||||
|
namespace Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for handling specific synchronizer errors
|
||||||
|
/// </summary>
|
||||||
|
public interface ISynchronizerErrorHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if this handler can handle the specified error
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="error">The error to check</param>
|
||||||
|
/// <returns>True if this handler can handle the error, false otherwise</returns>
|
||||||
|
bool CanHandle(SynchronizerErrorContext error);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the specified error
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="error">The error to handle</param>
|
||||||
|
/// <returns>A task that completes when the error is handled</returns>
|
||||||
|
Task<bool> HandleAsync(SynchronizerErrorContext error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ISynchronizerErrorHandlerFactory
|
||||||
|
{
|
||||||
|
Task<bool> HandleErrorAsync(SynchronizerErrorContext error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IOutlookSynchronizerErrorHandlerFactory : ISynchronizerErrorHandlerFactory;
|
||||||
|
public interface IGmailSynchronizerErrorHandlerFactory : ISynchronizerErrorHandlerFactory;
|
||||||
42
Wino.Core/Domain/Models/Errors/SynchronizerErrorContext.cs
Normal file
42
Wino.Core/Domain/Models/Errors/SynchronizerErrorContext.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Wino.Core.Domain.Entities.Shared;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
|
namespace Wino.Core.Domain.Models.Errors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains context information about a synchronizer error
|
||||||
|
/// </summary>
|
||||||
|
public class SynchronizerErrorContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Account associated with the error
|
||||||
|
/// </summary>
|
||||||
|
public MailAccount Account { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the error code
|
||||||
|
/// </summary>
|
||||||
|
public int? ErrorCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the error message
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the request bundle associated with the error
|
||||||
|
/// </summary>
|
||||||
|
public IRequestBundle RequestBundle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets additional data associated with the error
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, object> AdditionalData { get; set; } = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the exception associated with the error
|
||||||
|
/// </summary>
|
||||||
|
public Exception Exception { get; set; }
|
||||||
|
}
|
||||||
11
Wino.Core/Services/GmailSynchronizerErrorHandlingFactory.cs
Normal file
11
Wino.Core/Services/GmailSynchronizerErrorHandlingFactory.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.Errors;
|
||||||
|
|
||||||
|
namespace Wino.Core.Services;
|
||||||
|
public class GmailSynchronizerErrorHandlingFactory : SynchronizerErrorHandlingFactory, IGmailSynchronizerErrorHandlerFactory
|
||||||
|
{
|
||||||
|
public bool CanHandle(SynchronizerErrorContext error) => CanHandle(error);
|
||||||
|
|
||||||
|
public Task HandleAsync(SynchronizerErrorContext error) => HandleErrorAsync(error);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.Errors;
|
||||||
|
using Wino.Core.Synchronizers.Errors.Outlook;
|
||||||
|
|
||||||
|
namespace Wino.Core.Services;
|
||||||
|
|
||||||
|
public class OutlookSynchronizerErrorHandlingFactory : SynchronizerErrorHandlingFactory, IOutlookSynchronizerErrorHandlerFactory
|
||||||
|
{
|
||||||
|
public OutlookSynchronizerErrorHandlingFactory(ObjectCannotBeDeletedHandler objectCannotBeDeleted)
|
||||||
|
{
|
||||||
|
RegisterHandler(objectCannotBeDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanHandle(SynchronizerErrorContext error) => CanHandle(error);
|
||||||
|
|
||||||
|
public Task HandleAsync(SynchronizerErrorContext error) => HandleErrorAsync(error);
|
||||||
|
}
|
||||||
49
Wino.Core/Services/SynchronizerErrorHandlingFactory.cs
Normal file
49
Wino.Core/Services/SynchronizerErrorHandlingFactory.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Serilog;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.Errors;
|
||||||
|
|
||||||
|
namespace Wino.Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory for handling synchronizer errors
|
||||||
|
/// </summary>
|
||||||
|
public class SynchronizerErrorHandlingFactory
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger = Log.ForContext<SynchronizerErrorHandlingFactory>();
|
||||||
|
private readonly List<ISynchronizerErrorHandler> _handlers = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an error handler
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">The handler to register</param>
|
||||||
|
public void RegisterHandler(ISynchronizerErrorHandler handler)
|
||||||
|
{
|
||||||
|
_handlers.Add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles an error using the registered handlers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="error">The error to handle</param>
|
||||||
|
/// <returns>True if the error was handled, false otherwise</returns>
|
||||||
|
public async Task<bool> HandleErrorAsync(SynchronizerErrorContext error)
|
||||||
|
{
|
||||||
|
foreach (var handler in _handlers)
|
||||||
|
{
|
||||||
|
if (handler.CanHandle(error))
|
||||||
|
{
|
||||||
|
_logger.Debug("Found handler {HandlerType} for error code {ErrorCode} message {ErrorMessage}",
|
||||||
|
handler.GetType().Name, error.ErrorCode, error.ErrorMessage);
|
||||||
|
|
||||||
|
return await handler.HandleAsync(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("No handler found for error code {ErrorCode} message {ErrorMessage}",
|
||||||
|
error.ErrorCode, error.ErrorMessage);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ public class SynchronizerFactory : ISynchronizerFactory
|
|||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly IImapSynchronizationStrategyProvider _imapSynchronizationStrategyProvider;
|
private readonly IImapSynchronizationStrategyProvider _imapSynchronizationStrategyProvider;
|
||||||
private readonly IApplicationConfiguration _applicationConfiguration;
|
private readonly IApplicationConfiguration _applicationConfiguration;
|
||||||
|
private readonly IOutlookSynchronizerErrorHandlerFactory _outlookSynchronizerErrorHandlerFactory;
|
||||||
|
private readonly IGmailSynchronizerErrorHandlerFactory _gmailSynchronizerErrorHandlerFactory;
|
||||||
private readonly IOutlookChangeProcessor _outlookChangeProcessor;
|
private readonly IOutlookChangeProcessor _outlookChangeProcessor;
|
||||||
private readonly IGmailChangeProcessor _gmailChangeProcessor;
|
private readonly IGmailChangeProcessor _gmailChangeProcessor;
|
||||||
private readonly IImapChangeProcessor _imapChangeProcessor;
|
private readonly IImapChangeProcessor _imapChangeProcessor;
|
||||||
@@ -30,7 +32,9 @@ public class SynchronizerFactory : ISynchronizerFactory
|
|||||||
IGmailAuthenticator gmailAuthenticator,
|
IGmailAuthenticator gmailAuthenticator,
|
||||||
IAccountService accountService,
|
IAccountService accountService,
|
||||||
IImapSynchronizationStrategyProvider imapSynchronizationStrategyProvider,
|
IImapSynchronizationStrategyProvider imapSynchronizationStrategyProvider,
|
||||||
IApplicationConfiguration applicationConfiguration)
|
IApplicationConfiguration applicationConfiguration,
|
||||||
|
IOutlookSynchronizerErrorHandlerFactory outlookSynchronizerErrorHandlerFactory,
|
||||||
|
IGmailSynchronizerErrorHandlerFactory gmailSynchronizerErrorHandlerFactory)
|
||||||
{
|
{
|
||||||
_outlookChangeProcessor = outlookChangeProcessor;
|
_outlookChangeProcessor = outlookChangeProcessor;
|
||||||
_gmailChangeProcessor = gmailChangeProcessor;
|
_gmailChangeProcessor = gmailChangeProcessor;
|
||||||
@@ -40,6 +44,8 @@ public class SynchronizerFactory : ISynchronizerFactory
|
|||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
_imapSynchronizationStrategyProvider = imapSynchronizationStrategyProvider;
|
_imapSynchronizationStrategyProvider = imapSynchronizationStrategyProvider;
|
||||||
_applicationConfiguration = applicationConfiguration;
|
_applicationConfiguration = applicationConfiguration;
|
||||||
|
_outlookSynchronizerErrorHandlerFactory = outlookSynchronizerErrorHandlerFactory;
|
||||||
|
_gmailSynchronizerErrorHandlerFactory = gmailSynchronizerErrorHandlerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IWinoSynchronizerBase> GetAccountSynchronizerAsync(Guid accountId)
|
public async Task<IWinoSynchronizerBase> GetAccountSynchronizerAsync(Guid accountId)
|
||||||
@@ -69,9 +75,9 @@ public class SynchronizerFactory : ISynchronizerFactory
|
|||||||
switch (providerType)
|
switch (providerType)
|
||||||
{
|
{
|
||||||
case Domain.Enums.MailProviderType.Outlook:
|
case Domain.Enums.MailProviderType.Outlook:
|
||||||
return new OutlookSynchronizer(mailAccount, _outlookAuthenticator, _outlookChangeProcessor);
|
return new OutlookSynchronizer(mailAccount, _outlookAuthenticator, _outlookChangeProcessor, _outlookSynchronizerErrorHandlerFactory);
|
||||||
case Domain.Enums.MailProviderType.Gmail:
|
case Domain.Enums.MailProviderType.Gmail:
|
||||||
return new GmailSynchronizer(mailAccount, _gmailAuthenticator, _gmailChangeProcessor);
|
return new GmailSynchronizer(mailAccount, _gmailAuthenticator, _gmailChangeProcessor, _gmailSynchronizerErrorHandlerFactory);
|
||||||
case Domain.Enums.MailProviderType.IMAP4:
|
case Domain.Enums.MailProviderType.IMAP4:
|
||||||
return new ImapSynchronizer(mailAccount, _imapChangeProcessor, _imapSynchronizationStrategyProvider, _applicationConfiguration);
|
return new ImapSynchronizer(mailAccount, _imapChangeProcessor, _imapSynchronizationStrategyProvider, _applicationConfiguration);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Kiota.Abstractions;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.Errors;
|
||||||
|
using Wino.Core.Domain.Models.Requests;
|
||||||
|
using Wino.Core.Requests.Bundles;
|
||||||
|
|
||||||
|
namespace Wino.Core.Synchronizers.Errors.Outlook;
|
||||||
|
|
||||||
|
public class ObjectCannotBeDeletedHandler : ISynchronizerErrorHandler
|
||||||
|
{
|
||||||
|
private readonly IMailService _mailService;
|
||||||
|
|
||||||
|
public ObjectCannotBeDeletedHandler(IMailService mailService)
|
||||||
|
{
|
||||||
|
_mailService = mailService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanHandle(SynchronizerErrorContext error)
|
||||||
|
{
|
||||||
|
return error.ErrorMessage.Contains("ErrorCannotDeleteObject") && error.RequestBundle is HttpRequestBundle<RequestInformation>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HandleAsync(SynchronizerErrorContext error)
|
||||||
|
{
|
||||||
|
var castedBundle = error.RequestBundle as HttpRequestBundle<RequestInformation>;
|
||||||
|
|
||||||
|
if (castedBundle?.Request is MailRequestBase mailRequest)
|
||||||
|
{
|
||||||
|
var request = castedBundle.Request;
|
||||||
|
|
||||||
|
await _mailService.DeleteMailAsync(error.Account.Id, mailRequest.Item.Id);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ using Wino.Core.Domain.Enums;
|
|||||||
using Wino.Core.Domain.Exceptions;
|
using Wino.Core.Domain.Exceptions;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Accounts;
|
using Wino.Core.Domain.Models.Accounts;
|
||||||
|
using Wino.Core.Domain.Models.Errors;
|
||||||
using Wino.Core.Domain.Models.Folders;
|
using Wino.Core.Domain.Models.Folders;
|
||||||
using Wino.Core.Domain.Models.MailItem;
|
using Wino.Core.Domain.Models.MailItem;
|
||||||
using Wino.Core.Domain.Models.Synchronization;
|
using Wino.Core.Domain.Models.Synchronization;
|
||||||
@@ -57,6 +58,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
private readonly PeopleServiceService _peopleService;
|
private readonly PeopleServiceService _peopleService;
|
||||||
|
|
||||||
private readonly IGmailChangeProcessor _gmailChangeProcessor;
|
private readonly IGmailChangeProcessor _gmailChangeProcessor;
|
||||||
|
private readonly IGmailSynchronizerErrorHandlerFactory _gmailSynchronizerErrorHandlerFactory;
|
||||||
private readonly ILogger _logger = Log.ForContext<GmailSynchronizer>();
|
private readonly ILogger _logger = Log.ForContext<GmailSynchronizer>();
|
||||||
|
|
||||||
// Keeping a reference for quick access to the virtual archive folder.
|
// Keeping a reference for quick access to the virtual archive folder.
|
||||||
@@ -64,7 +66,8 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
|
|
||||||
public GmailSynchronizer(MailAccount account,
|
public GmailSynchronizer(MailAccount account,
|
||||||
IGmailAuthenticator authenticator,
|
IGmailAuthenticator authenticator,
|
||||||
IGmailChangeProcessor gmailChangeProcessor) : base(account)
|
IGmailChangeProcessor gmailChangeProcessor,
|
||||||
|
IGmailSynchronizerErrorHandlerFactory gmailSynchronizerErrorHandlerFactory) : base(account)
|
||||||
{
|
{
|
||||||
var messageHandler = new GmailClientMessageHandler(authenticator, account);
|
var messageHandler = new GmailClientMessageHandler(authenticator, account);
|
||||||
|
|
||||||
@@ -79,6 +82,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
_calendarService = new CalendarService(initializer);
|
_calendarService = new CalendarService(initializer);
|
||||||
|
|
||||||
_gmailChangeProcessor = gmailChangeProcessor;
|
_gmailChangeProcessor = gmailChangeProcessor;
|
||||||
|
_gmailSynchronizerErrorHandlerFactory = gmailSynchronizerErrorHandlerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args) => _googleHttpClient;
|
public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args) => _googleHttpClient;
|
||||||
@@ -1106,30 +1110,50 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessGmailRequestError(RequestError error, IRequestBundle<IClientServiceRequest> bundle)
|
private async Task ProcessGmailRequestErrorAsync(RequestError error, IRequestBundle<IClientServiceRequest> bundle)
|
||||||
{
|
{
|
||||||
if (error == null) return;
|
if (error == null) return;
|
||||||
|
|
||||||
// OutOfMemoryException is a known bug in Gmail SDK.
|
// Create error context
|
||||||
if (error.Code == 0)
|
var errorContext = new SynchronizerErrorContext
|
||||||
{
|
{
|
||||||
bundle?.UIChangeRequest?.RevertUIChanges();
|
ErrorCode = error.Code,
|
||||||
throw new OutOfMemoryException(error.Message);
|
ErrorMessage = error.Message,
|
||||||
}
|
RequestBundle = bundle,
|
||||||
|
AdditionalData = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "Account", Account },
|
||||||
|
{ "Error", error }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Entity not found.
|
// Try to handle the error with registered handlers
|
||||||
if (error.Code == 404)
|
var handled = await _gmailSynchronizerErrorHandlerFactory.HandleErrorAsync(errorContext);
|
||||||
|
|
||||||
|
// If not handled by any specific handler, apply default error handling
|
||||||
|
if (!handled)
|
||||||
{
|
{
|
||||||
bundle?.UIChangeRequest?.RevertUIChanges();
|
// OutOfMemoryException is a known bug in Gmail SDK.
|
||||||
throw new SynchronizerEntityNotFoundException(error.Message);
|
if (error.Code == 0)
|
||||||
}
|
{
|
||||||
|
bundle?.UIChangeRequest?.RevertUIChanges();
|
||||||
|
throw new OutOfMemoryException(error.Message);
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(error.Message))
|
// Entity not found.
|
||||||
{
|
if (error.Code == 404)
|
||||||
bundle?.UIChangeRequest?.RevertUIChanges();
|
{
|
||||||
error.Errors?.ForEach(error => _logger.Error("Unknown Gmail SDK error for {Name}\n{Error}", Account.Name, error));
|
bundle?.UIChangeRequest?.RevertUIChanges();
|
||||||
|
throw new SynchronizerEntityNotFoundException(error.Message);
|
||||||
|
}
|
||||||
|
|
||||||
throw new SynchronizerException(error.Message);
|
if (!string.IsNullOrEmpty(error.Message))
|
||||||
|
{
|
||||||
|
bundle?.UIChangeRequest?.RevertUIChanges();
|
||||||
|
error.Errors?.ForEach(error => _logger.Error("Unknown Gmail SDK error for {Name}\n{Error}", Account.Name, error));
|
||||||
|
|
||||||
|
throw new SynchronizerException(error.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1148,7 +1172,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProcessGmailRequestError(error, null);
|
await ProcessGmailRequestErrorAsync(error, null);
|
||||||
}
|
}
|
||||||
catch (OutOfMemoryException)
|
catch (OutOfMemoryException)
|
||||||
{
|
{
|
||||||
@@ -1209,7 +1233,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
HttpResponseMessage httpResponseMessage,
|
HttpResponseMessage httpResponseMessage,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
ProcessGmailRequestError(error, bundle);
|
await ProcessGmailRequestErrorAsync(error, bundle);
|
||||||
|
|
||||||
if (bundle is HttpRequestBundle<IClientServiceRequest, Message> messageBundle)
|
if (bundle is HttpRequestBundle<IClientServiceRequest, Message> messageBundle)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ using Wino.Core.Domain.Enums;
|
|||||||
using Wino.Core.Domain.Exceptions;
|
using Wino.Core.Domain.Exceptions;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Accounts;
|
using Wino.Core.Domain.Models.Accounts;
|
||||||
|
using Wino.Core.Domain.Models.Errors;
|
||||||
using Wino.Core.Domain.Models.Folders;
|
using Wino.Core.Domain.Models.Folders;
|
||||||
using Wino.Core.Domain.Models.MailItem;
|
using Wino.Core.Domain.Models.MailItem;
|
||||||
using Wino.Core.Domain.Models.Synchronization;
|
using Wino.Core.Domain.Models.Synchronization;
|
||||||
@@ -83,9 +84,12 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
|||||||
private readonly ILogger _logger = Log.ForContext<OutlookSynchronizer>();
|
private readonly ILogger _logger = Log.ForContext<OutlookSynchronizer>();
|
||||||
private readonly IOutlookChangeProcessor _outlookChangeProcessor;
|
private readonly IOutlookChangeProcessor _outlookChangeProcessor;
|
||||||
private readonly GraphServiceClient _graphClient;
|
private readonly GraphServiceClient _graphClient;
|
||||||
|
private readonly IOutlookSynchronizerErrorHandlerFactory _errorHandlingFactory;
|
||||||
|
|
||||||
public OutlookSynchronizer(MailAccount account,
|
public OutlookSynchronizer(MailAccount account,
|
||||||
IAuthenticator authenticator,
|
IAuthenticator authenticator,
|
||||||
IOutlookChangeProcessor outlookChangeProcessor) : base(account)
|
IOutlookChangeProcessor outlookChangeProcessor,
|
||||||
|
IOutlookSynchronizerErrorHandlerFactory errorHandlingFactory) : base(account)
|
||||||
{
|
{
|
||||||
var tokenProvider = new MicrosoftTokenProvider(Account, authenticator);
|
var tokenProvider = new MicrosoftTokenProvider(Account, authenticator);
|
||||||
|
|
||||||
@@ -106,6 +110,7 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
|||||||
_graphClient = new GraphServiceClient(httpClient, new BaseBearerTokenAuthenticationProvider(tokenProvider));
|
_graphClient = new GraphServiceClient(httpClient, new BaseBearerTokenAuthenticationProvider(tokenProvider));
|
||||||
|
|
||||||
_outlookChangeProcessor = outlookChangeProcessor;
|
_outlookChangeProcessor = outlookChangeProcessor;
|
||||||
|
_errorHandlingFactory = errorHandlingFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region MS Graph Handlers
|
#region MS Graph Handlers
|
||||||
@@ -990,14 +995,37 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
|||||||
HttpResponseMessage response,
|
HttpResponseMessage response,
|
||||||
List<string> errors)
|
List<string> errors)
|
||||||
{
|
{
|
||||||
bundle.UIChangeRequest?.RevertUIChanges();
|
|
||||||
|
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
var errorJson = JsonNode.Parse(content);
|
var errorJson = JsonNode.Parse(content);
|
||||||
var errorString = $"[{response.StatusCode}] {errorJson["error"]["code"]} - {errorJson["error"]["message"]}\n";
|
var errorCode = errorJson["error"]["code"].GetValue<string>();
|
||||||
|
var errorMessage = errorJson["error"]["message"].GetValue<string>();
|
||||||
|
var errorString = $"[{response.StatusCode}] {errorCode} - {errorMessage}\n";
|
||||||
|
|
||||||
Debug.WriteLine(errorString);
|
// Create error context
|
||||||
errors.Add(errorString);
|
var errorContext = new SynchronizerErrorContext
|
||||||
|
{
|
||||||
|
Account = Account,
|
||||||
|
ErrorCode = (int)response.StatusCode,
|
||||||
|
ErrorMessage = errorMessage,
|
||||||
|
RequestBundle = bundle,
|
||||||
|
AdditionalData = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "ErrorCode", errorCode },
|
||||||
|
{ "HttpResponse", response },
|
||||||
|
{ "Content", content }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to handle the error with registered handlers
|
||||||
|
var handled = await _errorHandlingFactory.HandleErrorAsync(errorContext);
|
||||||
|
|
||||||
|
// If not handled by any specific handler, revert UI changes and add to error list
|
||||||
|
if (!handled)
|
||||||
|
{
|
||||||
|
bundle.UIChangeRequest?.RevertUIChanges();
|
||||||
|
Debug.WriteLine(errorString);
|
||||||
|
errors.Add(errorString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowBatchExecutionException(List<string> errors)
|
private void ThrowBatchExecutionException(List<string> errors)
|
||||||
|
|||||||
@@ -203,6 +203,8 @@
|
|||||||
Key="A"
|
Key="A"
|
||||||
Invoked="SelectAllInvoked"
|
Invoked="SelectAllInvoked"
|
||||||
Modifiers="Control" />
|
Modifiers="Control" />
|
||||||
|
|
||||||
|
<KeyboardAccelerator Key="Delete" Invoked="DeleteAllInvoked" />
|
||||||
</Page.KeyboardAccelerators>
|
</Page.KeyboardAccelerators>
|
||||||
<wino:BasePage.ShellContent>
|
<wino:BasePage.ShellContent>
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
@@ -495,4 +495,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
|
|
||||||
private void SelectAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
private void SelectAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||||
=> MailListView.SelectAllWino();
|
=> MailListView.SelectAllWino();
|
||||||
|
|
||||||
|
private void DeleteAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||||
|
=> ViewModel.ExecuteMailOperationCommand.Execute(MailOperation.SoftDelete);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,6 @@ public class UserActionRequestHandler : ServerMessageHandler<ServerRequestPackag
|
|||||||
var synchronizer = await _synchronizerFactory.GetAccountSynchronizerAsync(package.AccountId);
|
var synchronizer = await _synchronizerFactory.GetAccountSynchronizerAsync(package.AccountId);
|
||||||
synchronizer.QueueRequest(package.Request);
|
synchronizer.QueueRequest(package.Request);
|
||||||
|
|
||||||
//if (package.QueueSynchronization)
|
|
||||||
//{
|
|
||||||
// var options = new SynchronizationOptions
|
|
||||||
// {
|
|
||||||
// AccountId = package.AccountId,
|
|
||||||
// Type = Wino.Core.Domain.Enums.SynchronizationType.ExecuteRequests
|
|
||||||
// };
|
|
||||||
|
|
||||||
// WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options));
|
|
||||||
//}
|
|
||||||
|
|
||||||
return WinoServerResponse<bool>.CreateSuccessResponse(true);
|
return WinoServerResponse<bool>.CreateSuccessResponse(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user