Handle deleted events.

This commit is contained in:
Burak Kaan Köse
2025-12-30 23:32:00 +01:00
parent b81ab0ca15
commit 2056a2d783
4 changed files with 55 additions and 14 deletions
@@ -12,6 +12,7 @@ public interface ICalendarService
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId); Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId); Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId);
Task DeleteCalendarItemAsync(Guid calendarItemId); Task DeleteCalendarItemAsync(Guid calendarItemId);
Task DeleteCalendarItemAsync(string calendarRemoteEventId, Guid calendarId);
Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar); Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar);
Task InsertAccountCalendarAsync(AccountCalendar accountCalendar); Task InsertAccountCalendarAsync(AccountCalendar accountCalendar);
@@ -44,6 +44,7 @@ public interface IDefaultChangeProcessor
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId); Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
Task DeleteCalendarItemAsync(Guid calendarItemId); Task DeleteCalendarItemAsync(Guid calendarItemId);
Task DeleteCalendarItemAsync(string calendarRemoteEventId, Guid calendarId);
Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar); Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar);
Task InsertAccountCalendarAsync(AccountCalendar accountCalendar); Task InsertAccountCalendarAsync(AccountCalendar accountCalendar);
@@ -188,6 +189,9 @@ public class DefaultChangeProcessor(IDatabaseService databaseService,
public Task DeleteCalendarItemAsync(Guid calendarItemId) public Task DeleteCalendarItemAsync(Guid calendarItemId)
=> CalendarService.DeleteCalendarItemAsync(calendarItemId); => CalendarService.DeleteCalendarItemAsync(calendarItemId);
public Task DeleteCalendarItemAsync(string calendarRemoteEventId, Guid calendarId)
=> CalendarService.DeleteCalendarItemAsync(calendarRemoteEventId, calendarId);
public Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar) public Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar)
=> CalendarService.DeleteAccountCalendarAsync(accountCalendar); => CalendarService.DeleteAccountCalendarAsync(accountCalendar);
+39 -14
View File
@@ -49,8 +49,6 @@ namespace Wino.Core.Synchronizers.Mail;
[JsonSerializable(typeof(OutlookFileAttachment))] [JsonSerializable(typeof(OutlookFileAttachment))]
public partial class OutlookSynchronizerJsonContext : JsonSerializerContext; public partial class OutlookSynchronizerJsonContext : JsonSerializerContext;
/// <summary> /// <summary>
/// Outlook synchronizer implementation with delta token synchronization. /// Outlook synchronizer implementation with delta token synchronization.
/// ///
@@ -102,6 +100,7 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
]; ];
private readonly SemaphoreSlim _handleItemRetrievalSemaphore = new(1); private readonly SemaphoreSlim _handleItemRetrievalSemaphore = new(1);
private readonly SemaphoreSlim _handleCalendarEventRetrievalSemaphore = new(1);
private readonly ILogger _logger = Log.ForContext<OutlookSynchronizer>(); private readonly ILogger _logger = Log.ForContext<OutlookSynchronizer>();
private readonly IOutlookChangeProcessor _outlookChangeProcessor; private readonly IOutlookChangeProcessor _outlookChangeProcessor;
@@ -1633,8 +1632,8 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
_logger.Information("No calendar sync identifier for calendar {Name}. Performing initial sync.", calendar.Name); _logger.Information("No calendar sync identifier for calendar {Name}. Performing initial sync.", calendar.Name);
// ISO 8601 format as expected by Microsoft Graph API (e.g., "2019-11-08T19:00:00-08:00") // ISO 8601 format as expected by Microsoft Graph API (e.g., "2019-11-08T19:00:00-08:00")
var startDate = DateTimeOffset.UtcNow.AddYears(-2).ToString("yyyy-MM-ddTHH:mm:sszzz"); var startDate = DateTimeOffset.Now.AddYears(-2).ToString("yyyy-MM-ddTHH:mm:sszzz");
var endDate = DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz"); var endDate = DateTimeOffset.Now.ToString("yyyy-MM-ddTHH:mm:sszzz");
eventsDeltaResponse = await _graphClient.Me.Calendars[calendar.RemoteCalendarId].CalendarView.Delta.GetAsDeltaGetResponseAsync((requestConfiguration) => eventsDeltaResponse = await _graphClient.Me.Calendars[calendar.RemoteCalendarId].CalendarView.Delta.GetAsDeltaGetResponseAsync((requestConfiguration) =>
{ {
@@ -1691,18 +1690,44 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
foreach (var item in events) foreach (var item in events)
{ {
try if (IsResourceDeleted(item.AdditionalData))
{ {
await _handleItemRetrievalSemaphore.WaitAsync(); await _outlookChangeProcessor.DeleteCalendarItemAsync(item.Id, calendar.Id).ConfigureAwait(false);
await _outlookChangeProcessor.ManageCalendarEventAsync(item, calendar, Account).ConfigureAwait(false);
} }
catch (Exception) else
{ {
// _logger.Error(ex, "Error occurred while handling item {Id} for calendar {Name}", item.Id, calendar.Name); try
} {
finally await _handleCalendarEventRetrievalSemaphore.WaitAsync();
{
_handleItemRetrievalSemaphore.Release(); // Check if the event has complete information
// Sometimes delta sync returns events with only Id available
Event fullEvent = item;
if (string.IsNullOrEmpty(item.Subject) || item.Start == null || item.End == null)
{
_logger.Information("Event {Id} has incomplete data. Downloading full event details.", item.Id);
try
{
fullEvent = await _graphClient.Me.Calendars[calendar.RemoteCalendarId].Events[item.Id].GetAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch (Exception downloadEx)
{
_logger.Error(downloadEx, "Failed to download full event details for {Id}. Using partial data.", item.Id);
fullEvent = item; // Fallback to partial data
}
}
await _outlookChangeProcessor.ManageCalendarEventAsync(fullEvent, calendar, Account).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Error(ex, "Error occurred while handling item {Id} for calendar {Name}", item.Id, calendar.Name);
}
finally
{
_handleCalendarEventRetrievalSemaphore.Release();
}
} }
} }
@@ -1715,7 +1740,7 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
var deltaToken = GetDeltaTokenFromDeltaLink(latestDeltaLink); var deltaToken = GetDeltaTokenFromDeltaLink(latestDeltaLink);
/// await _outlookChangeProcessor.UpdateCalendarDeltaSynchronizationToken(calendar.Id, deltaToken).ConfigureAwait(false); await _outlookChangeProcessor.UpdateCalendarDeltaSynchronizationToken(calendar.Id, deltaToken).ConfigureAwait(false);
} }
} }
+11
View File
@@ -76,6 +76,17 @@ public class CalendarService : BaseDatabaseService, ICalendarService
} }
} }
public async Task DeleteCalendarItemAsync(string calendarRemoteEventId, Guid calendarId)
{
var calendarItem = await Connection.FindWithQueryAsync<CalendarItem>(
"SELECT * FROM CalendarItem WHERE CalendarId = ? AND RemoteEventId = ?",
calendarId, calendarRemoteEventId);
if (calendarItem == null) return;
await DeleteCalendarItemAsync(calendarItem.Id);
}
public async Task CreateNewCalendarItemAsync(CalendarItem calendarItem, List<CalendarEventAttendee> attendees) public async Task CreateNewCalendarItemAsync(CalendarItem calendarItem, List<CalendarEventAttendee> attendees)
{ {
try try