diff --git a/src/Core/Enums/PushType.cs b/src/Core/Enums/PushType.cs index 77719cc67c..5af56e0767 100644 --- a/src/Core/Enums/PushType.cs +++ b/src/Core/Enums/PushType.cs @@ -26,5 +26,6 @@ public enum PushType : byte SyncOrganizations = 17, - SyncNotification = 18, + SyncNotificationCreate = 18, + SyncNotificationUpdate = 19 } diff --git a/src/Core/Models/PushNotification.cs b/src/Core/Models/PushNotification.cs index f8dc7acb8a..d6fac8b381 100644 --- a/src/Core/Models/PushNotification.cs +++ b/src/Core/Models/PushNotification.cs @@ -1,10 +1,11 @@ -using Bit.Core.Enums; +#nullable enable +using Bit.Core.Enums; namespace Bit.Core.Models; public class PushNotificationData { - public PushNotificationData(PushType type, T payload, string contextId) + public PushNotificationData(PushType type, T payload, string? contextId) { Type = type; Payload = payload; @@ -13,7 +14,7 @@ public class PushNotificationData public PushType Type { get; set; } public T Payload { get; set; } - public string ContextId { get; set; } + public string? ContextId { get; set; } } public class SyncCipherPushNotification @@ -21,7 +22,7 @@ public class SyncCipherPushNotification public Guid Id { get; set; } public Guid? UserId { get; set; } public Guid? OrganizationId { get; set; } - public IEnumerable CollectionIds { get; set; } + public IEnumerable? CollectionIds { get; set; } public DateTime RevisionDate { get; set; } } @@ -52,6 +53,8 @@ public class SyncNotificationPushNotification public Guid? OrganizationId { get; set; } public ClientType ClientType { get; set; } public DateTime RevisionDate { get; set; } + public DateTime? ReadDate { get; set; } + public DateTime? DeletedDate { get; set; } } public class AuthRequestPushNotification diff --git a/src/Core/NotificationCenter/Commands/CreateNotificationCommand.cs b/src/Core/NotificationCenter/Commands/CreateNotificationCommand.cs index 75faba0874..c512ed2227 100644 --- a/src/Core/NotificationCenter/Commands/CreateNotificationCommand.cs +++ b/src/Core/NotificationCenter/Commands/CreateNotificationCommand.cs @@ -37,7 +37,7 @@ public class CreateNotificationCommand : ICreateNotificationCommand var newNotification = await _notificationRepository.CreateAsync(notification); - await _pushNotificationService.PushSyncNotificationAsync(newNotification); + await _pushNotificationService.PushSyncNotificationCreateAsync(newNotification, null); return newNotification; } diff --git a/src/Core/NotificationCenter/Commands/CreateNotificationStatusCommand.cs b/src/Core/NotificationCenter/Commands/CreateNotificationStatusCommand.cs index fcd61ceebc..3c1b0fcf00 100644 --- a/src/Core/NotificationCenter/Commands/CreateNotificationStatusCommand.cs +++ b/src/Core/NotificationCenter/Commands/CreateNotificationStatusCommand.cs @@ -5,6 +5,7 @@ using Bit.Core.NotificationCenter.Authorization; using Bit.Core.NotificationCenter.Commands.Interfaces; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; @@ -16,16 +17,19 @@ public class CreateNotificationStatusCommand : ICreateNotificationStatusCommand private readonly IAuthorizationService _authorizationService; private readonly INotificationRepository _notificationRepository; private readonly INotificationStatusRepository _notificationStatusRepository; + private readonly IPushNotificationService _pushNotificationService; public CreateNotificationStatusCommand(ICurrentContext currentContext, IAuthorizationService authorizationService, INotificationRepository notificationRepository, - INotificationStatusRepository notificationStatusRepository) + INotificationStatusRepository notificationStatusRepository, + IPushNotificationService pushNotificationService) { _currentContext = currentContext; _authorizationService = authorizationService; _notificationRepository = notificationRepository; _notificationStatusRepository = notificationStatusRepository; + _pushNotificationService = pushNotificationService; } public async Task CreateAsync(NotificationStatus notificationStatus) @@ -42,6 +46,10 @@ public class CreateNotificationStatusCommand : ICreateNotificationStatusCommand await _authorizationService.AuthorizeOrThrowAsync(_currentContext.HttpContext.User, notificationStatus, NotificationStatusOperations.Create); - return await _notificationStatusRepository.CreateAsync(notificationStatus); + var newNotificationStatus = await _notificationStatusRepository.CreateAsync(notificationStatus); + + await _pushNotificationService.PushSyncNotificationCreateAsync(notification, newNotificationStatus); + + return newNotificationStatus; } } diff --git a/src/Core/NotificationCenter/Commands/MarkNotificationDeletedCommand.cs b/src/Core/NotificationCenter/Commands/MarkNotificationDeletedCommand.cs index 2ca7aa9051..2928f4ccb2 100644 --- a/src/Core/NotificationCenter/Commands/MarkNotificationDeletedCommand.cs +++ b/src/Core/NotificationCenter/Commands/MarkNotificationDeletedCommand.cs @@ -5,6 +5,7 @@ using Bit.Core.NotificationCenter.Authorization; using Bit.Core.NotificationCenter.Commands.Interfaces; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; @@ -16,16 +17,19 @@ public class MarkNotificationDeletedCommand : IMarkNotificationDeletedCommand private readonly IAuthorizationService _authorizationService; private readonly INotificationRepository _notificationRepository; private readonly INotificationStatusRepository _notificationStatusRepository; + private readonly IPushNotificationService _pushNotificationService; public MarkNotificationDeletedCommand(ICurrentContext currentContext, IAuthorizationService authorizationService, INotificationRepository notificationRepository, - INotificationStatusRepository notificationStatusRepository) + INotificationStatusRepository notificationStatusRepository, + IPushNotificationService pushNotificationService) { _currentContext = currentContext; _authorizationService = authorizationService; _notificationRepository = notificationRepository; _notificationStatusRepository = notificationStatusRepository; + _pushNotificationService = pushNotificationService; } public async Task MarkDeletedAsync(Guid notificationId) @@ -59,7 +63,9 @@ public class MarkNotificationDeletedCommand : IMarkNotificationDeletedCommand await _authorizationService.AuthorizeOrThrowAsync(_currentContext.HttpContext.User, notificationStatus, NotificationStatusOperations.Create); - await _notificationStatusRepository.CreateAsync(notificationStatus); + var newNotificationStatus = await _notificationStatusRepository.CreateAsync(notificationStatus); + + await _pushNotificationService.PushSyncNotificationCreateAsync(notification, newNotificationStatus); } else { @@ -69,6 +75,8 @@ public class MarkNotificationDeletedCommand : IMarkNotificationDeletedCommand notificationStatus.DeletedDate = DateTime.UtcNow; await _notificationStatusRepository.UpdateAsync(notificationStatus); + + await _pushNotificationService.PushSyncNotificationCreateAsync(notification, notificationStatus); } } } diff --git a/src/Core/NotificationCenter/Commands/MarkNotificationReadCommand.cs b/src/Core/NotificationCenter/Commands/MarkNotificationReadCommand.cs index 400e44463a..a0bfd271f2 100644 --- a/src/Core/NotificationCenter/Commands/MarkNotificationReadCommand.cs +++ b/src/Core/NotificationCenter/Commands/MarkNotificationReadCommand.cs @@ -5,6 +5,7 @@ using Bit.Core.NotificationCenter.Authorization; using Bit.Core.NotificationCenter.Commands.Interfaces; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; @@ -16,16 +17,19 @@ public class MarkNotificationReadCommand : IMarkNotificationReadCommand private readonly IAuthorizationService _authorizationService; private readonly INotificationRepository _notificationRepository; private readonly INotificationStatusRepository _notificationStatusRepository; + private readonly IPushNotificationService _pushNotificationService; public MarkNotificationReadCommand(ICurrentContext currentContext, IAuthorizationService authorizationService, INotificationRepository notificationRepository, - INotificationStatusRepository notificationStatusRepository) + INotificationStatusRepository notificationStatusRepository, + IPushNotificationService pushNotificationService) { _currentContext = currentContext; _authorizationService = authorizationService; _notificationRepository = notificationRepository; _notificationStatusRepository = notificationStatusRepository; + _pushNotificationService = pushNotificationService; } public async Task MarkReadAsync(Guid notificationId) @@ -59,7 +63,9 @@ public class MarkNotificationReadCommand : IMarkNotificationReadCommand await _authorizationService.AuthorizeOrThrowAsync(_currentContext.HttpContext.User, notificationStatus, NotificationStatusOperations.Create); - await _notificationStatusRepository.CreateAsync(notificationStatus); + var newNotificationStatus = await _notificationStatusRepository.CreateAsync(notificationStatus); + + await _pushNotificationService.PushSyncNotificationCreateAsync(notification, newNotificationStatus); } else { @@ -69,6 +75,8 @@ public class MarkNotificationReadCommand : IMarkNotificationReadCommand notificationStatus.ReadDate = DateTime.UtcNow; await _notificationStatusRepository.UpdateAsync(notificationStatus); + + await _pushNotificationService.PushSyncNotificationCreateAsync(notification, notificationStatus); } } } diff --git a/src/Core/NotificationCenter/Commands/UpdateNotificationCommand.cs b/src/Core/NotificationCenter/Commands/UpdateNotificationCommand.cs index f049478178..77af283353 100644 --- a/src/Core/NotificationCenter/Commands/UpdateNotificationCommand.cs +++ b/src/Core/NotificationCenter/Commands/UpdateNotificationCommand.cs @@ -5,6 +5,7 @@ using Bit.Core.NotificationCenter.Authorization; using Bit.Core.NotificationCenter.Commands.Interfaces; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; @@ -15,14 +16,17 @@ public class UpdateNotificationCommand : IUpdateNotificationCommand private readonly ICurrentContext _currentContext; private readonly IAuthorizationService _authorizationService; private readonly INotificationRepository _notificationRepository; + private readonly IPushNotificationService _pushNotificationService; public UpdateNotificationCommand(ICurrentContext currentContext, IAuthorizationService authorizationService, - INotificationRepository notificationRepository) + INotificationRepository notificationRepository, + IPushNotificationService pushNotificationService) { _currentContext = currentContext; _authorizationService = authorizationService; _notificationRepository = notificationRepository; + _pushNotificationService = pushNotificationService; } public async Task UpdateAsync(Notification notificationToUpdate) @@ -43,5 +47,7 @@ public class UpdateNotificationCommand : IUpdateNotificationCommand notification.RevisionDate = DateTime.UtcNow; await _notificationRepository.ReplaceAsync(notification); + + await _pushNotificationService.PushSyncNotificationCreateAsync(notification, null); } } diff --git a/src/Core/NotificationHub/NotificationHubPushNotificationService.cs b/src/Core/NotificationHub/NotificationHubPushNotificationService.cs index a30e20f8bc..2cdbb513a2 100644 --- a/src/Core/NotificationHub/NotificationHubPushNotificationService.cs +++ b/src/Core/NotificationHub/NotificationHubPushNotificationService.cs @@ -1,10 +1,12 @@ -using System.Text.Json; +#nullable enable +using System.Text.Json; using System.Text.RegularExpressions; using Bit.Core.Auth.Entities; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Models; using Bit.Core.Models.Data; +using Bit.Core.NotificationCenter.Entities; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Tools.Entities; @@ -50,7 +52,7 @@ public class NotificationHubPushNotificationService : IPushNotificationService await PushCipherAsync(cipher, PushType.SyncLoginDelete, null); } - private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable collectionIds) + private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable? collectionIds) { if (cipher.OrganizationId.HasValue) { @@ -180,7 +182,7 @@ public class NotificationHubPushNotificationService : IPushNotificationService await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse); } - public async Task PushSyncNotificationAsync(Notification notification) + public async Task PushSyncNotificationCreateAsync(Notification notification, NotificationStatus? notificationStatus) { var message = new SyncNotificationPushNotification { @@ -188,17 +190,44 @@ public class NotificationHubPushNotificationService : IPushNotificationService UserId = notification.UserId, OrganizationId = notification.OrganizationId, ClientType = notification.ClientType, - RevisionDate = notification.RevisionDate + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate }; if (notification.UserId.HasValue) { - await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotification, message, true, + await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationCreate, message, true, notification.ClientType); } else if (notification.OrganizationId.HasValue) { - await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotification, message, + await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationCreate, message, + true, notification.ClientType); + } + } + + public async Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) + { + var message = new SyncNotificationPushNotification + { + Id = notification.Id, + UserId = notification.UserId, + OrganizationId = notification.OrganizationId, + ClientType = notification.ClientType, + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate + }; + + if (notification.UserId.HasValue) + { + await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationUpdate, message, true, + notification.ClientType); + } + else if (notification.OrganizationId.HasValue) + { + await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationUpdate, message, true, notification.ClientType); } } @@ -224,8 +253,8 @@ public class NotificationHubPushNotificationService : IPushNotificationService GetContextIdentifier(excludeCurrentContext), clientType: clientType); } - public async Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + public async Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier, + string? deviceId = null, ClientType? clientType = null) { var tag = BuildTag($"template:payload_userId:{SanitizeTagInput(userId)}", identifier, clientType); await SendPayloadAsync(tag, type, payload); @@ -235,8 +264,8 @@ public class NotificationHubPushNotificationService : IPushNotificationService } } - public async Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + public async Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier, + string? deviceId = null, ClientType? clientType = null) { var tag = BuildTag($"template:payload && organizationId:{SanitizeTagInput(orgId)}", identifier, clientType); await SendPayloadAsync(tag, type, payload); @@ -246,7 +275,7 @@ public class NotificationHubPushNotificationService : IPushNotificationService } } - private string GetContextIdentifier(bool excludeCurrentContext) + private string? GetContextIdentifier(bool excludeCurrentContext) { if (!excludeCurrentContext) { @@ -254,11 +283,11 @@ public class NotificationHubPushNotificationService : IPushNotificationService } var currentContext = - _httpContextAccessor?.HttpContext?.RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; + _httpContextAccessor.HttpContext?.RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; return currentContext?.DeviceIdentifier; } - private string BuildTag(string tag, string identifier, ClientType? clientType) + private string BuildTag(string tag, string? identifier, ClientType? clientType) { if (!string.IsNullOrWhiteSpace(identifier)) { diff --git a/src/Core/Services/IPushNotificationService.cs b/src/Core/Services/IPushNotificationService.cs index 9cf8f4f876..d40f7beeb0 100644 --- a/src/Core/Services/IPushNotificationService.cs +++ b/src/Core/Services/IPushNotificationService.cs @@ -1,4 +1,5 @@ -using Bit.Core.Auth.Entities; +#nullable enable +using Bit.Core.Auth.Entities; using Bit.Core.Enums; using Bit.Core.NotificationCenter.Entities; using Bit.Core.Tools.Entities; @@ -23,13 +24,14 @@ public interface IPushNotificationService Task PushSyncSendCreateAsync(Send send); Task PushSyncSendUpdateAsync(Send send); Task PushSyncSendDeleteAsync(Send send); - Task PushSyncNotificationAsync(Notification notification); + Task PushSyncNotificationCreateAsync(Notification notification, NotificationStatus? notificationStatus); + Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus); Task PushAuthRequestAsync(AuthRequest authRequest); Task PushAuthRequestResponseAsync(AuthRequest authRequest); Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null); + string? deviceId = null, ClientType? clientType = null); Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null); + string? deviceId = null, ClientType? clientType = null); } diff --git a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs index 9b45d49187..a254887edf 100644 --- a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs +++ b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +#nullable enable +using System.Text.Json; using Azure.Storage.Queues; using Bit.Core.Auth.Entities; using Bit.Core.Context; @@ -41,7 +42,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService await PushCipherAsync(cipher, PushType.SyncLoginDelete, null); } - private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable collectionIds) + private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable? collectionIds) { if (cipher.OrganizationId.HasValue) { @@ -164,7 +165,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService await PushSendAsync(send, PushType.SyncSendDelete); } - public async Task PushSyncNotificationAsync(Notification notification) + public async Task PushSyncNotificationCreateAsync(Notification notification, NotificationStatus? notificationStatus) { var message = new SyncNotificationPushNotification { @@ -172,10 +173,28 @@ public class AzureQueuePushNotificationService : IPushNotificationService UserId = notification.UserId, OrganizationId = notification.OrganizationId, ClientType = notification.ClientType, - RevisionDate = notification.RevisionDate + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate }; - await SendMessageAsync(PushType.SyncNotification, message, true); + await SendMessageAsync(PushType.SyncNotificationCreate, message, true); + } + + public async Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) + { + var message = new SyncNotificationPushNotification + { + Id = notification.Id, + UserId = notification.UserId, + OrganizationId = notification.OrganizationId, + ClientType = notification.ClientType, + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate + }; + + await SendMessageAsync(PushType.SyncNotificationUpdate, message, true); } private async Task PushSendAsync(Send send, PushType type) @@ -201,7 +220,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService await _queueClient.SendMessageAsync(message); } - private string GetContextIdentifier(bool excludeCurrentContext) + private string? GetContextIdentifier(bool excludeCurrentContext) { if (!excludeCurrentContext) { @@ -214,14 +233,14 @@ public class AzureQueuePushNotificationService : IPushNotificationService } public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { // Noop return Task.FromResult(0); } public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { // Noop return Task.FromResult(0); diff --git a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs index b5272f55be..2f321348a8 100644 --- a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs +++ b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs @@ -1,4 +1,5 @@ -using Bit.Core.Auth.Entities; +#nullable enable +using Bit.Core.Auth.Entities; using Bit.Core.Enums; using Bit.Core.NotificationCenter.Entities; using Bit.Core.Settings; @@ -11,7 +12,7 @@ namespace Bit.Core.Services; public class MultiServicePushNotificationService : IPushNotificationService { - private readonly IEnumerable _services; + private readonly IEnumerable? _services; private readonly ILogger _logger; public MultiServicePushNotificationService( @@ -23,7 +24,7 @@ public class MultiServicePushNotificationService : IPushNotificationService _logger = logger; _logger.LogInformation("Hub services: {Services}", _services.Count()); - globalSettings?.NotificationHubPool?.NotificationHubs?.ForEach(hub => + globalSettings.NotificationHubPool?.NotificationHubs?.ForEach(hub => { _logger.LogInformation("HubName: {HubName}, EnableSendTracing: {EnableSendTracing}, RegistrationStartDate: {RegistrationStartDate}, RegistrationEndDate: {RegistrationEndDate}", hub.HubName, hub.EnableSendTracing, hub.RegistrationStartDate, hub.RegistrationEndDate); }); @@ -132,33 +133,43 @@ public class MultiServicePushNotificationService : IPushNotificationService } public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { PushToServices((s) => s.SendPayloadToUserAsync(userId, type, payload, identifier, deviceId, clientType)); return Task.FromResult(0); } public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { PushToServices((s) => s.SendPayloadToOrganizationAsync(orgId, type, payload, identifier, deviceId, clientType)); return Task.FromResult(0); } - public Task PushSyncNotificationAsync(Notification notification) + public Task PushSyncNotificationCreateAsync(Notification notification, NotificationStatus? notificationStatus) { - PushToServices((s) => s.PushSyncNotificationAsync(notification)); + PushToServices((s) => s.PushSyncNotificationCreateAsync(notification, notificationStatus)); + return Task.CompletedTask; + } + + public Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) + { + PushToServices((s) => s.PushSyncNotificationUpdateAsync(notification, notificationStatus)); return Task.CompletedTask; } private void PushToServices(Func pushFunc) { - if (_services != null) + if (_services == null) { - foreach (var service in _services) - { - pushFunc(service); - } + _logger.LogWarning("No services found to push notification"); + return; + } + + foreach (var service in _services) + { + _logger.LogDebug("Pushing notification to service {}", service.GetType().Name); + pushFunc(service); } } } diff --git a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs index 0d114f88b4..65f4a8e1c8 100644 --- a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs @@ -1,4 +1,5 @@ -using Bit.Core.Auth.Entities; +#nullable enable +using Bit.Core.Auth.Entities; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Models; @@ -13,7 +14,6 @@ namespace Bit.Core.Services; public class NotificationsApiPushNotificationService : BaseIdentityClientService, IPushNotificationService { - private readonly GlobalSettings _globalSettings; private readonly IHttpContextAccessor _httpContextAccessor; public NotificationsApiPushNotificationService( @@ -30,7 +30,6 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService globalSettings.InternalIdentityKey, logger) { - _globalSettings = globalSettings; _httpContextAccessor = httpContextAccessor; } @@ -49,7 +48,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService await PushCipherAsync(cipher, PushType.SyncLoginDelete, null); } - private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable collectionIds) + private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable? collectionIds) { if (cipher.OrganizationId.HasValue) { @@ -173,7 +172,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService await PushSendAsync(send, PushType.SyncSendDelete); } - public async Task PushSyncNotificationAsync(Notification notification) + public async Task PushSyncNotificationCreateAsync(Notification notification, NotificationStatus? notificationStatus) { var message = new SyncNotificationPushNotification { @@ -181,10 +180,28 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService UserId = notification.UserId, OrganizationId = notification.OrganizationId, ClientType = notification.ClientType, - RevisionDate = notification.RevisionDate + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate }; - await SendMessageAsync(PushType.SyncNotification, message, true); + await SendMessageAsync(PushType.SyncNotificationCreate, message, true); + } + + public async Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) + { + var message = new SyncNotificationPushNotification + { + Id = notification.Id, + UserId = notification.UserId, + OrganizationId = notification.OrganizationId, + ClientType = notification.ClientType, + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate + }; + + await SendMessageAsync(PushType.SyncNotificationUpdate, message, true); } private async Task PushSendAsync(Send send, PushType type) @@ -209,7 +226,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService await SendAsync(HttpMethod.Post, "send", request); } - private string GetContextIdentifier(bool excludeCurrentContext) + private string? GetContextIdentifier(bool excludeCurrentContext) { if (!excludeCurrentContext) { @@ -217,19 +234,19 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService } var currentContext = - _httpContextAccessor?.HttpContext?.RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; + _httpContextAccessor.HttpContext?.RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; return currentContext?.DeviceIdentifier; } public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { // Noop return Task.FromResult(0); } public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { // Noop return Task.FromResult(0); diff --git a/src/Core/Services/Implementations/RelayPushNotificationService.cs b/src/Core/Services/Implementations/RelayPushNotificationService.cs index 9d548440bd..ac52c07743 100644 --- a/src/Core/Services/Implementations/RelayPushNotificationService.cs +++ b/src/Core/Services/Implementations/RelayPushNotificationService.cs @@ -1,4 +1,5 @@ -using Bit.Core.Auth.Entities; +#nullable enable +using Bit.Core.Auth.Entities; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.IdentityServer; @@ -53,7 +54,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti await PushCipherAsync(cipher, PushType.SyncLoginDelete, null); } - private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable collectionIds) + private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable? collectionIds) { if (cipher.OrganizationId.HasValue) { @@ -189,7 +190,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti await SendPayloadToUserAsync(authRequest.UserId, type, message, true); } - public async Task PushSyncNotificationAsync(Notification notification) + public async Task PushSyncNotificationCreateAsync(Notification notification, NotificationStatus? notificationStatus) { var message = new SyncNotificationPushNotification { @@ -197,17 +198,44 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti UserId = notification.UserId, OrganizationId = notification.OrganizationId, ClientType = notification.ClientType, - RevisionDate = notification.RevisionDate + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate }; if (notification.UserId.HasValue) { - await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotification, message, true, + await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationCreate, message, true, notification.ClientType); } else if (notification.OrganizationId.HasValue) { - await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotification, message, + await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationCreate, message, + true, notification.ClientType); + } + } + + public async Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) + { + var message = new SyncNotificationPushNotification + { + Id = notification.Id, + UserId = notification.UserId, + OrganizationId = notification.OrganizationId, + ClientType = notification.ClientType, + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate + }; + + if (notification.UserId.HasValue) + { + await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationUpdate, message, true, + notification.ClientType); + } + else if (notification.OrganizationId.HasValue) + { + await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationUpdate, message, true, notification.ClientType); } } @@ -245,7 +273,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti private async Task AddCurrentContextAsync(PushSendRequestModel request, bool addIdentifier) { var currentContext = - _httpContextAccessor?.HttpContext?.RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; + _httpContextAccessor.HttpContext?.RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; if (!string.IsNullOrWhiteSpace(currentContext?.DeviceIdentifier)) { var device = await _deviceRepository.GetByIdentifierAsync(currentContext.DeviceIdentifier); @@ -262,13 +290,13 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti } public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { throw new NotImplementedException(); } public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { throw new NotImplementedException(); } diff --git a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs index d55a83c470..e202d29d73 100644 --- a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs +++ b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs @@ -1,4 +1,5 @@ -using Bit.Core.Auth.Entities; +#nullable enable +using Bit.Core.Auth.Entities; using Bit.Core.Enums; using Bit.Core.NotificationCenter.Entities; using Bit.Core.Tools.Entities; @@ -84,7 +85,7 @@ public class NoopPushNotificationService : IPushNotificationService } public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { return Task.FromResult(0); } @@ -100,10 +101,12 @@ public class NoopPushNotificationService : IPushNotificationService } public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, - string deviceId = null, ClientType? clientType = null) + string? deviceId = null, ClientType? clientType = null) { return Task.FromResult(0); } - public Task PushSyncNotificationAsync(Notification notification) => Task.CompletedTask; + public Task PushSyncNotificationCreateAsync(Notification notification, NotificationStatus? notificationStatus) => Task.CompletedTask; + + public Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) => Task.CompletedTask; } diff --git a/src/Notifications/HubHelpers.cs b/src/Notifications/HubHelpers.cs index 25f43138e9..7047db0734 100644 --- a/src/Notifications/HubHelpers.cs +++ b/src/Notifications/HubHelpers.cs @@ -89,7 +89,8 @@ public static class HubHelpers await hubContext.Clients.User(authRequestNotification.Payload.UserId.ToString()) .SendAsync(_receiveMessageMethod, authRequestNotification, cancellationToken); break; - case PushType.SyncNotification: + case PushType.SyncNotificationCreate: + case PushType.SyncNotificationUpdate: var syncNotification = JsonSerializer.Deserialize>( notificationJson, _deserializerOptions); diff --git a/test/Core.Test/NotificationCenter/Commands/CreateNotificationCommandTest.cs b/test/Core.Test/NotificationCenter/Commands/CreateNotificationCommandTest.cs index 0ccf4995ce..d0a2deb790 100644 --- a/test/Core.Test/NotificationCenter/Commands/CreateNotificationCommandTest.cs +++ b/test/Core.Test/NotificationCenter/Commands/CreateNotificationCommandTest.cs @@ -41,6 +41,9 @@ public class CreateNotificationCommandTest Setup(sutProvider, notification, authorized: false); await Assert.ThrowsAsync(() => sutProvider.Sut.CreateAsync(notification)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -58,6 +61,6 @@ public class CreateNotificationCommandTest Assert.Equal(notification.CreationDate, notification.RevisionDate); await sutProvider.GetDependency() .Received(1) - .PushSyncNotificationAsync(newNotification); + .PushSyncNotificationCreateAsync(newNotification, null); } } diff --git a/test/Core.Test/NotificationCenter/Commands/CreateNotificationStatusCommandTest.cs b/test/Core.Test/NotificationCenter/Commands/CreateNotificationStatusCommandTest.cs index 8dc8524926..efe9d4236c 100644 --- a/test/Core.Test/NotificationCenter/Commands/CreateNotificationStatusCommandTest.cs +++ b/test/Core.Test/NotificationCenter/Commands/CreateNotificationStatusCommandTest.cs @@ -5,6 +5,7 @@ using Bit.Core.NotificationCenter.Authorization; using Bit.Core.NotificationCenter.Commands; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Test.NotificationCenter.AutoFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -50,6 +51,9 @@ public class CreateNotificationStatusCommandTest Setup(sutProvider, notification: null, notificationStatus, true, true); await Assert.ThrowsAsync(() => sutProvider.Sut.CreateAsync(notificationStatus)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -61,6 +65,9 @@ public class CreateNotificationStatusCommandTest Setup(sutProvider, notification, notificationStatus, authorizedNotification: false, true); await Assert.ThrowsAsync(() => sutProvider.Sut.CreateAsync(notificationStatus)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -72,6 +79,9 @@ public class CreateNotificationStatusCommandTest Setup(sutProvider, notification, notificationStatus, true, authorizedCreate: false); await Assert.ThrowsAsync(() => sutProvider.Sut.CreateAsync(notificationStatus)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -85,5 +95,8 @@ public class CreateNotificationStatusCommandTest var newNotificationStatus = await sutProvider.Sut.CreateAsync(notificationStatus); Assert.Equal(notificationStatus, newNotificationStatus); + await sutProvider.GetDependency() + .Received(1) + .PushSyncNotificationCreateAsync(notification, notificationStatus); } } diff --git a/test/Core.Test/NotificationCenter/Commands/MarkNotificationDeletedCommandTest.cs b/test/Core.Test/NotificationCenter/Commands/MarkNotificationDeletedCommandTest.cs index a5bb20423c..adcdfa44bf 100644 --- a/test/Core.Test/NotificationCenter/Commands/MarkNotificationDeletedCommandTest.cs +++ b/test/Core.Test/NotificationCenter/Commands/MarkNotificationDeletedCommandTest.cs @@ -6,6 +6,7 @@ using Bit.Core.NotificationCenter.Authorization; using Bit.Core.NotificationCenter.Commands; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Test.NotificationCenter.AutoFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -63,6 +64,9 @@ public class MarkNotificationDeletedCommandTest Setup(sutProvider, notificationId, userId: null, notification, notificationStatus, true, true, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkDeletedAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -74,6 +78,9 @@ public class MarkNotificationDeletedCommandTest Setup(sutProvider, notificationId, userId, notification: null, notificationStatus, true, true, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkDeletedAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -86,6 +93,9 @@ public class MarkNotificationDeletedCommandTest true, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkDeletedAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -98,6 +108,9 @@ public class MarkNotificationDeletedCommandTest authorizedCreate: false, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkDeletedAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -110,6 +123,9 @@ public class MarkNotificationDeletedCommandTest authorizedUpdate: false); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkDeletedAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -119,13 +135,22 @@ public class MarkNotificationDeletedCommandTest Guid notificationId, Guid userId, Notification notification) { Setup(sutProvider, notificationId, userId, notification, notificationStatus: null, true, true, true); + var expectedNotificationStatus = new NotificationStatus + { + NotificationId = notificationId, + UserId = userId, + ReadDate = null, + DeletedDate = DateTime.UtcNow + }; await sutProvider.Sut.MarkDeletedAsync(notificationId); await sutProvider.GetDependency().Received(1) - .CreateAsync(Arg.Is(ns => - ns.NotificationId == notificationId && ns.UserId == userId && !ns.ReadDate.HasValue && - ns.DeletedDate.HasValue && DateTime.UtcNow - ns.DeletedDate.Value < TimeSpan.FromMinutes(1))); + .CreateAsync(Arg.Do(ns => AssertNotificationStatus(expectedNotificationStatus, ns))); + await sutProvider.GetDependency() + .Received(1) + .PushSyncNotificationCreateAsync(notification, + Arg.Do(ns => AssertNotificationStatus(expectedNotificationStatus, ns))); } [Theory] @@ -134,18 +159,27 @@ public class MarkNotificationDeletedCommandTest SutProvider sutProvider, Guid notificationId, Guid userId, Notification notification, NotificationStatus notificationStatus) { - var deletedDate = notificationStatus.DeletedDate; - Setup(sutProvider, notificationId, userId, notification, notificationStatus, true, true, true); await sutProvider.Sut.MarkDeletedAsync(notificationId); await sutProvider.GetDependency().Received(1) - .UpdateAsync(Arg.Is(ns => - ns.Equals(notificationStatus) && - ns.NotificationId == notificationStatus.NotificationId && ns.UserId == notificationStatus.UserId && - ns.ReadDate == notificationStatus.ReadDate && ns.DeletedDate != deletedDate && - ns.DeletedDate.HasValue && - DateTime.UtcNow - ns.DeletedDate.Value < TimeSpan.FromMinutes(1))); + .UpdateAsync(Arg.Do(ns => AssertNotificationStatus(notificationStatus, ns))); + await sutProvider.GetDependency() + .Received(1) + .PushSyncNotificationCreateAsync(notification, + Arg.Do(ns => AssertNotificationStatus(notificationStatus, ns))); + } + + private static void AssertNotificationStatus(NotificationStatus expectedNotificationStatus, + NotificationStatus? actualNotificationStatus) + { + Assert.NotNull(actualNotificationStatus); + Assert.Equal(expectedNotificationStatus.NotificationId, actualNotificationStatus.NotificationId); + Assert.Equal(expectedNotificationStatus.UserId, actualNotificationStatus.UserId); + Assert.Equal(expectedNotificationStatus.ReadDate, actualNotificationStatus.ReadDate); + Assert.NotEqual(expectedNotificationStatus.DeletedDate, actualNotificationStatus.DeletedDate); + Assert.NotNull(actualNotificationStatus.DeletedDate); + Assert.Equal(DateTime.UtcNow, actualNotificationStatus.DeletedDate.Value, TimeSpan.FromMinutes(1)); } } diff --git a/test/Core.Test/NotificationCenter/Commands/MarkNotificationReadCommandTest.cs b/test/Core.Test/NotificationCenter/Commands/MarkNotificationReadCommandTest.cs index f80234c075..cb607fe159 100644 --- a/test/Core.Test/NotificationCenter/Commands/MarkNotificationReadCommandTest.cs +++ b/test/Core.Test/NotificationCenter/Commands/MarkNotificationReadCommandTest.cs @@ -6,6 +6,7 @@ using Bit.Core.NotificationCenter.Authorization; using Bit.Core.NotificationCenter.Commands; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Test.NotificationCenter.AutoFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -63,6 +64,9 @@ public class MarkNotificationReadCommandTest Setup(sutProvider, notificationId, userId: null, notification, notificationStatus, true, true, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkReadAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -74,6 +78,9 @@ public class MarkNotificationReadCommandTest Setup(sutProvider, notificationId, userId, notification: null, notificationStatus, true, true, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkReadAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -86,6 +93,9 @@ public class MarkNotificationReadCommandTest true, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkReadAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -98,6 +108,9 @@ public class MarkNotificationReadCommandTest authorizedCreate: false, true); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkReadAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -110,6 +123,9 @@ public class MarkNotificationReadCommandTest authorizedUpdate: false); await Assert.ThrowsAsync(() => sutProvider.Sut.MarkReadAsync(notificationId)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -119,13 +135,22 @@ public class MarkNotificationReadCommandTest Guid notificationId, Guid userId, Notification notification) { Setup(sutProvider, notificationId, userId, notification, notificationStatus: null, true, true, true); + var expectedNotificationStatus = new NotificationStatus + { + NotificationId = notificationId, + UserId = userId, + ReadDate = DateTime.UtcNow, + DeletedDate = null + }; await sutProvider.Sut.MarkReadAsync(notificationId); await sutProvider.GetDependency().Received(1) - .CreateAsync(Arg.Is(ns => - ns.NotificationId == notificationId && ns.UserId == userId && !ns.DeletedDate.HasValue && - ns.ReadDate.HasValue && DateTime.UtcNow - ns.ReadDate.Value < TimeSpan.FromMinutes(1))); + .CreateAsync(Arg.Do(ns => AssertNotificationStatus(expectedNotificationStatus, ns))); + await sutProvider.GetDependency() + .Received(1) + .PushSyncNotificationCreateAsync(notification, + Arg.Do(ns => AssertNotificationStatus(expectedNotificationStatus, ns))); } [Theory] @@ -134,18 +159,27 @@ public class MarkNotificationReadCommandTest SutProvider sutProvider, Guid notificationId, Guid userId, Notification notification, NotificationStatus notificationStatus) { - var readDate = notificationStatus.ReadDate; - Setup(sutProvider, notificationId, userId, notification, notificationStatus, true, true, true); await sutProvider.Sut.MarkReadAsync(notificationId); await sutProvider.GetDependency().Received(1) - .UpdateAsync(Arg.Is(ns => - ns.Equals(notificationStatus) && - ns.NotificationId == notificationStatus.NotificationId && ns.UserId == notificationStatus.UserId && - ns.DeletedDate == notificationStatus.DeletedDate && ns.ReadDate != readDate && - ns.ReadDate.HasValue && - DateTime.UtcNow - ns.ReadDate.Value < TimeSpan.FromMinutes(1))); + .UpdateAsync(Arg.Do(ns => AssertNotificationStatus(notificationStatus, ns))); + await sutProvider.GetDependency() + .Received(1) + .PushSyncNotificationCreateAsync(notification, + Arg.Do(ns => AssertNotificationStatus(notificationStatus, ns))); + } + + private static void AssertNotificationStatus(NotificationStatus expectedNotificationStatus, + NotificationStatus? actualNotificationStatus) + { + Assert.NotNull(actualNotificationStatus); + Assert.Equal(expectedNotificationStatus.NotificationId, actualNotificationStatus.NotificationId); + Assert.Equal(expectedNotificationStatus.UserId, actualNotificationStatus.UserId); + Assert.NotEqual(expectedNotificationStatus.ReadDate, actualNotificationStatus.ReadDate); + Assert.NotNull(actualNotificationStatus.ReadDate); + Assert.Equal(DateTime.UtcNow, actualNotificationStatus.ReadDate.Value, TimeSpan.FromMinutes(1)); + Assert.Equal(expectedNotificationStatus.DeletedDate, actualNotificationStatus.DeletedDate); } } diff --git a/test/Core.Test/NotificationCenter/Commands/UpdateNotificationCommandTest.cs b/test/Core.Test/NotificationCenter/Commands/UpdateNotificationCommandTest.cs index 976d1d77a3..2cd8e2a40e 100644 --- a/test/Core.Test/NotificationCenter/Commands/UpdateNotificationCommandTest.cs +++ b/test/Core.Test/NotificationCenter/Commands/UpdateNotificationCommandTest.cs @@ -7,6 +7,7 @@ using Bit.Core.NotificationCenter.Commands; using Bit.Core.NotificationCenter.Entities; using Bit.Core.NotificationCenter.Enums; using Bit.Core.NotificationCenter.Repositories; +using Bit.Core.Services; using Bit.Core.Test.NotificationCenter.AutoFixture; using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; @@ -45,6 +46,9 @@ public class UpdateNotificationCommandTest Setup(sutProvider, notification.Id, notification: null, true); await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(notification)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -56,6 +60,9 @@ public class UpdateNotificationCommandTest Setup(sutProvider, notification.Id, notification, authorized: false); await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(notification)); + await sutProvider.GetDependency() + .Received(0) + .PushSyncNotificationCreateAsync(Arg.Any(), Arg.Any()); } [Theory] @@ -91,5 +98,8 @@ public class UpdateNotificationCommandTest n.Priority == notificationToUpdate.Priority && n.ClientType == notificationToUpdate.ClientType && n.Title == notificationToUpdate.Title && n.Body == notificationToUpdate.Body && DateTime.UtcNow - n.RevisionDate < TimeSpan.FromMinutes(1))); + await sutProvider.GetDependency() + .Received(1) + .PushSyncNotificationCreateAsync(notification, null); } } diff --git a/test/Core.Test/NotificationHub/NotificationHubPushNotificationServiceTests.cs b/test/Core.Test/NotificationHub/NotificationHubPushNotificationServiceTests.cs index dc391b9801..9cf943aa3f 100644 --- a/test/Core.Test/NotificationHub/NotificationHubPushNotificationServiceTests.cs +++ b/test/Core.Test/NotificationHub/NotificationHubPushNotificationServiceTests.cs @@ -15,15 +15,19 @@ using Xunit; namespace Bit.Core.Test.NotificationHub; [SutProviderCustomize] +[NotificationStatusCustomize] public class NotificationHubPushNotificationServiceTests { [Theory] - [BitAutoData] + [BitAutoData(false)] + [BitAutoData(true)] [NotificationCustomize] - public async void PushSyncNotificationAsync_Global_NotSent( - SutProvider sutProvider, Notification notification) + public async Task PushSyncNotificationCreateAsync_Global_NotSent(bool notificationStatusNull, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) { - await sutProvider.Sut.PushSyncNotificationAsync(notification); + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, + notificationStatusNull ? null : notificationStatus); await sutProvider.GetDependency() .Received(0) @@ -36,12 +40,15 @@ public class NotificationHubPushNotificationServiceTests } [Theory] - [BitAutoData(false)] - [BitAutoData(true)] + [BitAutoData(false, false)] + [BitAutoData(false, true)] + [BitAutoData(true, false)] + [BitAutoData(true, true)] [NotificationCustomize(false)] - public async void PushSyncNotificationAsync_UserIdProvidedClientTypeAll_SentToUser( - bool organizationIdNull, SutProvider sutProvider, - Notification notification) + public async Task PushSyncNotificationCreateAsync_UserIdProvidedClientTypeAll_SentToUser( + bool organizationIdNull, bool notificationStatusNull, + SutProvider sutProvider, + Notification notification, NotificationStatus notificationStatus) { if (organizationIdNull) { @@ -49,11 +56,13 @@ public class NotificationHubPushNotificationServiceTests } notification.ClientType = ClientType.All; - var expectedSyncNotification = ToSyncNotificationPushNotification(notification); + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); - await sutProvider.Sut.PushSyncNotificationAsync(notification); + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, expectedNotificationStatus); - await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotification, expectedSyncNotification, + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationCreate, + expectedSyncNotification, $"(template:payload_userId:{notification.UserId})"); await sutProvider.GetDependency() .Received(0) @@ -70,21 +79,20 @@ public class NotificationHubPushNotificationServiceTests [BitAutoData(true, ClientType.Web)] [BitAutoData(true, ClientType.Mobile)] [NotificationCustomize(false)] - public async void PushSyncNotificationAsync_UserIdProvidedClientTypeNotAll_SentToUser(bool organizationIdNull, - ClientType clientType, SutProvider sutProvider, - Notification notification) + public async Task PushSyncNotificationCreateAsync_UserIdProvidedOrganizationIdNullClientTypeNotAll_SentToUser( + bool notificationStatusNull, ClientType clientType, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) { - if (organizationIdNull) - { - notification.OrganizationId = null; - } - + notification.OrganizationId = null; notification.ClientType = clientType; - var expectedSyncNotification = ToSyncNotificationPushNotification(notification); + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); - await sutProvider.Sut.PushSyncNotificationAsync(notification); + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, expectedNotificationStatus); - await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotification, expectedSyncNotification, + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationCreate, + expectedSyncNotification, $"(template:payload_userId:{notification.UserId} && clientType:{clientType})"); await sutProvider.GetDependency() .Received(0) @@ -92,18 +100,51 @@ public class NotificationHubPushNotificationServiceTests } [Theory] - [BitAutoData] + [BitAutoData(false, ClientType.Browser)] + [BitAutoData(false, ClientType.Desktop)] + [BitAutoData(false, ClientType.Web)] + [BitAutoData(false, ClientType.Mobile)] + [BitAutoData(true, ClientType.Browser)] + [BitAutoData(true, ClientType.Desktop)] + [BitAutoData(true, ClientType.Web)] + [BitAutoData(true, ClientType.Mobile)] [NotificationCustomize(false)] - public async void PushSyncNotificationAsync_UserIdNullOrganizationIdProvidedClientTypeAll_SentToOrganization( - SutProvider sutProvider, Notification notification) + public async Task PushSyncNotificationCreateAsync_UserIdProvidedOrganizationIdProvidedClientTypeNotAll_SentToUser( + bool notificationStatusNull, ClientType clientType, + SutProvider sutProvider, + Notification notification, NotificationStatus notificationStatus) + { + notification.ClientType = clientType; + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); + + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, expectedNotificationStatus); + + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationCreate, + expectedSyncNotification, + $"(template:payload_userId:{notification.UserId} && clientType:{clientType})"); + await sutProvider.GetDependency() + .Received(0) + .UpsertAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(false)] + [BitAutoData(true)] + [NotificationCustomize(false)] + public async Task PushSyncNotificationCreateAsync_UserIdNullOrganizationIdProvidedClientTypeAll_SentToOrganization( + bool notificationStatusNull, SutProvider sutProvider, + Notification notification, NotificationStatus notificationStatus) { notification.UserId = null; notification.ClientType = ClientType.All; - var expectedSyncNotification = ToSyncNotificationPushNotification(notification); + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); - await sutProvider.Sut.PushSyncNotificationAsync(notification); + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, expectedNotificationStatus); - await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotification, expectedSyncNotification, + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationCreate, + expectedSyncNotification, $"(template:payload && organizationId:{notification.OrganizationId})"); await sutProvider.GetDependency() .Received(0) @@ -111,23 +152,194 @@ public class NotificationHubPushNotificationServiceTests } [Theory] - [BitAutoData(ClientType.Browser)] - [BitAutoData(ClientType.Desktop)] - [BitAutoData(ClientType.Web)] - [BitAutoData(ClientType.Mobile)] + [BitAutoData(false, ClientType.Browser)] + [BitAutoData(false, ClientType.Desktop)] + [BitAutoData(false, ClientType.Web)] + [BitAutoData(false, ClientType.Mobile)] + [BitAutoData(true, ClientType.Browser)] + [BitAutoData(true, ClientType.Desktop)] + [BitAutoData(true, ClientType.Web)] + [BitAutoData(true, ClientType.Mobile)] [NotificationCustomize(false)] - public async void PushSyncNotificationAsync_UserIdNullOrganizationIdProvidedClientTypeNotAll_SentToOrganization( - ClientType clientType, SutProvider sutProvider, - Notification notification) + public async Task + PushSyncNotificationCreateAsync_UserIdNullOrganizationIdProvidedClientTypeNotAll_SentToOrganization( + bool notificationStatusNull, ClientType clientType, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) { notification.UserId = null; notification.ClientType = clientType; + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); - var expectedSyncNotification = ToSyncNotificationPushNotification(notification); + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, expectedNotificationStatus); - await sutProvider.Sut.PushSyncNotificationAsync(notification); + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationCreate, + expectedSyncNotification, + $"(template:payload && organizationId:{notification.OrganizationId} && clientType:{clientType})"); + await sutProvider.GetDependency() + .Received(0) + .UpsertAsync(Arg.Any()); + } - await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotification, expectedSyncNotification, + [Theory] + [BitAutoData(false)] + [BitAutoData(true)] + [NotificationCustomize] + public async Task PushSyncNotificationUpdateAsync_Global_NotSent(bool notificationStatusNull, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) + { + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, + notificationStatusNull ? null : notificationStatus); + + await sutProvider.GetDependency() + .Received(0) + .AllClients + .Received(0) + .SendTemplateNotificationAsync(Arg.Any>(), Arg.Any()); + await sutProvider.GetDependency() + .Received(0) + .UpsertAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(false, false)] + [BitAutoData(false, true)] + [BitAutoData(true, false)] + [BitAutoData(true, true)] + [NotificationCustomize(false)] + public async Task PushSyncNotificationUpdateAsync_UserIdProvidedClientTypeAll_SentToUser( + bool organizationIdNull, bool notificationStatusNull, + SutProvider sutProvider, + Notification notification, NotificationStatus notificationStatus) + { + if (organizationIdNull) + { + notification.OrganizationId = null; + } + + notification.ClientType = ClientType.All; + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); + + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus); + + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationUpdate, + expectedSyncNotification, + $"(template:payload_userId:{notification.UserId})"); + await sutProvider.GetDependency() + .Received(0) + .UpsertAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(false, ClientType.Browser)] + [BitAutoData(false, ClientType.Desktop)] + [BitAutoData(false, ClientType.Web)] + [BitAutoData(false, ClientType.Mobile)] + [BitAutoData(true, ClientType.Browser)] + [BitAutoData(true, ClientType.Desktop)] + [BitAutoData(true, ClientType.Web)] + [BitAutoData(true, ClientType.Mobile)] + [NotificationCustomize(false)] + public async Task PushSyncNotificationUpdateAsync_UserIdProvidedOrganizationIdNullClientTypeNotAll_SentToUser( + bool notificationStatusNull, ClientType clientType, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) + { + notification.OrganizationId = null; + notification.ClientType = clientType; + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); + + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus); + + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationUpdate, + expectedSyncNotification, + $"(template:payload_userId:{notification.UserId} && clientType:{clientType})"); + await sutProvider.GetDependency() + .Received(0) + .UpsertAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(false, ClientType.Browser)] + [BitAutoData(false, ClientType.Desktop)] + [BitAutoData(false, ClientType.Web)] + [BitAutoData(false, ClientType.Mobile)] + [BitAutoData(true, ClientType.Browser)] + [BitAutoData(true, ClientType.Desktop)] + [BitAutoData(true, ClientType.Web)] + [BitAutoData(true, ClientType.Mobile)] + [NotificationCustomize(false)] + public async Task PushSyncNotificationUpdateAsync_UserIdProvidedOrganizationIdProvidedClientTypeNotAll_SentToUser( + bool notificationStatusNull, ClientType clientType, + SutProvider sutProvider, + Notification notification, NotificationStatus notificationStatus) + { + notification.ClientType = clientType; + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); + + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus); + + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationUpdate, + expectedSyncNotification, + $"(template:payload_userId:{notification.UserId} && clientType:{clientType})"); + await sutProvider.GetDependency() + .Received(0) + .UpsertAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(false)] + [BitAutoData(true)] + [NotificationCustomize(false)] + public async Task PushSyncNotificationUpdateAsync_UserIdNullOrganizationIdProvidedClientTypeAll_SentToOrganization( + bool notificationStatusNull, SutProvider sutProvider, + Notification notification, NotificationStatus notificationStatus) + { + notification.UserId = null; + notification.ClientType = ClientType.All; + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); + + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus); + + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationUpdate, + expectedSyncNotification, + $"(template:payload && organizationId:{notification.OrganizationId})"); + await sutProvider.GetDependency() + .Received(0) + .UpsertAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(false, ClientType.Browser)] + [BitAutoData(false, ClientType.Desktop)] + [BitAutoData(false, ClientType.Web)] + [BitAutoData(false, ClientType.Mobile)] + [BitAutoData(true, ClientType.Browser)] + [BitAutoData(true, ClientType.Desktop)] + [BitAutoData(true, ClientType.Web)] + [BitAutoData(true, ClientType.Mobile)] + [NotificationCustomize(false)] + public async Task + PushSyncNotificationUpdateAsync_UserIdNullOrganizationIdProvidedClientTypeNotAll_SentToOrganization( + bool notificationStatusNull, ClientType clientType, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) + { + notification.UserId = null; + notification.ClientType = clientType; + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus); + + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus); + + await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationUpdate, + expectedSyncNotification, $"(template:payload && organizationId:{notification.OrganizationId} && clientType:{clientType})"); await sutProvider.GetDependency() .Received(0) @@ -137,7 +349,7 @@ public class NotificationHubPushNotificationServiceTests [Theory] [BitAutoData([null])] [BitAutoData(ClientType.All)] - public async void SendPayloadToUserAsync_ClientTypeNullOrAll_SentToUser(ClientType? clientType, + public async Task SendPayloadToUserAsync_ClientTypeNullOrAll_SentToUser(ClientType? clientType, SutProvider sutProvider, Guid userId, PushType pushType, string payload, string identifier) { @@ -156,7 +368,7 @@ public class NotificationHubPushNotificationServiceTests [BitAutoData(ClientType.Desktop)] [BitAutoData(ClientType.Mobile)] [BitAutoData(ClientType.Web)] - public async void SendPayloadToUserAsync_ClientTypeExplicit_SentToUserAndClientType(ClientType clientType, + public async Task SendPayloadToUserAsync_ClientTypeExplicit_SentToUserAndClientType(ClientType clientType, SutProvider sutProvider, Guid userId, PushType pushType, string payload, string identifier) { @@ -173,7 +385,7 @@ public class NotificationHubPushNotificationServiceTests [Theory] [BitAutoData([null])] [BitAutoData(ClientType.All)] - public async void SendPayloadToOrganizationAsync_ClientTypeNullOrAll_SentToOrganization(ClientType? clientType, + public async Task SendPayloadToOrganizationAsync_ClientTypeNullOrAll_SentToOrganization(ClientType? clientType, SutProvider sutProvider, Guid organizationId, PushType pushType, string payload, string identifier) { @@ -192,7 +404,7 @@ public class NotificationHubPushNotificationServiceTests [BitAutoData(ClientType.Desktop)] [BitAutoData(ClientType.Mobile)] [BitAutoData(ClientType.Web)] - public async void SendPayloadToOrganizationAsync_ClientTypeExplicit_SentToOrganizationAndClientType( + public async Task SendPayloadToOrganizationAsync_ClientTypeExplicit_SentToOrganizationAndClientType( ClientType clientType, SutProvider sutProvider, Guid organizationId, PushType pushType, string payload, string identifier) { @@ -206,14 +418,17 @@ public class NotificationHubPushNotificationServiceTests .UpsertAsync(Arg.Any()); } - private static SyncNotificationPushNotification ToSyncNotificationPushNotification(Notification notification) => + private static SyncNotificationPushNotification ToSyncNotificationPushNotification(Notification notification, + NotificationStatus? notificationStatus) => new() { Id = notification.Id, UserId = notification.UserId, OrganizationId = notification.OrganizationId, ClientType = notification.ClientType, - RevisionDate = notification.RevisionDate + RevisionDate = notification.RevisionDate, + ReadDate = notificationStatus?.ReadDate, + DeletedDate = notificationStatus?.DeletedDate }; private static async Task AssertSendTemplateNotificationAsync( diff --git a/test/Core.Test/Services/AzureQueuePushNotificationServiceTests.cs b/test/Core.Test/Services/AzureQueuePushNotificationServiceTests.cs index 375e9d6883..078bfb3ccb 100644 --- a/test/Core.Test/Services/AzureQueuePushNotificationServiceTests.cs +++ b/test/Core.Test/Services/AzureQueuePushNotificationServiceTests.cs @@ -22,22 +22,50 @@ namespace Bit.Core.Test.Services; public class AzureQueuePushNotificationServiceTests { [Theory] - [BitAutoData] + [BitAutoData(false)] + [BitAutoData(true)] [NotificationCustomize] + [NotificationStatusCustomize] [CurrentContextCustomize] - public async void PushSyncNotificationAsync_Notification_Sent( + public async Task PushSyncNotificationCreateAsync_Notification_Sent(bool notificationStatusNull, SutProvider sutProvider, Notification notification, Guid deviceIdentifier, - ICurrentContext currentContext) + ICurrentContext currentContext, NotificationStatus notificationStatus) { + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString()); sutProvider.GetDependency().HttpContext!.RequestServices .GetService(Arg.Any()).Returns(currentContext); - await sutProvider.Sut.PushSyncNotificationAsync(notification); + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, expectedNotificationStatus); await sutProvider.GetDependency().Received(1) .SendMessageAsync(Arg.Is(message => - MatchMessage(PushType.SyncNotification, message, new SyncNotificationEquals(notification), + MatchMessage(PushType.SyncNotificationCreate, message, + new SyncNotificationEquals(notification, expectedNotificationStatus), + deviceIdentifier.ToString()))); + } + + [Theory] + [BitAutoData(false)] + [BitAutoData(true)] + [NotificationCustomize] + [NotificationStatusCustomize] + [CurrentContextCustomize] + public async Task PushSyncNotificationUpdateAsync_Notification_Sent(bool notificationStatusNull, + SutProvider sutProvider, Notification notification, Guid deviceIdentifier, + ICurrentContext currentContext, NotificationStatus notificationStatus) + { + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString()); + sutProvider.GetDependency().HttpContext!.RequestServices + .GetService(Arg.Any()).Returns(currentContext); + + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus); + + await sutProvider.GetDependency().Received(1) + .SendMessageAsync(Arg.Is(message => + MatchMessage(PushType.SyncNotificationUpdate, message, + new SyncNotificationEquals(notification, expectedNotificationStatus), deviceIdentifier.ToString()))); } @@ -52,7 +80,8 @@ public class AzureQueuePushNotificationServiceTests pushNotificationData.ContextId == contextId; } - private class SyncNotificationEquals(Notification notification) : IEquatable + private class SyncNotificationEquals(Notification notification, NotificationStatus? notificationStatus) + : IEquatable { public bool Equals(SyncNotificationPushNotification? other) { @@ -61,7 +90,9 @@ public class AzureQueuePushNotificationServiceTests other.UserId == notification.UserId && other.OrganizationId == notification.OrganizationId && other.ClientType == notification.ClientType && - other.RevisionDate == notification.RevisionDate; + other.RevisionDate == notification.RevisionDate && + other.ReadDate == notificationStatus?.ReadDate && + other.DeletedDate == notificationStatus?.DeletedDate; } } } diff --git a/test/Core.Test/Services/MultiServicePushNotificationServiceTests.cs b/test/Core.Test/Services/MultiServicePushNotificationServiceTests.cs index 58571987b2..f93bbd12de 100644 --- a/test/Core.Test/Services/MultiServicePushNotificationServiceTests.cs +++ b/test/Core.Test/Services/MultiServicePushNotificationServiceTests.cs @@ -14,17 +14,41 @@ namespace Bit.Core.Test.Services; public class MultiServicePushNotificationServiceTests { [Theory] - [BitAutoData] + [BitAutoData(false)] + [BitAutoData(true)] [NotificationCustomize] - public async Task PushSyncNotificationAsync_Notification_Sent( - SutProvider sutProvider, Notification notification) + [NotificationStatusCustomize] + public async Task PushSyncNotificationCreateAsync_Notification_Sent(bool notificationStatusNull, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) { - await sutProvider.Sut.PushSyncNotificationAsync(notification); + await sutProvider.Sut.PushSyncNotificationCreateAsync(notification, + notificationStatusNull ? null : notificationStatus); + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; await sutProvider.GetDependency>() .First() .Received(1) - .PushSyncNotificationAsync(notification); + .PushSyncNotificationCreateAsync(notification, expectedNotificationStatus); + } + + [Theory] + [BitAutoData(false)] + [BitAutoData(true)] + [NotificationCustomize] + [NotificationStatusCustomize] + public async Task PushSyncNotificationUpdateAsync_Notification_Sent(bool notificationStatusNull, + SutProvider sutProvider, Notification notification, + NotificationStatus notificationStatus) + { + await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, + notificationStatusNull ? null : notificationStatus); + + var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus; + await sutProvider.GetDependency>() + .First() + .Received(1) + .PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus); } [Theory]