diff --git a/src/Api/NotificationCenter/Controllers/NotificationsController.cs b/src/Api/NotificationCenter/Controllers/NotificationsController.cs index ab27b943b..8431d8eac 100644 --- a/src/Api/NotificationCenter/Controllers/NotificationsController.cs +++ b/src/Api/NotificationCenter/Controllers/NotificationsController.cs @@ -1,10 +1,13 @@ #nullable enable +using System.Text.Json; using Bit.Api.Models.Response; +using Bit.Api.NotificationCenter.Models; using Bit.Api.NotificationCenter.Models.Request; using Bit.Api.NotificationCenter.Models.Response; using Bit.Core.NotificationCenter.Commands.Interfaces; using Bit.Core.NotificationCenter.Models.Filter; using Bit.Core.NotificationCenter.Queries.Interfaces; +using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -32,6 +35,7 @@ public class NotificationsController : Controller public async Task> List( [FromQuery] NotificationFilterRequestModel filter) { + var continuationToken = ParseContinuationToken(filter.ContinuationToken); var notificationStatusFilter = new NotificationStatusFilter { Read = filter.ReadStatusFilter, @@ -40,12 +44,27 @@ public class NotificationsController : Controller var notifications = await _getNotificationsForUserQuery.GetByUserIdStatusFilterAsync(notificationStatusFilter); - var filteredNotifications = notifications - .Where(n => n.RevisionDate >= filter.Start && n.RevisionDate < filter.End) - .Take(filter.PageSize); + if (continuationToken != null) + { + notifications = notifications + .Where(n => n.Priority <= continuationToken.Priority && n.RevisionDate < continuationToken.Date); + } - var responses = filteredNotifications.Select(n => new NotificationResponseModel(n)); - return new ListResponseModel(responses); + var responses = notifications + .Take(10) + .Select(n => new NotificationResponseModel(n)) + .ToList(); + + var nextContinuationToken = responses.Count > 0 && responses.Count < 10 + ? new NotificationContinuationToken + { + Priority = responses.Last().Priority, + Date = responses.Last().Date + } + : null; + + return new ListResponseModel(responses, + CreateContinuationToken(nextContinuationToken)); } [HttpPatch("{id}/delete")] @@ -59,4 +78,27 @@ public class NotificationsController : Controller { await _markNotificationReadCommand.MarkReadAsync(id); } + + private NotificationContinuationToken? ParseContinuationToken(string? continuationToken) + { + if (continuationToken == null) + { + return null; + } + + var decodedContinuationToken = CoreHelpers.Base64UrlDecodeString(continuationToken); + return JsonSerializer.Deserialize(decodedContinuationToken, + JsonHelpers.IgnoreCase); + } + + private string? CreateContinuationToken(NotificationContinuationToken? notificationContinuationToken) + { + if (notificationContinuationToken == null) + { + return null; + } + + var serializedContinuationToken = JsonSerializer.Serialize(notificationContinuationToken); + return CoreHelpers.Base64UrlEncodeString(serializedContinuationToken); + } } diff --git a/src/Api/NotificationCenter/Models/NotificationContinuationToken.cs b/src/Api/NotificationCenter/Models/NotificationContinuationToken.cs new file mode 100644 index 000000000..af8773622 --- /dev/null +++ b/src/Api/NotificationCenter/Models/NotificationContinuationToken.cs @@ -0,0 +1,11 @@ +#nullable enable +using Bit.Core.NotificationCenter.Enums; + +namespace Bit.Api.NotificationCenter.Models; + +public class NotificationContinuationToken +{ + public Priority Priority { get; set; } + + public DateTime Date { get; set; } +} diff --git a/src/Api/NotificationCenter/Models/Request/NotificationFilterRequestModel.cs b/src/Api/NotificationCenter/Models/Request/NotificationFilterRequestModel.cs index a6a46f63a..16cda0998 100644 --- a/src/Api/NotificationCenter/Models/Request/NotificationFilterRequestModel.cs +++ b/src/Api/NotificationCenter/Models/Request/NotificationFilterRequestModel.cs @@ -1,4 +1,5 @@ -namespace Bit.Api.NotificationCenter.Models.Request; +#nullable enable +namespace Bit.Api.NotificationCenter.Models.Request; public class NotificationFilterRequestModel { @@ -13,17 +14,7 @@ public class NotificationFilterRequestModel public bool? DeletedStatusFilter { get; set; } /// - /// The start date. Must be less than the end date. Inclusive. + /// A cursor for use in pagination. /// - public DateTime Start { get; set; } = DateTime.MinValue; - - /// - /// The end date. Must be greater than the start date. Not inclusive. - /// - public DateTime End { get; set; } = DateTime.MaxValue; - - /// - /// Number of items to return. Defaults to 10. - /// - public int PageSize { get; set; } = 10; + public string? ContinuationToken { get; set; } }