using System.Threading.Tasks; using Microsoft.Graph.Models.ODataErrors; using Microsoft.Kiota.Abstractions; using Serilog; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Synchronization; using Wino.Core.Integration.Processors; namespace Wino.Core.Synchronizers.Errors.Outlook; /// /// Handles 410 Gone errors for Outlook synchronization, which indicates that delta tokens have expired. /// When this occurs, all local mail cache should be deleted and initial synchronization should be reset. /// public class DeltaTokenExpiredHandler : ISynchronizerErrorHandler { private readonly ILogger _logger = Log.ForContext(); private readonly IOutlookChangeProcessor _outlookChangeProcessor; public DeltaTokenExpiredHandler(IOutlookChangeProcessor outlookChangeProcessor) { _outlookChangeProcessor = outlookChangeProcessor; } public bool CanHandle(SynchronizerErrorContext error) { // Handle 410 Gone responses which indicate delta token expiration return error.ErrorCode == 410 || (error.Exception is ODataError oDataError && oDataError.ResponseStatusCode == 410) || (error.Exception is ApiException apiException && apiException.ResponseStatusCode == 410); } public async Task HandleAsync(SynchronizerErrorContext error) { _logger.Warning("Delta token has expired for account {AccountName} ({AccountId}). Deleting all local mail cache and resetting synchronization.", error.Account.Name, error.Account.Id); try { // Delete all local mail cache for the account await _outlookChangeProcessor.DeleteUserMailCacheAsync(error.Account.Id).ConfigureAwait(false); // Reset the account's delta synchronization identifier await _outlookChangeProcessor.UpdateAccountDeltaSynchronizationIdentifierAsync(error.Account.Id, string.Empty).ConfigureAwait(false); // Get all folders for the account and reset their delta tokens var folders = await _outlookChangeProcessor.GetLocalFoldersAsync(error.Account.Id).ConfigureAwait(false); foreach (var folder in folders) { // Reset folder delta token to force full re-sync (last 30 days) await _outlookChangeProcessor.UpdateFolderDeltaSynchronizationIdentifierAsync(folder.Id, string.Empty).ConfigureAwait(false); } _logger.Information("Successfully reset synchronization state for account {AccountName} ({AccountId}). Next sync will download last 30 days.", error.Account.Name, error.Account.Id); return true; } catch (System.Exception ex) { _logger.Error(ex, "Failed to handle delta token expiration for account {AccountName} ({AccountId})", error.Account.Name, error.Account.Id); return false; } } }